当你手上有两个预测模型,比如用逻辑回归做的疾病风险预测模型,或者用Cox回归做的生存分析模型,怎么判断新模型比旧模型好多少?光看AUC可能不够,这时候就需要NRI(净重新分类指数)和IDI(综合判别改善指数)这两把专业尺子。
NRI就像考试改卷老师:假设旧模型把100个病人分成"高风险"和"低风险"两组,新模型重新分类后,有20人从低风险升到高风险(其中15人确实是病人),有10人从高风险降到低风险(其中8人确实不是病人)。那么NRI = [(15-5)+(8-2)]/100 = 0.16,说明新模型比旧模型多正确分类了16%的人。
IDI更像精确度提升检测仪:它计算的是新模型在病人群体中预测概率的平均提升幅度,减去在非病人群体中预测概率的平均下降幅度。比如病人预测概率平均增加0.05,非病人平均减少0.02,那么IDI=0.05-(-0.02)=0.07。这个值越大,说明模型区分能力提升越明显。
我在心血管疾病预测项目中就遇到过:新模型AUC只提高了0.02看似不大,但NRI达到0.31,临床医生立刻意识到这个改进很有价值——因为这意味着每100个患者中能多正确分类31人。
我们先模拟一个典型的医学数据集:
r复制set.seed(123)
patient_data <- data.frame(
age = rnorm(1000, mean=50, sd=10),
bmi = rnorm(1000, mean=24, sd=3),
cholesterol = rnorm(1000, mean=5, sd=1),
diabetes = sample(0:1, 1000, replace=TRUE, prob=c(0.7,0.3))
)
构建新旧两个逻辑回归模型:
r复制# 旧模型只使用年龄和BMI
old_model <- glm(diabetes ~ age + bmi, data=patient_data, family="binomial")
# 新模型加入胆固醇指标
new_model <- glm(diabetes ~ age + bmi + cholesterol,
data=patient_data, family="binomial")
安装并加载包:
r复制install.packages("PredictABEL")
library(PredictABEL)
关键计算步骤:
r复制# 获取预测概率
old_pred <- predict(old_model, type="response")
new_pred <- predict(new_model, type="response")
# 设置临床决策阈值(例如0.3和0.7)
results <- reclassification(
data = patient_data,
cOutcome = 4, # 糖尿病所在列号
predrisk1 = old_pred,
predrisk2 = new_pred,
cutoff = c(0, 0.3, 0.7, 1)
)
解读输出时要注意:
我曾遇到一个陷阱:当数据不平衡时(比如病例对照研究),需要用weight参数进行调整,否则会高估改善效果。
以肺癌生存数据为例:
r复制library(survival)
data(lung)
# 处理缺失值
lung_clean <- na.omit(lung)
# 旧模型(仅含年龄和性别)
old_surv <- coxph(Surv(time, status) ~ age + sex, data=lung_clean)
# 新模型(加入ph.ecog评分)
new_surv <- coxph(Surv(time, status) ~ age + sex + ph.ecog,
data=lung_clean)
安装专用包:
r复制install.packages("survIDINRI")
library(survIDINRI)
设置时间点(比如500天)进行分析:
r复制covs0 <- lung_clean[,c("age","sex")] # 旧模型变量
covs1 <- lung_clean[,c("age","sex","ph.ecog")] # 新模型变量
result <- IDI.INF(
Surv.data = Surv(lung_clean$time, lung_clean$status),
covs0 = as.matrix(covs0),
covs1 = as.matrix(covs1),
t0 = 500, # 关注的时间点
npert = 500 # 重抽样次数
)
可视化结果的小技巧:
r复制plot(IDI.INF.OUT(result))
这张图会同时显示IDI和NRI的估计值及其置信区间,红色虚线表示零值参考线。
NRI解读:0.1表示每100人中有10人得到更准确的分类。但要注意:
IDI基准:一般来说:
0.05:显著改善
r复制time_points <- seq(100, 1000, by=100)
nri_values <- sapply(time_points, function(t) {
res <- IDI.INF(Surv.data, covs0, covs1, t0=t, npert=200)
IDI.INF.OUT(res)$NRI[1]
})
plot(time_points, nri_values, type="b")
置信区间过宽:当样本量小时,增加npert参数到1000以上
多重比较问题:如果比较多个模型,记得进行p值校正:
r复制p.adjust(c(0.03, 0.01, 0.05), method="BH")
使用加权方法:
r复制library(nricens)
nricens(mdl.std=old_model, mdl.new=new_model,
weight=ifelse(patient_data$diabetes==1, 0.7, 0.3))
动态测试多个阈值组合:
r复制cutoff_matrix <- expand.grid(
seq(0.1,0.5,by=0.1),
seq(0.5,0.9,by=0.1)
)
apply(cutoff_matrix, 1, function(cuts) {
reclassification(patient_data, 4, old_pred, new_pred,
cutoff=c(0, cuts[1], cuts[2], 1))
})
建议在报告中同时呈现:
比如这样展示:
r复制library(pROC)
roc_old <- roc(patient_data$diabetes, old_pred)
roc_new <- roc(patient_data$diabetes, new_pred)
plot(roc_old, col="blue")
lines(roc_new, col="red")
在最近的一个肝癌预测项目中,我们发现虽然AUC只提高0.03,但NRI达到0.25,结合DCA分析最终让临床团队采纳了新模型。记住,好的模型评估需要多角度验证,NRI和IDI提供了传统指标之外的重要视角。