R—生存分析—Kaplan-Meier + Log-rank test + Cox回歸

參考教程:http://www.sthda.com/english/wiki/survival-analysis-basics
假設(shè)有228個被診斷患有肺癌的病人開始長期的隨訪盆繁,如果病人死亡腺劣;則隨訪結(jié)束怕午,并記錄生存時間(從確診到死亡的時間)。而生存分析Survival Analysis 主要回答三個方面的問題:
(1)病人的第5年生存率是多少震贵?病人總體的中位生存率的時間可以到多久?
(2)若根據(jù)病人信息(例如性別)利赋,將病人分為兩組,那么這兩組的生存率是否有顯著差異猩系?
(3)若有很多病人的臨床信息或者分子特征媚送,其中哪些屬性會顯著影響病人的生存?

1寇甸、基本概念

1.1 event事件與survival time生存時間

  • 在生存分析中塘偎,這里的事件一般是指死亡,在廣義的生存分析中拿霉,事件可以引申至許多其它含義吟秩,例如疾病復(fù)發(fā)等;

如果處理TCGA的數(shù)據(jù)绽淘,會發(fā)現(xiàn)有各種各樣的event的定義涵防,可以參考筆記http://www.reibang.com/p/f07588bcf22e。本文筆記全篇采用的是死亡作為事件收恢。

  • 而生存時間是指在事件發(fā)生的前提下武学,從病人確診到死亡的時間。

1.2 Censor 刪失值

  • 在隨訪的過程中伦意,可能無法全部病人都可以收集到準(zhǔn)確地生存時間火窒。
  • 一方面由于隨訪項目時間限制,直至隨訪結(jié)束驮肉,病人仍在世熏矿;一方面可能在隨訪期間,病人失聯(lián)离钝、或者因其它原因去世等票编,無法繼續(xù)隨訪。
  • 這樣的數(shù)據(jù)就稱之為Censor卵渴,而此時的生存時間即為所能記錄到的最長生存時間慧域。病人的status要么是事件發(fā)生了,要么就是Censor浪读。


2昔榴、Kaplan-Meier與Log-rank test

2.1方法簡介

  • Kaplan-Meier簡單來說是根據(jù)病人的status與survival time計算、預(yù)估病人在確診后時間t時刻的生存率(survival probability)如何碘橘。

survivor function關(guān)注的是event事件不發(fā)生的概率互订;與之相反,之后會提到的hazard function則主要關(guān)注event事件發(fā)生的概率

  • Log-rank test是指將病人分為兩組或者多組的情況下痘拆,組間的生存率是否有顯著差異仰禽。

一般上述二者同時分析

2.2 分析R包與示例數(shù)據(jù)

  • R包
# install.packages(c("survival", "survminer"))

library("survival") #生存分析
library("survminer") #結(jié)果可視化
  • 示例數(shù)據(jù) lung {survival}
head(lung)
#     inst time status age sex ph.ecog ph.karno pat.karno meal.cal wt.loss
# 1    3  306      2  74   1       1       90       100     1175      NA
# 2    3  455      2  68   1       0       90        90     1225      15
# 3    3 1010      1  56   1       0       90        90       NA      15
# 4    5  210      2  57   1       1       90        60     1150      11
# 5    1  883      2  60   1       0      100        90       NA       0
# 6   12 1022      1  74   1       1       50        80      513       0

# inst: Institution code
# time: Survival time in days(*) 生存時間
# status: censoring status 1=censored, 2=dead(*) 事件是否發(fā)生
# age: Age in years
# sex: Male=1 Female=2
# ph.ecog: ECOG performance score (0=good 5=dead)
# ph.karno: Karnofsky performance score (bad=0-good=100) rated by physician
# pat.karno: Karnofsky performance score as rated by patient
# meal.cal: Calories consumed at meals
# wt.loss: Weight loss in last six months

如果挖掘TCGA的數(shù)據(jù),通常0表示存活纺蛆,1表示死亡

2.3 survival分析

  • 開始分析:按性別將病人分為兩組
fit <- survfit(Surv(time, status) ~ sex, data = lung)
print(fit)
# Call: survfit(formula = Surv(time, status) ~ sex, data = lung)
# 
# n events median 0.95LCL 0.95UCL
# sex=1 138    112    270     212     310
# sex=2  90     53    426     348     550

