進階已經雙重審閱,尚未人工抽查

Cox 比例風險模型

HR 到底是什麼比值(不是存活時間比、也不是勝算比)、baseline hazard 不用指定為什麼還算得出係數、多變項校正實際上在做什麼、ties 怎麼處理,以及信賴區間跨過 1 的時候該怎麼寫。

這個模型在解決什麼問題

Kaplan-Meier 與 log-rank 可以告訴你兩條曲線不一樣,但給不了兩件事:差多少,以及扣掉其他因素之後還差多少

真實的臨床問題幾乎都是後者。女性肺癌病人存活比男性長——這是年齡的效果嗎?是體能狀態(performance status)的效果嗎?還是性別本身?而且分組變項如果是連續的(年齡、腫瘤大小、eGFR),log-rank 根本無從下手,你只能硬把它切成幾組,切點還是自己選的。

Cox 比例風險模型(Cox proportional hazards model)處理的就是這件事:同時放進多個變項(連續的、類別的都行),對每一個給出一個效果量,而且不需要假設存活時間服從任何特定分布。

核心概念:風險函數與 HR

先定義風險函數(hazard function)h(t)h(t):一個到 tt 這一刻為止還沒發生事件的人,在這一瞬間發生事件的速率。

h(t)=limΔt0P(tT<t+ΔtTt)Δth(t) = \lim_{\Delta t \to 0} \frac{P(t \le T < t + \Delta t \mid T \ge t)}{\Delta t}

這個式子有兩個關鍵:分子裡的條件 TtT \ge t(只算還在風險集合裡的人),以及它是個速率不是機率,可以大於 1。

Cox 模型寫成:

h(tx)=h0(t)exp(β1x1+β2x2++βpxp)h(t \mid x) = h_0(t) \cdot \exp(\beta_1 x_1 + \beta_2 x_2 + \cdots + \beta_p x_p)

拆開來看是兩塊乘在一起:

  • h0(t)h_0(t)基線風險(baseline hazard),所有變項都等於 0 的那個人的風險隨時間怎麼跑。它可以是任何形狀——先高後低、有峰、有波動都行。
  • exp(β1x1+)\exp(\beta_1 x_1 + \cdots) 是一個不隨時間變的倍數,把基線風險整條往上或往下拉。

於是兩個人的風險比值是:

h(tx=1)h(tx=0)=exp(β)\frac{h(t \mid x = 1)}{h(t \mid x = 0)} = \exp(\beta)

h0(t)h_0(t) 上下抵消掉了。這就是 hazard ratio(HR,風險比),也解釋了兩件事:為什麼 h0(t)h_0(t) 不用指定(它在比值裡消失了,這叫半參數 semi-parametric——效果的部分是參數化的,時間的部分不是),以及為什麼這叫「比例風險」(proportional hazards)——這個比值被假設在整段時間裡都是同一個數。

動手跑一次

library(survival)
data(cancer, package = "survival")   # lung、colon、mgus2 都在這包裡

lung$sex_f <- factor(lung$sex, levels = c(1, 2),
                     labels = c("Male", "Female"))

fit <- coxph(Surv(time, status) ~ age + sex_f + ph.ecog + wt.loss,
             data = lung)
summary(fit)        # coef、exp(coef) = HR、95% CI、p、Concordance

# 年齡改成「每 10 歲」的對比:要在係數尺度上乘,不是把 HR 乘 10
b  <- coef(fit)["age"]; se <- sqrt(vcov(fit)["age", "age"])
exp(10 * c(b, b - 1.96 * se, b + 1.96 * se))

# 大一點的真實試驗:colon 輔助化療,etype 2 = 死亡
colon_d <- subset(colon, etype == 2)
colon_d$rx <- factor(colon_d$rx, levels = c("Obs", "Lev", "Lev+5FU"))
fit2 <- coxph(Surv(time, status) ~ rx + sex + age + nodes + extent + differ,
              data = colon_d)
summary(fit2)

# 校正後的存活曲線:三組都代入同一組共變項值
ref <- data.frame(rx = factor(levels(colon_d$rx), levels = levels(colon_d$rx)),
                  sex = 1, age = 60, nodes = 2, extent = 3, differ = 2)
