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.
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
#> Found litedown! Enabling r-universe template| Characteristic | 4-year CIF | 8-year CIF |
|---|---|---|
| fruitq1 | ||
| Other intake | 18% (15%, 21%) | 26% (23%, 30%) |
| Low intake | 26% (20%, 32%) | 35% (28%, 42%) |
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.
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.
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| Characteristic | 4-year CIF1 | 8-year CIF1 |
|---|---|---|
| fruitq1 | ||
| Other intake | 18% (15%, 21%) | 26% (23%, 30%) |
| Low intake | 26% (21%, 32%) | 35% (30%, 42%) |
| 1 95% pointwise IF-t intervals; log-log transformation of 1 - CIF; deletion allocation for degrees of freedom. | ||
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.
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)
#> group time df interval_status
#> 1 fruitq1=Other intake 4 659.7447 ok
#> 2 fruitq1=Other intake 8 624.5755 ok
#> 3 fruitq1=Low intake 4 227.4970 ok
#> 4 fruitq1=Low intake 8 212.0966 okAn 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.
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.
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)
)
#> Detected >2 status levels; outcome.type set to 'competing-risk'.
#> Detected >2 status levels; outcome.type set to 'competing-risk'.
#> Detected >2 status levels; outcome.type set to 'competing-risk'.
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| Curve comparison through 8 years | p-value |
|---|---|
| Gray's test unweighted | 0.004 |
| Fine-Gray score test custom | 0.006 |
| Fine-Gray score test custom | 0.038 |
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.
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.
The table can be converted with standard gtsummary methods. The temporary paths below can be replaced with the desired output filenames.