跳转至

生存分析Cox回归进阶

一句话概述:Cox比例风险模型是生存分析的核心方法,不假设基线风险函数的具体形式,通过风险比(HR)评估协变量对生存时间的影响,广泛用于预后因素分析和预后模型构建。

核心知识点速查表

概念说明
Cox模型半参数生存分析模型(白话:分析什么因素影响病人活多久)
HR(Hazard Ratio)风险比,HR>1表示风险增加,HR<1表示保护因素
比例风险假设Cox模型的核心假设:HR不随时间变化
C-index一致性指数,衡量模型预测能力(0.5-1,越大越好)
Nomogram列线图,将Cox模型可视化为临床评分工具
LASSO-CoxLASSO正则化的Cox回归,用于高维特征选择
时间依赖ROC评估预后模型在不同时间点的预测能力

一、基础Cox回归

# === Cox回归基础 ===
library(survival)
library(survminer)

# 准备数据
data(lung)                                  # 肺癌数据集
head(lung)                                  # 查看数据结构

# 单变量Cox回归
uni_cox <- coxph(                           # Cox回归
  Surv(time, status) ~ age,                 # 生存对象 ~ 协变量
  data = lung                                # 数据
)
summary(uni_cox)                             # 查看结果
# HR(exp(coef)), 95%CI, p-value

# 多变量Cox回归
multi_cox <- coxph(
  Surv(time, status) ~ age + sex + ph.ecog + ph.karno,  # 多个变量
  data = lung
)
summary(multi_cox)

# 森林图可视化
ggforest(multi_cox, data=lung)               # ★森林图(HR和95%CI)

二、比例风险假设检验

# === PH假设检验(非常重要!) ===
# Cox模型的前提条件:HR不随时间变化

# Schoenfeld残差检验
ph_test <- cox.zph(multi_cox)               # 检验PH假设
print(ph_test)                               # 查看结果
# p > 0.05 → 满足PH假设 ✓
# p < 0.05 → 违反PH假设 ✗ → 需要处理

# 可视化Schoenfeld残差
ggcoxzph(ph_test)                            # 图形检验(应为水平线)

# === 违反PH假设的处理方法 ===
# 方法1: 分层(Stratification)
strat_cox <- coxph(
  Surv(time, status) ~ age + strata(sex),   # 对sex分层
  data = lung
)

# 方法2: 时间依赖协变量
# 添加 变量*时间 的交互项
td_cox <- coxph(
  Surv(time, status) ~ age + sex + tt(sex),  # tt()=时间依赖
  data = lung,
  tt = function(x, t, ...) x * log(t)        # 时间变换函数
)

三、LASSO-Cox特征选择

# === LASSO-Cox:从高维数据中选择预后基因 ===
library(glmnet)

# 准备数据(假设有基因表达矩阵)
x <- as.matrix(expr_matrix)                  # 基因表达矩阵(n x p)
y <- Surv(clinical$time, clinical$status)     # 生存数据

# 交叉验证选择lambda
set.seed(42)
cv_fit <- cv.glmnet(
  x, y,
  family = "cox",                             # Cox模型
  alpha = 1,                                  # alpha=1是LASSO
  nfolds = 10                                 # 10折交叉验证
)

# 查看最优lambda
plot(cv_fit)                                   # lambda vs deviance图
lambda_min <- cv_fit$lambda.min                # 最小deviance的lambda
lambda_1se <- cv_fit$lambda.1se                # 1se规则的lambda(更简约)

# 提取选中的基因
coef_selected <- coef(cv_fit, s="lambda.1se")  # 获取系数
selected_genes <- rownames(coef_selected)[coef_selected[,1] != 0]  # 非零系数的基因
print(paste("选中", length(selected_genes), "个基因"))
print(selected_genes)

# 用选中的基因建立Cox模型
final_cox <- coxph(
  Surv(time, status) ~ .,                     # 所有选中基因
  data = data.frame(time=clinical$time,
                    status=clinical$status,
                    x[, selected_genes])
)
summary(final_cox)

