PAYATHON 2026

如何用 ggsurvfit 绘制分面内含分层曲线的 KM 图?

数据阿明

可以。绘图数据需要同时包含分面变量 sex 和曲线分组变量 surg。可以先按 sex 拆分原始数据,在各子集中按 surg 拟合,再用 tidy_survfit() 整理并合并结果,最后用 ggplot2 分面绘图。

为什么原代码会报错?

ggsurvfit() 使用生存拟合结果整理出的数据,不会自动把原始数据的所有列带入图中。原代码只拟合了 ~ sex,绘图数据缺少 facet_wrap(~surg) 所需的 surg 列,因此出现 combine_vars() 错误。官方说明指出,ggsurvfit() 内部通过 tidy_survfit() 构造绘图数据。ggsurvfit 绘图说明

原代码按 sex 分组、按 surg 分面,与你描述的目标正好相反。下面按 sex 分面,在每个分面内按 surg 分组。

按 sex 分面,在每个分面中按 surg 绘制曲线

下面保留 ggsurvfit 的拟合、结果整理和阶梯置信区间工具,用 ggplot2 控制分面和颜色:

library(survival)
library(ggsurvfit)
library(ggplot2)
library(dplyr)

# 分面变量和分组变量需要有明确取值
 dat <- df_colon |>
  filter(!is.na(sex), !is.na(surg)) |>
  mutate(surg = droplevels(factor(surg)))

surg_levels <- levels(dat$surg)
palette <- setNames(
  grDevices::hcl.colors(length(surg_levels), palette = "Dark 3"),
  surg_levels
)

# 每个 sex 子集独立拟合,保留其分面标识
curve_list <- lapply(split(dat, dat$sex, drop = TRUE), function(d) {
  fit <- survfit2(Surv(time, status) ~ surg, data = d)
  out <- tidy_survfit(fit)

  # 子集中只有一个 surg 类别时,结果可能没有 strata 列
  if (!"strata" %in% names(out)) {
    out$strata <- as.character(unique(d$surg))
  }

  out$sex <- d$sex[1]
  out$surg <- factor(as.character(out$strata), levels = surg_levels)
  out
})

plot_data <- bind_rows(curve_list)

ggplot(
  plot_data,
  aes(x = time, y = estimate, group = surg)
) +
  stat_stepribbon(
    aes(ymin = conf.low, ymax = conf.high, fill = surg),
    alpha = 0.2,
    colour = NA
  ) +
  geom_step(aes(colour = surg), direction = "hv") +
  facet_wrap(~sex, nrow = 1) +
  scale_colour_manual(values = palette, drop = FALSE) +
  scale_fill_manual(values = palette, drop = FALSE) +
  coord_cartesian(ylim = c(0, 1)) +
  labs(
    x = "时间",
    y = "生存概率",
    colour = "surg",
    fill = "surg"
  ) +
  theme_bw()

tidy_survfit() 提供 time、estimate、conf.low、conf.high 和分层信息。代码额外补回了 sex,让所有图层都能按它分面。tidy_survfit 文档

置信区间使用 stat_stepribbon(),使阴影与 KM 曲线一样呈阶梯形,避免普通 geom_ribbon() 在相邻时间点之间形成斜边。阶梯置信区间文档

如何保证各分面的颜色一致?

代码中的 palette 是以 surg 类别名称为键的命名颜色向量,同时传给了 scale_colour_manual() 和 scale_fill_manual()。这样,相同类别的曲线与置信区间会使用固定颜色。

统一因子水平并设置 drop = FALSE,还可以保留完整的图例类别。如果某个 sex 子集没有某个 surg 类别,该分面不会出现对应曲线;颜色设置不会补出不存在的数据。

strata 是否限制了分面变量的数量?

没有。strata 是整理后的曲线分组列,官方示例用它分面,是因为它已经存在于绘图数据中。只要数据中有相应列,就可以分别把不同变量用于颜色分组、facet_wrap() 或 facet_grid()。

分面只负责安排曲线的位置,不会重新计算生存估计。因此,应先计算每个 sex 内各 surg 类别的曲线,再分面;不能把按全部样本计算的曲线简单复制到各个分面。

如果还需要风险表,就要另外设计布局。官方画廊说明,直接分面的曲线不支持附带风险表;可以考虑分别创建各个 sex 的 ggsurvfit() 图,再组合展示。官方分面示例与限制

备注:内容仅供参考。