生存模型的C-index(C指数)

概述

参考自:如何在R软件中求一致性指数

C-index,C指数即一致性指数(concordance index),用来评价模型的预测能力。c指数是指所有病人对子中预测结果与实际结果一致的对子所占的比例。它估计了预测结果与实际观察到的结果相一致的概率。c指数的计算方法是:把所研究的资料中的所有研究对象随机地两两组成对子。以生存分析为例,对于一对病人,如果生存时间较长的一位的预测生存时间也长于另一位的预测生存时间,或预测的生存概率高的一位的生存时间长于生存概率低的另一位,则称之为预测结果与实际结果一致。

C-index最早是由范德堡大学(Vanderbilt University)生物统计教教授Frank E Harrell Jr 1996年提出,主要用于计算生存分析中的COX模型预测值与真实之间的区分度(discrimination),也称为Harrell's concordance index ;现阶段用的最多的是肿瘤患者预后模型的预测精度。一般评价模型的好坏主要有两个方面,一是模型的拟合优度(Goodness of Fit),常见的评价指标主要有R方,-2logL,AIC,BIC等等;另外一个是模型的预测精度,主要就是模型的真实值与预测值之间的差的大小,均方误差,相对误差等。从临床应用的角度来说,我们更注重后者,即统计建模主要是用于预测,而从C-index的概念大家看出它属于模型评价指标的后者,这一指标比前面提到的几个指标看起来更高大上,一般文献中用的也比较多。换句话说,如果预后模型建好,效果不错,即使不知道如何计算C-index值,报告软件输出结果中的预测误差是相同效果,再添加拟合优度会更能说明效果,这样反而更实用。

计算方法

C-index本质上是估计了预测结果与实际观察到的结果相一致的概率,即资料所有病人对子中预测结果与实际结果一致的对子所占的比例;分类问题中,正类预测score大于负类预测score的概率即是C指数(Mann–Whitney U检验的C统计量),也称AUC,推导可以参考:AUC与Mann–Whitney U test 以及 wikipedia/Mann_Whitney_U_test。当然,生存数据也有专门的AUC计算方法,integratedAUC( iAUC),对应的ROC一般称之为riskROC、时间依赖ROC等。integratedAUC和C-index其实差不太多,计算的结果也相差不大。

C-index的计算方法是:把所研究的资料中的所有研究对象随机地两两组成对子。以生存分析为例,对于一对病人,如果生存时间较长的一位,其预测生存时间长于生存时间较短的一位,或预测的生存概率高的一位的生存时间长于生存概率低的另一位,则称之为预测结果与实际结果一致。

计算步骤为:

  1. 产生所有的病例配对。若有n个观察个体,则所有的对子数应为Cn2(组合数)
  2. 排除下面两种对子:对子中具有较小观察时间的个体没有达到观察终点及对子中两个个体都没达到观察终点;剩余的为有用对子。
  3. 计算有用对子中,预测结果和实际相一致的对子数, 即具有较坏预测结果个体的实际观察时间较短。
  4. 计算,C-index = 一致对子数/有用对子数。

由上述计算方法可以看出,C-index在0.5-1之间。0.5为完全随机,说明该模型没有预测作用,1为完全一致,说明该模型预测结果与实际完全一致。在实际应用中,很难找到完全一致的预测模型,既往研究认为,C-index在0.50-0.70为较低准确度:在0.71-0.90之间为中等准确度;而高于0.90则为高准确度。

这和AUC其实是类似的,如果完全预测反了,那么C-index其实应该是1。但是呢,这种情况仅在训练集中可以这么计算,在验证集中,预测反了就是预测反了。因此看到C-index或AUC小于0.5的情况,也不要大惊小怪。

R代码示例

C-index的R软件计算实现有很多种方法,如Harrell本人的的R包Hmisc package,或者Le Kang, Weijie Chen 2014年12月18日发布的R compareC Package(笔者没用过,本文不作介绍,可参考相关文档)。survival包的survConcordance函数也可以计算C-index,或者在使用coxph建模得到cox.fit后使用summary(cox.fit)$concordance也能输出C-index。

参考自C-index/C-statistic 计算的5种不同方法及比较

方法1:Hmisc包中的rcorr.cens函数:见前面的代码(注意需求出中间变量predictor,而不能使用公式)。这个函数有公式型写法rcorrcens,可参考其文档。

data <- read.csv("survivaldta.csv")
library(Hmisc)
library(survival)
f <- cph(Surv(time,death)~x1+x2+x3,data=survivldata)
fp <- predict(f)
cindex.orig=1-rcorr.cens(fp,Surv(time,death))