如上吐葵,n表示每組的病人數(shù);events表示每組有多少病人死亡桥氏;median表示中位生存率所對應(yīng)的生存時間折联;最后兩列表示第三列的95%置信區(qū)間。

res.sum <- surv_summary(fit)
head(res.sum[res.sum$sex==1,])
#   time n.risk n.event n.censor      surv    std.err     upper     lower strata sex
# 1   11    138       3        0 0.9782609 0.01268978 1.0000000 0.9542301  sex=1   1
# 2   12    135       1        0 0.9710145 0.01470747 0.9994124 0.9434235  sex=1   1
# 3   13    134       2        0 0.9565217 0.01814885 0.9911586 0.9230952  sex=1   1
# 4   15    132       1        0 0.9492754 0.01967768 0.9866017 0.9133612  sex=1   1
# 5   26    131       1        0 0.9420290 0.02111708 0.9818365 0.9038355  sex=1   1
# 6   30    130       1        0 0.9347826 0.02248469 0.9768989 0.8944820  sex=1   1
head(res.sum[res.sum$sex==2,])
#     time n.risk n.event n.censor      surv    std.err     upper     lower strata sex
# 120    5     90       1        0 0.9888889 0.01117336 1.0000000 0.9674682  sex=2   2
# 121   60     89       1        0 0.9777778 0.01589104 1.0000000 0.9477934  sex=2   2
# 122   61     88       1        0 0.9666667 0.01957401 1.0000000 0.9302835  sex=2   2
# 123   62     87       1        0 0.9555556 0.02273314 0.9990942 0.9139143  sex=2   2
# 124   79     86       1        0 0.9444444 0.02556550 0.9929738 0.8982868  sex=2   2
# 125   81     85       1        0 0.9333333 0.02817181 0.9863173 0.8831956  sex=2   2
  • 如上好像沒有看到 log-rank test的結(jié)果识颊,即組間生存率差異的顯著性诚镰。其實參看下面的可視化結(jié)果是有的,如果想提取出來P值的內(nèi)容祥款,可以如下操作:
surv_diff <- survdiff(Surv(time, status) ~ sex, data = lung)
# Call:
#   survdiff(formula = Surv(time, status) ~ sex, data = lung)
# 
# N Observed Expected (O-E)^2/E (O-E)^2/V
# sex=1 138      112     91.6      4.55      10.3
# sex=2  90       53     73.4      5.68      10.3
# 
# Chisq= 10.3  on 1 degrees of freedom, p= 0.001

#雖然如上結(jié)果是有P值的清笨,但是嘗試之后發(fā)現(xiàn)從對象中只能提取chisq的值,然后再進一步轉(zhuǎn)換
p.val = 1 - pchisq(surv_diff$chisq, length(surv_diff$n) - 1)
p.val
# [1] 0.001311165

2.4 survminer結(jié)果可視化

(1)生存曲線
ggsurvplot(fit,
           pval = TRUE, conf.int = TRUE,
           risk.table = TRUE)
圖釋

上圖表示:時間對應(yīng)的生存率的生存曲線圖刃跛,曲線中的短豎線表示在時間點有缺失值病人抠艾。不同顏色表示不同分組,陰影部分表示95%置信區(qū)間桨昙;左下角p值表示基于log-rank test計算得到的P值检号,對于本圖的結(jié)果p=0.0013腌歉,即不同性別的生存率有顯著差異。
下面的表:不同時間點對應(yīng)每組尚未發(fā)生事件(死亡)的數(shù)目齐苛。

圖形參數(shù)(結(jié)合具體代碼理解)
p1=ggsurvplot(fit,
           pval = TRUE, conf.int = TRUE,
           risk.table = TRUE, # Add risk table
           risk.table.col = "strata", # Change risk table color by groups
           linetype = "strata", # Change line type by groups
           surv.median.line = "hv", # Specify median survival
           ggtheme = theme_bw(), # Change ggplot2 theme
           palette = c("#E7B800", "#2E9FDF"))
