# Standardized CIF for a binary (0/1) exposure column of `dat`. Defaults give
# this section's eGFR-difference contrast; §7.3 reuses it for the excess dose.
adj_cif_binary <- function(time_var, status_var, extra_pre = NULL, label,
expo_var = "egfr_diff_lt_neg10",
lab_exp = "eGFR diff < -10 % (exposed)",
lab_ref = "eGFR diff ≥ -10 % (others)",
sub_exp = "eGFR diff < -10%") {
base_adj <- c("age", "sex", "ecog_score", "ici", "cockcroft")
d <- dat %>%
dplyr::select(all_of(c(time_var, status_var, expo_var,
base_adj, extra_pre))) %>%
tidyr::drop_na()
fmla <- as.formula(paste("~",
paste(c(expo_var, base_adj, extra_pre), collapse = " + ")))
X_full <- model.matrix(fmla, data = d)[, -1, drop = FALSE]
stat_num <- suppressWarnings(as.integer(as.character(d[[status_var]])))
time_num <- as.numeric(d[[time_var]])
fit <- tryCatch(
cmprsk::crr(ftime = time_num, fstatus = stat_num,
cov1 = X_full, failcode = 1),
error = function(e) NULL
)
if (is.null(fit)) return(.plot_failed(label))
beta_e <- unname(fit$coef[1])
se_e <- sqrt(fit$var[1, 1])
hr <- exp(beta_e)
lcl <- exp(beta_e - 1.96 * se_e)
ucl <- exp(beta_e + 1.96 * se_e)
pval <- 2 * (1 - pnorm(abs(beta_e / se_e)))
p_str <- ifelse(pval < 0.001, "<0.001", sprintf("%.3f", pval))
n_exp <- sum(d[[expo_var]] == 1)
n_ref <- sum(d[[expo_var]] == 0)
subtitle_str <- sprintf(
"%s (n = %d) vs others (n = %d): subHR = %.2f (95%% CI %.2f-%.2f), p = %s",
sub_exp, n_exp, n_ref, hr, lcl, ucl, p_str
)
X0 <- X_full; X0[, expo_var] <- 0
X1 <- X_full; X1[, expo_var] <- 1
pred0 <- predict(fit, X0)
pred1 <- predict(fit, X1)
times <- pred0[, 1]
cif0 <- rowMeans(pred0[, -1, drop = FALSE])
cif1 <- rowMeans(pred1[, -1, drop = FALSE])
# Anchor both curves at CIF = 0 on day 0 and carry the last estimate out to
# day 90 (end of follow-up), so the steps span the full 0–90-day axis.
t_end <- 90
df <- bind_rows(
tibble(time = c(0, times, t_end), cif = c(0, cif0, tail(cif0, 1)),
group = lab_ref),
tibble(time = c(0, times, t_end), cif = c(0, cif1, tail(cif1, 1)),
group = lab_exp)
)
df$group <- factor(df$group, levels = c(lab_exp, lab_ref))
ymax <- max(df$cif, na.rm = TRUE)
ggplot(df, aes(x = time, y = cif, color = group)) +
geom_step(linewidth = 1) +
scale_color_manual(values = setNames(c("#E31A1C", "#1F78B4"),
c(lab_exp, lab_ref))) +
scale_x_continuous(breaks = seq(0, t_end, by = 15)) +
coord_cartesian(xlim = c(0, t_end), ylim = c(0, max(0.05, ymax * 1.05))) +
labs(x = "Days from baseline",
y = "Cumulative incidence",
color = NULL,
title = label,
subtitle = subtitle_str) +
theme_minimal(base_size = 14) +
theme(legend.position = "top",
plot.subtitle = element_text(size = 12, color = "grey25")) -> p
.save_plot_pptx(p)
p
}
adj_cif_binary("cmp_time_side_effect", "cmp_status_side_effect",
NULL, "Platinum-Related Hospitalisation — eGFR diff < -10 % vs others")