方法2:survival包的函数coxph,或利用survival包中的函数survConcordance

data <- read.csv("survivaldta.csv")
library(survival)
fit <- coxph(Surv(time,death)~x1+x2+x3,data=survivldata)
sum.surv <- summary(fit)
c_index <-sum.surv$concordance
c_index
 
## concordance.concordant            se.std(c-d) 
##             0.54469239             0.02788881

# or using the function survConcordance to get c-index
c_index <- survConcordance(Surv(time,death)~predict(fit,survivldata))$concordance

方法3:利用survcomp包,可以直接输出95%可信区间,不需要自己再进行计算,但语法有点繁琐。

data <- read.csv("survivaldta.csv")
library(survcomp) # installed using Bioconductor
library(survival)
fit <- coxph(Surv(time,death)~x1+x2+x3,data=survivldata)
cindex <- concordance.index(predict(fit),
        surv.time = survivldata$os, surv.event = survivldata$death,method = "noether")
cindex$c.index; cindex$lower; cindex$upper

方法4:利用rms包中的cph函数和validate函数,可提供un-adjusted和bias adjusted C指数两种,未校正的C指数的结果和方法4是相同的。但是不提供可信区间和SE。

survivldata<- read.csv("survivaldta.csv")
library(rms)
set.seed(1) # the method of validate is using random
fit.cph <- cph(Surv(time,death)~x1+x2+x3, data = survivldata, x = TRUE, y = TRUE, surv = TRUE)
 
# Get the Dxy
v <- validate(fit.cph, dxy=TRUE, B=1000)
Dxy = v[rownames(v)=="Dxy", colnames(v)=="index.corrected"]
orig_Dxy = v[rownames(v)=="Dxy", colnames(v)=="index.orig"]
 
# The c-statistic according to Dxy=2(c-0.5)
bias_corrected_c_index  <- abs(Dxy)/2+0.5
orig_c_index <- abs(orig_Dxy)/2+0.5
 
bias_corrected_c_index
## [1] 0.5325809
 orig_c_index
## [1] 0.5446924

本质上,方法:3、4是相同的,都忽略tied risk (在处理tied risk上,也就是当两个观测拥有相同的生存时间和相同的自变量X时,方法3和4忽略tied risk),而方法2则考虑了tied risk。

方法3和4的计算:Concordance = #all concordant pairs/#total pairs ignoring ties.