p2=ggsurvplot(
  fit,                     # survfit object with calculated statistics.
  pval = TRUE,             # show p-value of log-rank test.
  conf.int = TRUE,         # show confidence intervals for 
  # point estimaes of survival curves.
  conf.int.style = "step",  # customize style of confidence intervals
  xlab = "Time in days",   # customize X axis label.
  break.time.by = 200,     # break X axis in time intervals by 200.
  ggtheme = theme_light(), # customize plot and risk table with a theme.
  risk.table = "abs_pct",  # absolute number and percentage at risk.
  risk.table.y.text.col = T,# colour risk table text annotations.
  risk.table.y.text = FALSE,# show bars instead of names in text annotations
  # in legend of risk table.
  ncensor.plot = TRUE,      # plot the number of censored subjects at time t
  surv.median.line = "hv",  # add the median survival pointer.
  legend.labs = 
    c("Male", "Female"),    # change legend labels.
  palette = 
    c("#E7B800", "#2E9FDF") # custom color palettes.
)
arrange_ggsurvplots(list(p1,p2))
(2)其它類型曲線
  • 參數(shù)fun="event"翘盖,表示cumulative event事件累計發(fā)生率
p3=ggsurvplot(fit,
           conf.int = TRUE,
           risk.table.col = "strata", # Change risk table color by groups
           ggtheme = theme_bw(), # Change ggplot2 theme
           palette = c("#E7B800", "#2E9FDF"),
           fun = "event")
  • 參數(shù)fun="cumhaz",表示cummulative hazard表示累計風(fēng)險水平(在時刻t凹蜂,事件發(fā)生的可能性)
p4=ggsurvplot(fit,
              conf.int = TRUE,
              risk.table.col = "strata", # Change risk table color by groups
              ggtheme = theme_bw(), # Change ggplot2 theme
              palette = c("#E7B800", "#2E9FDF"),
              fun = "cumhaz")
arrange_ggsurvplots(list(p3,p4))

3馍驯、Cox Proportional-Hazards Model比例風(fēng)險模型

3.1 簡單理解

  • 上面了解的log-rank test用于比較兩個分組的生存率是否有顯著差異,從而反映出這個分組因子的意義玛痊。但由于必須要分組汰瘫,所以必須使用分類變量,或者將連續(xù)變量(基因表達)轉(zhuǎn)為分組變量(中位數(shù)劃分)擂煞。
  • 而這里的Cox Proportional-Hazards Model可以理解為回歸模型混弥。其中回歸模型的響應(yīng)變量Y是Hazard rate(風(fēng)險因子,可以類比死亡率)对省;而預(yù)測變量就是想驗證是否會影響病人生存率的臨床特征剑逃、基因表達等變量(分類變量、連續(xù)變量均可)官辽。通過P值反映變量是否有顯著影響蛹磺;通過變量的系數(shù)coefficient表征如何影響Hazard rate,更常用的形式為:Hazard ratio(HR)=exp(coef)同仆;如果HR<1(coef<0)萤捆,則表明該變量越大,風(fēng)險越低俗批,生存率越高俗或;HR>1(coef>0),則表明該變量越大岁忘,風(fēng)險越高辛慰,生存率越小。

3.2 單變量Cox回歸

res.cox <- coxph(Surv(time, status) ~ sex, data = lung)
res.cox
# Call:
#   coxph(formula = Surv(time, status) ~ sex, data = lung)
# 
# coef exp(coef) se(coef)      z       p
# sex -0.5310    0.5880   0.1672 -3.176 0.00149
# 
# Likelihood ratio test=10.63  on 1 df, p=0.001111
# n= 228, number of events= 165
exp(res.cox$coefficients)
# sex 
# 0.5880028
  • 對多個變量分別進行單變量Cox回歸
#分別回歸
covariates <- c("age", "sex", "ph.karno", "ph.ecog", "wt.loss")
univ_formulas <- sapply(covariates,
function(x) as.formula(paste('Surv(time, status)~', x)))
univ_models <- lapply( univ_formulas, function(x){coxph(x, data = lung)})

#提取各個變量的回歸結(jié)果
univ_results <- lapply(univ_models,
                       function(x){
                         x <- summary(x)
                         p.value<-signif(x$wald["pvalue"], digits=2)
                         wald.test<-signif(x$wald["test"], digits=2)
                         beta<-signif(x$coef[1], digits=2);#coeficient beta
                         HR <-signif(x$coef[2], digits=2);#exp(beta)
                         HR.confint.lower <- signif(x$conf.int[,"lower .95"], 2)
                         HR.confint.upper <- signif(x$conf.int[,"upper .95"],2)
                         HR <- paste0(HR, " (",
                                      HR.confint.lower, "-", HR.confint.upper, ")")
                         res<-c(beta, HR, wald.test, p.value)
                         names(res)<-c("beta", "HR (95% CI for HR)", "wald.test",
                                       "p.value")
                         return(res)
                         #return(exp(cbind(coef(x),confint(x))))
                       })