plot(survfit(fit2, newdata = ref), lwd = 2,
     col = c("#8c6d4a", "#4d6a8c", "#c44d4d"),
     xlab = "Days", ylab = "Adjusted survival")

驗證環境:R 4.6.0 + survival 3.8.6

survival::lung 多變項 Cox 模型的森林圖,四個變項的 HR 與 95% 信賴區間,女性與 ECOG 分數的區間不跨過 1,年齡與體重減輕的區間跨過 1。
survival::lung 的四變項 Cox 模型。虛線是 HR = 1。橫線跨過虛線就是本研究未偵測到該變項與死亡風險的關聯。產圖腳本 figures/scripts/B3-03-cox.R

怎麼讀報表

summary(coxph(...)) 印出來的東西,對應到論文 Table 的每一欄。用上面那個 lung 模型逐格看——先說清楚這個模型是用多少人跑的:資料集有 228 人,其中 15 人因為共變項有缺失被完整個案分析排除,實際進入模型的是 213 人、151 個事件

(掉了幾個人要講出來。coxph() 不會警告你,它只是安靜地少算幾列;而缺失往往與預後有關,所以這不是無害的四捨五入。世代研究那一章有整節在談這件事。)

變項HR95% CIp
年齡,每增加一歲1.010.99–1.030.165
女性 vs 男性0.550.39–0.78< 0.001
ECOG PS,每增加一分1.671.31–2.14< 0.001
體重減輕,每公斤0.990.98–1.000.176

逐欄怎麼講:

  • HR 的方向。 大於 1 是風險升高、小於 1 是降低。女性相對男性 HR = 0.55,讀作「在同年齡、同 ECOG、同體重減輕的條件下,女性任一時刻的死亡風險約為男性的 0.55 倍」。「在其他變項相同的條件下」這句話不是客套話,是 HR 的定義的一部分,拿掉就是另一個數字了。
  • 連續變項的單位。 年齡的 HR = 1.013,看起來小到沒意義——因為那是每多一歲。換成每 10 歲是 HR 1.14(0.95–1.38)。換算要在係數尺度做(exp(10β)\exp(10\beta)),不能把 HR 乘以 10。論文沒寫單位的連續變項 HR 是無法解讀的。
  • 信賴區間比 p 值有用。 年齡的區間是 0.995–1.033,跨過 1;體重減輕是 0.978–1.004,也跨過 1。
  • Concordance(Harrell’s C)= 0.647。 隨機挑兩個人,模型把「先發生事件的那個」給較高風險的機率。0.5 是亂猜,1 是完美。0.647 意思是這個模型有訊號但區辨力普通——這在臨床預後模型裡很常見,也是為什麼「p 值很顯著」跟「模型能不能拿來預測個別病人」是兩件事。
  • 整體模型檢定(likelihood ratio test)= 31.0,df = 4,p < 0.001。 問的是「全部係數同時為 0 嗎」,相當於迴歸裡的整體 F 檢定。

多變項校正實際上在做什麼

「校正年齡」聽起來像是把年齡的影響減掉,但模型裡發生的事更具體:在每一個事件時點,把該時點風險集合裡每個人的預測風險依各自的共變項加權,再問「實際發生事件的是不是預測風險較高的那個」。 係數就是讓這件事最一致的那組數字(技術上叫偏概似 partial likelihood)。

結果是:報出來的性別 HR,是「同年齡、同 ECOG、同體重減輕」的兩個人的比較。這帶來三個實務後果:

  1. 校正的變項換了,HR 就換了。 兩篇論文報不同的 HR,可能只是模型裡的變項不同,不是資料衝突。看 HR 一定要連著看那個模型放了什麼。
  2. 不能把中介因子(mediator)放進去。 如果治療是透過降低發炎指標起作用,把發炎指標校正進去,等於把治療的效果扣掉一部分。要決定放什麼,靠的是因果圖(DAG)不是 p 值。
  3. 事件數決定你能放幾個變項。 傳統經驗法則是每個變項至少 10 個事件(events per variable, EPV),近年的模擬研究認為視情況可以放寬,但方向不變:151 個事件的模型放四個變項還好,放十五個就是在配雜訊。

用一個真的臨床試驗看校正後的樣子——survival::colon 是大腸癌術後輔助化療試驗。它的原始檔是雙端點的長格式,每位病人各有復發與死亡兩列,一共 1858 列;本頁先用 etype == 2 篩出死亡端點的 929 列,再扣掉共變項缺失,模型用的是 888 人、430 例死亡

