--- title: "Reporting Cumulative Incidence with gtsummary" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Reporting Cumulative Incidence with gtsummary} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include=FALSE} reporting_ready <- requireNamespace("gtsummary", quietly = TRUE) && utils::packageVersion("gtsummary") >= "2.6.1" knitr::opts_chunk$set(collapse = TRUE, comment = "#>", eval = reporting_ready) ``` ```{r optional-packages, echo=FALSE, results='asis', eval=!reporting_ready} cat("Install gtsummary version 2.6.1 or later to run these reporting examples. Estimation with cifmodeling does not require gtsummary.") ``` ## A cumulative incidence table The `diabetes.complications` dataset is the anonymized cohort used in the package overview. Follow-up is measured in years; event 1 is diabetic retinopathy and event 2 is a macrovascular complication. This example reports retinopathy probabilities by fruit-intake group. ```{r first-table} library(cifmodeling) library(gtsummary) data(diabetes.complications) report_data <- diabetes.complications report_data$fruitq1 <- factor(report_data$fruitq1, levels = c(0, 1), labels = c("Other intake", "Low intake")) curve <- cifcurve(Event(t, epsilon) ~ fruitq1, data = report_data, outcome.type = "competing-risk") cif_table <- tbl_survfit(curve, times = c(4, 8), type = "risk", label_header = "**{time}-year CIF**") cif_table ``` For competing risks, the `survfit`-compatible object stores `surv = 1 - CIF`. The `type = "risk"` argument displays the CIF and reverses the saved interval limits: the reported limits are `1 - upper` and `1 - lower`. The table does not re-fit the curve or construct a different confidence interval. ## Selecting confidence intervals Choose inference when fitting the curve. Here IF-based standard errors, Student critical values and a log-log transformation are requested. For the stored `1 - CIF`, this transformation is the complementary log-log transformation of the CIF. ```{r student-intervals} student_curve <- cifcurve(Event(t, epsilon) ~ fruitq1, data = report_data, outcome.type = "competing-risk", error = "if", conf.dist = "t", conf.type = "log-log") student_table <- tbl_survfit(student_curve, times = c(4, 8), type = "risk", label_header = "**{time}-year CIF**") |> modify_footnote_header(columns = all_stat_cols(), footnote = "95% pointwise IF-t intervals; log-log transformation of 1 - CIF; deletion allocation for degrees of freedom.") student_table ``` `error = "fay"` selects the Fay--Graubard-type variance correction, and `df.method = "equal"` is an alternative allocation for IF-t. Supplied weights are treated as fixed. Details of the inference methods are in `?cifcurve`. Degrees of freedom vary by group and time; they are available in a summary without adding diagnostic columns to the publication table. ```{r inference-diagnostics} s <- summary(student_curve, times = c(4, 8), extend = TRUE) data.frame(group = s$strata, time = s$time, df = s$df, interval_status = s$conf.status) ``` An unavailable interval remains missing. In particular, a valid interval is not carried beyond the last positive-weight follow-up. Exact structural boundaries retain the fitted singleton interval. These are pointwise intervals, not simultaneous confidence bands. ## Comparing curves through a specified horizon The following tests compare the CIF curves through 8 years. They are not separate tests of the probabilities in each column of the preceding table. The score tests emphasize earlier or later differences through their Fleming--Harrington weights. The method labels are taken from the saved fits. ```{r curve-tests} tests <- list( gray = ciftest(Event(t, epsilon) ~ fruitq1, report_data, test = "gray", tau = 8), early = ciftest(Event(t, epsilon) ~ fruitq1, report_data, test = "score", rho = 1, gamma = 0, tau = 8), late = ciftest(Event(t, epsilon) ~ fruitq1, report_data, test = "score", rho = 0, gamma = 1, tau = 8) ) test_results <- do.call(rbind, lapply(tests, function(fit) { x <- generics::tidy(fit) data.frame(method = paste(x$method, x$weight), p.value = x$p.value) })) test_table <- as_gtsummary(test_results) |> modify_header(method = "**Curve comparison through 8 years**", p.value = "**p-value**") |> modify_fmt_fun(p.value = label_style_pvalue(digits = 3)) test_table ``` This recipe reports the selected `ciftest()` results directly. The usual `add_p()` default for a survival table is a log-rank test and does not select these CIF tests. The ordinary Gray branch supports integer frequency weights; it does not treat fractional IPTW as frequency weights. To include the statistic and degrees of freedom, select `statistic` and `parameter` from `generics::tidy(fit)`. The latter is the effective rank of the test covariance, distinct from the time-specific Student degrees of freedom used for the curve intervals. ## Labels and risk-set counts Use gtsummary's existing label, header and formatting arguments to customize the table. For a weighted fit, distinguish the number of observations, the sum of weights and the effective sample size. `n.risk.type` chooses the quantity exposed as `n.risk`; the legacy `n.risk`, `n.event` and `n.censor` fields are rounded upward, while `n.risk.weighted`, `n.risk.unweighted` and `n.risk.ess` retain their respective values on the native grid. An event count in this curve object includes both event causes, not only retinopathy. ## Exporting the table The table can be converted with standard gtsummary methods. The temporary paths below can be replaced with the desired output filenames. ```{r export-cif} html_file <- tempfile(fileext = ".html") gt::gtsave(as_gt(student_table), filename = html_file) ``` ```{r export-cif-word, eval=reporting_ready && requireNamespace('flextable', quietly=TRUE)} word_file <- tempfile(fileext = ".docx") flextable::save_as_docx(as_flex_table(student_table), path = word_file) ```