res <- t(as.data.frame(univ_results, check.names = FALSE))
as.data.frame(res)
#             beta HR (95% CI for HR) wald.test p.value
# age       0.019            1 (1-1)       4.1   0.042
# sex       -0.53   0.59 (0.42-0.82)        10  0.0015
# ph.karno -0.016      0.98 (0.97-1)       7.9   0.005
# ph.ecog    0.48        1.6 (1.3-2)        18 2.7e-05
# wt.loss  0.0013         1 (0.99-1)      0.05    0.83

3.3 多變量Cox回歸(多元回歸)

res.cox <- coxph(Surv(time, status) ~ age + sex + ph.ecog, data = lung)
summary(res.cox)
# Call:
#   coxph(formula = Surv(time, status) ~ age + sex + ph.ecog, data = lung)
# 
# n= 227, number of events= 164 
# (因為不存在干像,1個觀察量被刪除了)
# 
# coef exp(coef)  se(coef)      z Pr(>|z|)    
# age      0.011067  1.011128  0.009267  1.194 0.232416    
# sex     -0.552612  0.575445  0.167739 -3.294 0.000986 ***
#   ph.ecog  0.463728  1.589991  0.113577  4.083 4.45e-05 ***
#   ---
#   Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
# 
# exp(coef) exp(-coef) lower .95 upper .95
# age        1.0111     0.9890    0.9929    1.0297
# sex        0.5754     1.7378    0.4142    0.7994
# ph.ecog    1.5900     0.6289    1.2727    1.9864
# 
# Concordance= 0.637  (se = 0.025 )
# Likelihood ratio test= 30.5  on 3 df,   p=1e-06
# Wald test            = 29.93  on 3 df,   p=1e-06
# Score (logrank) test = 30.5  on 3 df,   p=1e-06
ggforest(res.cox, data = lung, 
         main = "Hazard ratio",
         cpositions = c(0.10, 0.22, 0.4),
         fontsize = 1.0) 

3.4 注意點

(1)如果多變量cox回歸結(jié)果證明多個變量均影響Hazard rate帅腌,那么當(dāng)想要觀察其中一個變量所造成分組的生存率差異(KM plot)時,需要其它變量的值拉到統(tǒng)一水平(均值)
(2)Cox回歸一個重要的假設(shè)麻汰,就是變量對于hazard rate的影響不隨時間的變化而變化(等比例)速客,可通過統(tǒng)計方法檢驗變量數(shù)據(jù)是否適合Cox回歸分析。詳見 http://www.sthda.com/english/wiki/cox-model-assumptions

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末五鲫,一起剝皮案震驚了整個濱河市溺职,隨后出現(xiàn)的幾起案子绿满,更是在濱河造成了極大的恐慌少辣,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件伐谈,死亡現(xiàn)場離奇詭異瓦盛,居然都是意外死亡吕漂,警方通過查閱死者的電腦和手機背镇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門畴椰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人癞埠,你說我怎么就攤上這事状原×兀” “怎么了苗踪?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長削锰。 經(jīng)常有香客問我通铲,道長,這世上最難降的妖魔是什么器贩? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任颅夺,我火速辦了婚禮,結(jié)果婚禮上蛹稍,老公的妹妹穿的比我還像新娘吧黄。我一直安慰自己,他們只是感情好唆姐,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布拗慨。 她就那樣靜靜地躺著,像睡著了一般奉芦。 火紅的嫁衣襯著肌膚如雪赵抢。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天声功,我揣著相機與錄音烦却,去河邊找鬼。 笑死先巴,一個胖子當(dāng)著我的面吹牛其爵,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播伸蚯,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼醋闭,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了朝卒?” 一聲冷哼從身側(cè)響起证逻,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后囚企,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體丈咐,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年龙宏,在試婚紗的時候發(fā)現(xiàn)自己被綠了棵逊。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡银酗,死狀恐怖辆影,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情黍特,我是刑警寧澤蛙讥,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站灭衷,受9級特大地震影響次慢,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜翔曲,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一迫像、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧瞳遍,春花似錦闻妓、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至份蝴,卻和暖如春犁功,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背婚夫。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工浸卦, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人案糙。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓限嫌,卻偏偏與公主長得像,于是被迫代替她去往敵國和親时捌。 傳聞我的和親對象是個殘疾皇子怒医,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容