survival::colon 三個治療組的校正後存活曲線,Levamisole+5FU 組明顯高於觀察組,單獨 Levamisole 組與觀察組接近。
colon 試驗三組的校正後存活曲線:三條曲線都代入同一組共變項值(同年齡、同淋巴結數、同分期),所以差異只來自治療。這與直接畫 KM 不同——KM 畫的是各組實際收到的病人。產圖腳本 figures/scripts/B3-03-cox.R
變項HR95% CIp
Levamisole vs 觀察組0.9130.731–1.1410.423
Levamisole + 5FU vs 觀察組0.6730.530–0.8540.001
男性0.9670.799–1.1690.728
年齡,每增加一歲1.0060.998–1.0140.131
陽性淋巴結,每多一顆1.0911.071–1.111< 0.001
局部侵犯程度,每增加一級1.6171.293–2.023< 0.001
分化程度,每增加一級1.1570.949–1.4090.149

這張表把上一段那個警告變成了具體例子:Levamisole 單用的 HR 是 0.913,區間 0.731–1.141 跨過 1 —— 本試驗未偵測到單用 Levamisole 與死亡風險的關聯。而 Levamisole + 5-FU 的 HR 是 0.673(0.530–0.854),區間完全落在 1 的左邊。歷史上這個試驗正是 5-FU 併用方案成為大腸癌標準輔助治療的依據之一。

Ties:兩個人同一天發生事件

Cox 模型的推導假設事件時間是連續的,也就是不會有兩個人在完全同一刻發生事件。真實資料當然會——時間記到天、記到月、記到年,同時發生(ties)就一定出現。R 提供三種處理方式:

方法在做什麼什麼時候用
Efron對同時發生的事件做加權近似R 的預設,幾乎永遠選它
Breslow更粗的近似,把同時事件當成各自獨立處理SAS 的預設;ties 多時係數會被拉向 0
exact列舉所有可能的先後順序理論上最正確,ties 極多時很慢

這一節換回完整的 colon 死亡端點資料(不做完整個案排除,因為 ties 的比較只牽涉時間與治療組,不需要那些有缺失的共變項),所以事件數是 452 而不是上面模型表的 430——同一頁上兩個不同的數字,是因為分析集不同,不是其中一個算錯

時間以天為單位時,452 個死亡事件散在 409 個不同的日子,三種方法幾乎沒差:

方法Lev+5FU 的 HR淋巴結數的 HR
Efron0.67091.0957
Breslow0.67101.0956
exact0.67091.0958

但把同一份資料的時間粗化成「年」(452 個事件擠進 8 個時點),差距就出來了:

方法Lev+5FU 的 HR淋巴結數的 HR
Efron0.67471.0926
Breslow0.69551.0853
exact0.65701.1210

用之前要檢查的假設

Cox 模型有三個假設,其中最容易被忽略、後果最嚴重的是第一個:

  1. 比例風險 —— HR 在整段追蹤期間是同一個數。違反的話,那個單一 HR 是把不同時期相反的效果平均掉的產物,本身沒有清楚的意義。檢查方式與補救辦法在比例風險假設與 Schoenfeld residuals
  2. 連續變項與 log hazard 呈線性 —— 年齡對死亡風險的作用未必是直線。檢查方式是 martingale residuals 對該變項作圖,或直接改用限制性立方樣條(restricted cubic spline)。
  3. 非資訊性設限 —— 與所有存活方法共用的地基,見 Censoring 與存活資料的結構

另外,有競爭風險時,cause-specific Cox 回答的問題與 Fine-Gray 不同,這一點在競爭風險:CIF 與 Fine-Gray 裡談。

常見誤用

