生存模型的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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章