--- title: "Reporting CIF Regression with gtsummary" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Reporting CIF Regression with gtsummary} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include=FALSE} reporting_ready <- requireNamespace("gtsummary", quietly = TRUE) && utils::packageVersion("gtsummary") >= "2.6.1" && requireNamespace("broom.helpers", quietly = TRUE) 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 and broom.helpers version 1.20.0 or later to run these examples. Reporting packages are optional for estimation.") ``` ## An event-specific regression table Fit the direct CIF regression, then pass the result to `tbl_regression()`. The example uses the anonymized diabetes cohort from the overview. Effects compare low fruit intake with the reference group at 8 years; age and sex enter the nuisance model. See the direct regression vignette for model assumptions and estimation details. ```{r first-regression-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")) attr(report_data$fruitq1, "label") <- "Fruit intake" fit <- polyreg(Event(t, epsilon) ~ age + sex, exposure = "fruitq1", code.exposure.ref = "Other intake", data = report_data, time.point = 8, effect.measure1 = "RR", effect.measure2 = "RR") retinopathy_table <- tbl_regression(fit, exponentiate = TRUE) retinopathy_table ``` The default event is event 1, diabetic retinopathy. The table reports exponentiated effect estimates and their saved confidence limits. Standard errors and test statistics remain on the coefficient's log scale. The reference row describes the contrast; it is not an additional estimated coefficient. These observational estimates do not by themselves establish a causal effect of fruit intake. ## Reporting the competing event Select event 2 explicitly to report macrovascular complications. The numeric results, exposure labels and effect-measure heading all follow the selected event. ```{r competing-event} macrovascular_table <- tbl_regression(fit, exponentiate = TRUE, event = "event2") tbl_merge(list(retinopathy_table, macrovascular_table), tab_spanner = c("**Retinopathy**", "**Macrovascular complications**")) ``` For a tidy data frame containing both events, use `generics::tidy(fit, event = "both")`. Publication regression tables select one event at a time and are combined with `tbl_merge()`. ## RR, OR and SHR Choose the effect measure during estimation, not while formatting the table. RR compares cumulative incidence probabilities, OR compares their odds, and SHR is the subdistribution hazard ratio parameterization. At a fixed time, the latter is the ratio of `-log(1 - CIF)` values; it is not a cause-specific hazard ratio. The following tables use the same exposure comparison and 8-year target, with three different effect-measure models. ```{r effect-measures} fit_or <- polyreg(Event(t, epsilon) ~ age + sex, exposure = "fruitq1", code.exposure.ref = "Other intake", data = report_data, time.point = 8, effect.measure1 = "OR", effect.measure2 = "OR") fit_shr <- polyreg(Event(t, epsilon) ~ age + sex, exposure = "fruitq1", code.exposure.ref = "Other intake", data = report_data, time.point = 8, effect.measure1 = "SHR", effect.measure2 = "SHR") tbl_merge(list(tbl_regression(fit, exponentiate = TRUE), tbl_regression(fit_or, exponentiate = TRUE), tbl_regression(fit_shr, exponentiate = TRUE)), tab_spanner = c("**RR model**", "**OR model**", "**SHR model**")) ``` The two events can also use different effect measures. Headings are read from the fitted `estimand` information; no effect measure is inferred from the size of a coefficient. ```{r mixed-effect-measures} fit_mixed <- polyreg(Event(t, epsilon) ~ age + sex, exposure = "fruitq1", code.exposure.ref = "Other intake", data = report_data, time.point = 8, effect.measure1 = "RR", effect.measure2 = "SHR") tbl_merge(list(tbl_regression(fit_mixed, exponentiate = TRUE), tbl_regression(fit_mixed, exponentiate = TRUE, event = "event2")), tab_spanner = c("**Retinopathy**", "**Macrovascular complications**")) ``` ## Confidence intervals and labels With unchanged inference settings, the table extracts the fitted intervals. This also applies to stored bootstrap intervals: they are not replaced by Wald limits during exponentiation. To retain the fitted confidence level explicitly, pass `conf.level = fit$conf.int`. A request for a different level uses the existing supported reconstruction, or fails if the saved method cannot provide that level without refitting, as with BCa limits. ```{r labels} tbl_regression(fit, exponentiate = TRUE, conf.level = fit$conf.int, label = list(fruitq1 ~ "Low fruit intake versus other intake")) ``` For direct extraction, `generics::tidy(fit)` retains log-scale estimates. Use `exponentiate = TRUE` for ratios. The broom convention uses logical `conf.int` and numeric `conf.level`; the older numeric `conf.int` argument remains accepted by `tidy.polyreg()` for compatibility. There is no need to modify the saved fit or supply a custom tidier to produce these tables. ## Exporting a regression table Use the standard gtsummary conversions. Replace the temporary filenames with the intended destination when saving the final report. ```{r export-regression} html_file <- tempfile(fileext = ".html") gt::gtsave(as_gt(retinopathy_table), filename = html_file) ``` ```{r export-regression-word, eval=reporting_ready && requireNamespace('flextable', quietly=TRUE)} word_file <- tempfile(fileext = ".docx") flextable::save_as_docx(as_flex_table(retinopathy_table), path = word_file) ```