誤用為什麼錯
把 HR 當成存活時間的比值HR 比的是瞬時風險;存活時間比是 AFT 模型給的量
把 HR 當成勝算比或相對風險三者的分母與時間概念都不同;事件率低時數值接近但意義不同
只報 HR 不報基線風險或絕對風險差HR 0.5 在罕見事件上可能只換到極小的絕對獲益
報連續變項的 HR 卻不寫單位每一歲、每十歲、每個標準差的 HR 完全不同,讀者無法解讀
把 HR 換算成「每 10 單位」時直接乘 10要在係數尺度上乘再取 exp,exp(10β)\exp(10\beta) 不等於 10exp(β)10 \exp(\beta)
信賴區間跨過 1 還照樣描述效果大小應寫「未偵測到顯著關聯」,並附上區間說明不確定的範圍
未達顯著就宣稱「兩者效果相同」要主張相當需要非劣性/等效性設計與事先訂好的界值
事件數不多卻放進大量共變項過度配適,係數與信賴區間都不可信
把治療的中介因子當共變項校正掉會把想估的效果扣掉一部分;納入哪些變項靠因果圖不靠 p 值
跑完 Cox 不檢查比例風險假設假設不成立時那個 HR 沒有清楚的解釋
依基線之後才知道的狀態分組Immortal time bias,見 B3-01
直接比較兩篇論文的 HR 大小校正的變項不同,兩個 HR 定義的對比就不同

重跑本頁的所有數字

/opt/homebrew/bin/Rscript figures/scripts/B3-03-cox.R

讀讀看這張圖

答案取自產生本頁圖表的同一份統計輸出,不是另外打上去的。

這個 Cox 模型有四個共變項,summary 把風險比一列一列印出來。ECOG PS 那一列是哪一個?

看答案與解析

正確答案: 風險比 1.67——ECOG 每多一分,死亡風險上升約三分之二

ECOG PS 的分數越高代表體能狀態越差,所以它的風險比必然大於一,這一步不需要看報表就能排除另外兩個選項。0.55 其實是女性相對於男性,0.99 是體重每下降一公斤。這三個數字在 summary 裡上下相鄰,而 R 印出來的列順序取決於公式怎麼寫,不是你心裡的重要性順序——讀任何一個風險比之前先確認那一列的名字,是報表判讀裡最便宜也最常被跳過的一步。

報表的 n= 那一行,比原始資料的列數少。哪一個說法對?

看答案與解析

正確答案: 模型用了 213 人——任何一個共變項有缺值的整列都被丟掉了

coxph 預設做完整病例分析:只要任一共變項有缺值,整列就不進模型,所以 228 列裡只有 213 進去。這件事在輸出上只是係數表下面的一行 n= 與 number of events=,很容易滑過去;151 是事件數,不是人數。少掉的那十五個人不是隨機消失的,通常正是狀況最差、量測最不完整的那一群,所以那不只是樣本變小,而是樣本換了一批人。

同一個模型的 likelihood ratio test,它的自由度是由什麼決定的?

看答案與解析

正確答案: 自由度是 4,等於模型裡估計的共變項個數

likelihood ratio test 比的是「有這些共變項」與「什麼都沒有」兩個模型,自由度就是兩者相差的參數個數,這裡是四個共變項。15 是被缺值丟掉的列數,151 是事件數——報表上這些整數上下挨著,意思卻毫不相干。事件數確實會影響這個檢定有多少檢定力,但它不是自由度;而這個檢定回答的是「這四項合起來有沒有解釋力」,不是任何單一項顯不顯著。

延伸觀看

Hazard Ratios – Best explanation for beginners
ENThe Pharmacist Academy· 3 min兩分半就把 HR 是什麼講完,讀本頁第二節前先看這支,之後的公式會好懂很多。
Cox Regression [Cox Proportional Hazards]
ENnumiqo· 6 min六分鐘的模型總覽,動畫把「風險比固定」這件事畫出來。
COX REGRESSION and HAZARD RATIOS
ENBiostatsquid· 11 min把係數、HR、報表欄位一格一格對起來,正好對應本頁「怎麼讀報表」那一節。
存活分析(Survival Analysis)第二部分
繁中Ming-Chieh Shih· 18 min繁中,接續 B3-01 推薦的第一部分,這一集講到 Cox 模型與 HR 的中文說法。

素材來源與授權

本頁為原創內容

回報內容問題

這個站的統計內容由 AI 撰寫、AI 互審,人工只做抽查。你看得出來的錯,我們不一定看得出來。

寫得越具體越修得動,例如哪一句話跟哪本教科書/哪篇論文的說法不一致。

留了才回得了信;不留也會看。

一併送出的資訊

這些是自動帶上的,每一項都可以取消。