四、列线图(Nomogram)

# === 构建列线图 ===
library(rms)

# 设置数据分布
dd <- datadist(lung)                          # 计算变量分布
options(datadist='dd')                         # 全局设置

# 拟合Cox模型(用rms的cph函数)
cox_rms <- cph(
  Surv(time, status) ~ age + sex + ph.ecog,
  data = lung,
  x = TRUE, y = TRUE, surv = TRUE
)

# 构建列线图
surv <- Survival(cox_rms)                     # 生存函数
nom <- nomogram(
  cox_rms,
  fun = list(function(x) surv(365, x),        # 1年生存率
             function(x) surv(730, x)),        # 2年生存率
  funlabel = c("1-Year Survival",
               "2-Year Survival"),
  lp = FALSE                                   # 不显示线性预测值
)
plot(nom)                                       # ★绘制列线图

# 校准曲线
cal <- calibrate(cox_rms, u=365, B=200)       # 1年生存率校准
plot(cal)                                       # 绘制校准曲线

五、时间依赖ROC

# === 时间依赖ROC曲线 ===
library(timeROC)

# 计算风险评分
risk_score <- predict(multi_cox, type="risk")  # 预测风险值

# 时间依赖ROC
roc <- timeROC(
  T = lung$time,                               # 生存时间
  delta = lung$status - 1,                     # 事件状态(0/1)
  marker = risk_score,                          # 风险评分
  cause = 1,                                    # 感兴趣的事件
  times = c(365, 730, 1095),                   # 1年、2年、3年
  iid = TRUE                                    # 计算置信区间
)

# 查看AUC
print(roc$AUC)                                 # 各时间点AUC

# 可视化
plot(roc, time=365, col="red", title=FALSE)     # 1年ROC
plot(roc, time=730, col="blue", add=TRUE)       # 2年ROC
legend("bottomright",
       legend=c(paste0("1-Year AUC=", round(roc$AUC[1],3)),
                paste0("2-Year AUC=", round(roc$AUC[2],3))),
       col=c("red","blue"), lty=1)

六、面试高频考点

Q1: Cox回归的HR怎么解读?

  • HR=1.5:风险增加50%(该因素每增加1单位,死亡风险增加50%)
  • HR=0.7:风险降低30%(保护因素)
  • HR=1.0:无影响
  • 白话:HR就像"倍率",>1是坏事,<1是好事

Q2: C-index和AUC的区别?

  • C-index衡量整体区分能力,AUC衡量特定时间点的预测能力
  • C-index=0.7-0.8算不错,>0.8算好
  • 时间依赖ROC比单一C-index信息更丰富

Q3: 什么时候用单变量vs多变量Cox?

  • 单变量:初步筛选,看每个变量单独的效果
  • 多变量:控制混杂因素后的独立预后因子
  • 标准流程:单变量筛选(p<0.1) → 多变量验证(p<0.05)

常见报错与解决

报错原因解决方案
NA/NaN/Inf in foreign function数据有缺失值先处理NA
PH assumption violated违反比例风险假设分层或加时间依赖项
Convergence failure变量太多或共线性减少变量或用LASSO筛选
C-index太低模型预测力差加入更多变量或换模型

速查表

# === Cox回归速查 ===
library(survival); library(survminer)

# 单变量
coxph(Surv(time, status) ~ var, data=df)

# 多变量
fit <- coxph(Surv(time, status) ~ var1+var2+var3, data=df)
ggforest(fit)                        # 森林图

# PH假设检验
cox.zph(fit)                         # p>0.05=满足

# LASSO-Cox
library(glmnet)
cv.glmnet(x, Surv(time,status), family="cox", alpha=1)

# 列线图
library(rms)
nomogram(cph_model, fun=list(...))

# 时间依赖ROC
library(timeROC)
timeROC(T, delta, marker, times=c(365,730,1095))