方法2的计算:Concordance = (#all concordant pairs + #tied pairs/2)/(#total pairs including ties)

更可靠的C-index

上述C-index是在全体数据建模后,评估全体数据预测值和实际值的一致性。但更可靠的是在验证数据中求C-index。

独立验证

主要的方法如下:

# Method 1: rcorr.cens
library(Hmisc)
library(survival)
fit <- cph(Surv(time,death)~x1+x2+x3,data=training)
fp <- predict(fit,validation)
cindex.orig=1-rcorr.cens(fp,Surv(validation$time,validation$death))

# Method 2: survConcordance
library(survival)
fit <- cph(Surv(time,death)~x1+x2+x3,data=training)
c_index <- survConcordance(Surv(validation$time,validation$death)~predict(fit,validation))$concordance

内部交叉验证或Bootstrap验证

交叉验证和Bootstrap都是内部采样验证的方法,具体原理本文不做介绍。此处简要给出Bootstrap验证的survConcordance方法的代码(交叉验证的代码也很简单,请自行解决)。

library(boot)

c_index <- function(formula, data, indices) {
  tran.data <- data[indices,]
  vali.data <- data[-indices,]
  fit <- coxph(formula, data=tran.data)
  result<-survConcordance(Surv(vali.data$time,vali.data$death)~predict(fit,vali.data))
  index<-as.numeric(result$concordance)
  return(index)
}


set.seed(1234)
results <- boot(data=survivldata, statistic=c_index, R=1000, formula=Surv(time,death)~x1+x2+x3)

mean(results$t)
boot.ci(results)

另外,还有一种常见的统计需求是整体数据Bootstrap重采样估计统计量。这种和上述Bootstra验证统计量有些不同,区别在于计算C-index等统计量时,仍然适用重采样的tran.data,而不是vali.data。这种做法可用于对统计量进行区间估计。应注意区分这两种Bootstrap。

 

bootstrap实现的核心代码如下:

for (i in 1:boot_num){
  boot_index = sample(1:data.size, data.size, replace = T)
  training = data_all[boot_index,]
  validation = data_all[-boot_index,]
  # calculate the statistic
}

另附一段别人写的Bootstrap代码(仅供参考):

###############################
#### Bootstrap code ###
###############################

bootit=200
for(i in 1:bootit){
    case=noNA[group=="long",] 
    control=noNA[group=="<24",]
    bootindex.case=sample(1:nrow(case),replace=T)
    boot.case.data=case[bootindex.case,]
    bootindex.control=sample(1:nrow(control),replace=T)
    boot.control.data=control[bootindex.control,]
    boot.data=rbind(boot.case.data,boot.control.data)
    dstr.boot=svydesign(id=~1, prob=~inv_weight, fpc=~ssize, data=boot.data)
    boot.fit=svycoxph(Surv(survival,surv_cens)         
                      ~x1+x2+x3,data=boot.data,x=TRUE,design=dstr.boot)
    cindex.train=1-rcorr.cens(lp.boot,Surv(boot.data$survival, boot.data$surv_cens))[[1]]
    cindex.test=1-rcorr.cens(lp_=.test,Surv(noNA$survival,noNA$surv_cens))[[1]]
    bias=rep(1,bootit)
    bias[i]=abs(cindex.train-cindex.test) 
}

 

相关讨论

C-index被普遍使用,甚至被认为是cox回归中的AUC,当然更为可靠的是 iAUC。

C-index由Frank Harrell教授提出,用于评价模型的预测能力。
可以看看Frank Harrell本人对C-index适用范围的看法:compare c-index of two logistic models using rcorrp.senc() of the Hmisc library

---------------------------------------------------------------------------

Osman:

Would it be appropriate to do the following to calculate a p-value for the difference between c-index of x1 and c-index of x2 using the output from

rcorrp.senc()
r<-rcorrp.senc(x1,x1,y)
pValue<-1-pnorm((r[11]-r[12])/(r[2]/r[5])*1.96) 

==================

Frank E Harrell:
Because tests for differences in two ROC areas are not very powerful, rcorrp.cens changes the hypothesis to "are predictions from one method  more concordant with the outcome than predictions from the other method,  within paired predictions".  You can't get a difference in ROC areas from the U-statistic computed by rcorrp.cens.

---------------------------------------------------------------------------

另外一个:How to do ROC-analysis in R with a Cox model

问题和讨论比较多,看看其中一些摘录:

---------------------------------------------------------------------------

DWin:

@chl has pointed to a specific answer to your question. The 'rms' package's cph function will produce a Somers-D which can be transformed trivially into a c-index. However, Harrell (who introduced the c-index to biostatistical practice) thinks this is unwise as a general strategy for assessing prognostic measures, because it has low power for discrimination among alternatives. Instead of relying on the surgical literature for your methodological guidance, it would be wiser to seek out the accumulated wisdom in Harrell's text, "Regression Modeling Strategies" or Steyerberg's "Clinical Prediction Models".

==================

Frank E Harrell:

Thanks for the note. I think that Dxy and C are not bad for describing the predictive discrimination of a single pre-specified model. But as you said, they lack power for doing more than that.

附: Interpreting the example given by Prof Frank Harrell in {Design} validate.cph

---------------------------------------------------------------------------

 

因此有人认为:“总结来说,C-index在评价单个模型的预测能力方面的确不错,但是要比较不同模型之间的C-index,那就有点勉为其难了,这是由C-index本身所使用的计算方法决定的。”

另外,丁香园有人提到“C-index比较的p值的确可以计算,请参见http://stats.stackexchange.com/questions/117332/how-to-assess-the-incremental-additive-information-of-a-new-predictor-in-a-cox-m。不过这种比较效能很低,Harrell教授本人都是不推荐的。模型的比较可以用Likelihood-ratio test。请参见https://stat.ethz.ch/pipermail/r-help/2011-October/292304.html”。

 

参考资料

如何在R软件中求一致性指数

C-index/C-statistic 计算的5种不同方法及比较

Harrell FE, Califf RM, Pryor DB, Lee KL, and Rosati RA. (1982) Evaluating the yield of medical tests. The Journal of the American Medical Association, 247(18), 2543–2546

Pencina MJ and D’Agostino RB. (2004) Overall C as a measure of discrimination in survival analysis: model specific population value and confidence interval estimation. Statistics in Medicine, 23(13), 2109–2123

Kang L, Chen W, Petrick NA, and Gallas BD. (2014) Comparing two correlated C indices with right-censored survival outcome: a one-shot nonparametric approach. Statistics in Medicine, 34(4), 685–703, doi: 10.1002/sim.6370

Hmisc Reference Manual

Compare C -Reference Manual

Frank.Harrell

 

  • 65
    点赞
  • 397
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 27
    评论
评论 27
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FarmerJohn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值