專家已經雙重審閱,尚未人工抽查

加權 log-rank、max-combo 與 milestone 存活率

曲線交叉或延遲效果時,log-rank 把每個事件時間等權加總,前後兩段的訊號會互相抵消。這一頁講 Fleming-Harrington 的權重長什麼形狀、免疫治療為什麼需要偏重晚期的權重、max-combo 把「我挑過三個」算進去要付多少代價,以及 milestone 存活率差的兩種信賴區間為什麼差那麼多。真實資料裡看不到「換個檢定就翻盤」,這件事本身也寫在頁面上。

log-rank 把每一個事件時間看得一樣重

Kaplan-Meier 與 log-rank 那一頁把 log-rank 拆開過: 它在每一個事件時間點上算一次「觀察到的死亡數減掉虛無假設下的期望死亡數」, 把這些差加起來,再除以標準誤。寫成式子是這樣:

U=jwj(OjEj)U = \sum_{j} w_j \,\bigl(O_j - E_j\bigr)

標準的 log-rank 就是把所有 wjw_j 都設成 1。這是一個選擇,不是一條定律, 而且它只有在風險比整段追蹤都固定時才是最有檢定力的那個選擇。

如果兩組的曲線交叉——前期一組較差、後期反超——那麼早期的 OjEjO_j - E_j 是正的、 晚期是負的,等權加起來會互相抵消,統計量趨近於零。 這不是「兩組真的一樣」,是這個加總方式看不見它。 免疫治療試驗的「前六個月兩條 PFS 曲線幾乎重疊、之後才分開」是 2019 年後的常態, 而那正好是 wj=1w_j = 1 最不擅長的形狀。

加權 log-rank 做的事只有一件:wjw_j 換掉。 其他一切不變。

這一頁的例子:一份比例風險明確不成立的資料

survival::veteran 是美國退伍軍人管理局的肺癌試驗,137 位病人、128 個死亡、只有 9 位設限。 選它不是因為它會給出戲劇性的結果,而是因為它的比例風險假設確實不成立:

項目χ²dfp
治療組別0.2110.650
Karnofsky 體能分數12.8113.4e-4
腫瘤細胞型別14.5330.002
整體(GLOBAL)22.8653.6e-4

模型是 coxph(Surv(time, status) ~ trt + karno + celltype),檢查用的是 cox.zph() 那一頁的做法。 整體的 p 是 3.6e-4,違反是明確的。

權重長什麼形狀

Fleming-Harrington 這一族用兩個參數 ρ\rhoγ\gamma 決定權重:

wj=S^(tj)ρ[1S^(tj)]γw_j = \hat{S}(t_j^-)^{\rho}\,\bigl[1 - \hat{S}(t_j^-)\bigr]^{\gamma}

S^\hat{S}合併兩組之後的 Kaplan-Meier 估計。三個常見的角落:

  • ρ=0, γ=0\rho = 0,\ \gamma = 0:權重恆為 1,就是標準 log-rank。
  • ρ=1, γ=0\rho = 1,\ \gamma = 0:權重等於 S^\hat{S},隨時間遞減,偏重早期(也就是 Peto / Gehan 那一系的精神)。
  • ρ=0, γ=1\rho = 0,\ \gamma = 1:權重等於 1S^1 - \hat{S},隨時間遞增,偏重晚期

抽象的話講到這裡就夠了,直接看數字。下表是這份資料的事件時間分位點上, 合併 KM 估計值與三種權重各是多少:

事件時間分位t(天)Ŝ(t⁻)ρ = 0ρ = 1FH(0, 1)
10%10.00.8981.0000.8980.102
30%29.10.7151.0000.7150.285
50%62.00.5311.0000.5310.469
70%125.60.3381.0000.3380.662
90%295.10.1171.0000.1170.883

看最後一列。到了第 295 天(最晚的 10% 事件所在的位置), ρ=1\rho = 1 給的權重只剩 0.117——而第 10 天的權重是 0.898。同一份資料,晚期一個死亡對統計量的貢獻被壓成早期的 13%,等於幾乎不看後期。 FH(0, 1) 那一欄剛好相反:同一個時點的權重是 0.883, 而在第 10 天只有 0.102。

兩個面板共用一條 0 到 400 天的橫軸。上方面板的縱軸是加在觀察減期望值上的權重,範圍 0 到 1。三條線:藍色實線是一條固定在 1 的水平線,代表 ρ = 0 的標準 log-rank;紅色實線從 1 起步一路下降,到右端已經接近 0,代表 ρ = 1 的偏重早期權重;綠色虛線與它鏡像,從 0 起步一路升到接近 1,代表 FH(0, 1) 的偏重晚期權重。紅線與綠線在權重 0.5 的高度交會,也就是合併存活曲線降到一半的時點;藍線恆在最上方,不與任何線交叉。紅線上標了兩個實心圓點與各自的垂直點線,標籤分別是 t = 10、w = 0.898 與 t = 295、w = 0.117。下方面板是同一段時間軸上實際事件時間的地毯圖,每一根短豎線代表一次死亡,密度明顯集中在前一百天,之後逐漸稀疏;面板標題寫明落在這個視窗內的死亡事件佔全部 128 個死亡的絕大多數。
同一份資料、同一組事件時間,三個檢定的差別只在這條權重曲線。下方的地毯圖說明為什麼 ρ = 1 的影響這麼大:事件本來就集中在前期,再把後期的權重壓下去,等於把統計量的注意力幾乎全部押在前段。產圖腳本 figures/scripts/B3-09-weighted-logrank.R

延遲效果為什麼需要 FH(0, 1)

免疫治療的典型形狀是這樣:前幾個月兩條曲線黏在一起(藥還沒發揮作用, 而且一部分病人根本不會反應),之後治療組的曲線平坦下來、對照組繼續往下掉。 真正的訊號全部在後段

此時 wj=1w_j = 1 的代價是:前段那一大堆「沒有差異」的事件時間, 每一個都以同樣的重量被算進 UU,把後段的差異稀釋掉。上表已經說明了問題有多大—— 事件時間的分佈本來就前密後疏,所以「等權」在實務上其實是偏重早期。 FH(0, 1) 只是把這個隱性偏誤反過來校正回去。

換了權重,結論沒有翻盤——而這正是要教的事

先看四組細胞型別的比較(4 組,df = 3):

權重χ²dfpα = 0.05 下來源
ρ = 0:標準 log-rank25.4031.3e-5達顯著survdiff
ρ = 0.5:略偏重早期22.7134.6e-5達顯著survdiff
ρ = 1:偏重早期19.7131.9e-4達顯著survdiff
FH(0, 1):偏重晚期25.7931.1e-5達顯著hand-computed

四組的人數與事件數是 squamous(鱗狀細胞癌)35 人、31 個死亡;smallcell(小細胞癌)48 人、45 個死亡;adeno(腺癌)27 人、26 個死亡;large(大細胞癌)27 人、26 個死亡。 再看兩組治療的比較:

權重χ²dfpα = 0.05 下來源
ρ = 0:標準 log-rank0.0110.928未達顯著survdiff
ρ = 0.5:略偏重早期0.4710.491未達顯著survdiff
ρ = 1:偏重早期0.8710.351未達顯著survdiff
FH(0, 1):偏重晚期0.8110.369未達顯著hand-computed

細胞型別的比較在四種權重下都達到顯著,結論沒有改變; 治療組別的比較在四種權重下都未達顯著,結論同樣沒有改變。 機器可讀的版本是 anyConclusionChanged 這個欄位,兩個對比都是 falsefalse

max-combo:多重性的代價是一個具體數字

如果事先不確定訊號會出現在早期還是晚期,一個誠實的做法是同時跑幾個權重, 取其中最極端的那個統計量,然後把「我挑過幾個」算進 p 值裡。這就是 max-combo。

本頁用的三個成分是 rho0rho1fh01, 各自的 z 值(以標準治療組的觀察減期望值為準,負號代表該組死亡數少於期望)是:

成分z方向
ρ = 0:標準 log-rank-0.091標準治療組死亡少於期望
ρ = 1:偏重早期-0.933標準治療組死亡少於期望
FH(0, 1):偏重晚期0.898標準治療組死亡多於期望

這張表把 log-rank 為什麼看不見東西講完了。 偏重早期的 z 是 -0.933、偏重晚期的 z 是 0.898, 兩者符號相反——早期與晚期指向不同的方向,正是下一節那張 milestone 圖裡曲線交叉的形狀。 而標準 log-rank 把這兩段等權加起來,得到的 z 是 -0.091, 幾乎就是零,對應到 p = 0.928。

三個統計量彼此高度相關,因為它們是在同一批事件時間上的加權和:

相關rho0rho1fh01
rho01.0000.8910.855
rho10.8911.0000.526
fh010.8550.5261.000

有了相關矩陣就可以積分三元常態,把「取最大值」這個動作的代價算出來:

報什麼p可以寫進論文嗎
事先指定的標準 log-rank0.928可以
三個權重裡最好看的那一個0.351不可以——它是挑出來的
max-combo(把挑選算進去)0.549可以,但要事先宣告成分

多重性的代價在這裡不是一句「記得校正」,是一個看得到的數字: 從 0.351 變成 0.549。 順帶一提,同樣的三個檢定用 Bonferroni 會校正到 1.052 (實務上截在 1),比 max-combo 保守得多——因為 Bonferroni 假設三個檢定互相獨立, 而上面那張相關矩陣說它們一點也不獨立。 相關性愈高,該付的多重性代價愈小,這一點與 多重比較 那一頁講的是同一件事。

milestone 存活率:第幾天的存活率差幾個百分點

Milestone survival 是最不需要統計訓練就能讀懂的那個寫法: 「一年存活率 X% 對 Y%,差 Z 個百分點」,而且它完全不依賴任何比例假設—— 它只讀 KM 曲線在某一個垂直切面上的高度。

兩條 Kaplan-Meier 階梯曲線疊在同一張圖上。橫軸是隨機分派後的天數 0 到 400,縱軸是存活機率 0 到 1;兩條線上都有代表設限的短豎記號,刻意沒有畫信賴區間帶。藍線是標準治療組(69 人、64 個死亡),紅線是試驗治療組(68 人、64 個死亡)。紅線在前期掉得明顯比較快,兩條線在第 180 天的標記線之前不久交叉,之後紅線反而略高於藍線,一路維持到右端。三條垂直點線分別畫在第 90、180、365 天,線頂標出時點與該時點的存活率差 16.7、-2.0、-3.9 個百分點;每條點線與兩條曲線相交處各有一個實心圓點。圖上方一行小字寫明曲線交叉、而 log-rank p = 0.928 看不出這件事。圖下方是風險人數表,刻度就是這三個 milestone 時點加上第 0 天與第 270 天:藍組從 69 人一路降到第 365 天的 4 人,紅組從 68 人降到 6 人。
兩組的 KM 曲線在追蹤中段交叉。圖上刻意不畫信賴區間帶——帶子會蓋住交叉點,而交叉點正是這張圖的重點。風險人數表的刻度對齊 milestone 時點:一個 milestone 存活率的可信度,取決於那一刻還有幾個人在被追蹤。產圖腳本 figures/scripts/B3-09-weighted-logrank.R

三個時點的數字(差值方向是標準治療組減試驗治療組):

時點(天)標準治療組試驗治療組差值(百分點)95% CI(百分點)
9054.7%(尚在追蹤 37)38.0%(尚在追蹤 25)16.70.1 到 33.2
18021.2%(尚在追蹤 13)23.3%(尚在追蹤 14)-2.0-16.5 到 12.4
3657.1%(尚在追蹤 4)11.0%(尚在追蹤 6)-3.9-14.2 到 6.5

注意差值的符號翻過去了:第 90 天是 16.7 個百分點、 第 180 天是 -2.0、第 365 天是 -3.9。 這正是 log-rank 的 p = 0.928 掩蓋掉的東西—— 一個把整段時間壓成一個數字的統計量,沒有辦法告訴你符號在中途換過邊。

順帶注意第 90 天那一列:差值的 95% CI 是 0.1 到 33.2 個百分點, 下界只差一點就碰到零。這種剛好落在邊緣的區間最需要問一句「這個時點是事先決定的嗎」—— 如果它是看過圖之後從三個裡挑出來的,就不能照字面讀,理由與上一節的 max-combo 完全相同。

兩種區間轉換給的答案不一樣

存活率的信賴區間有兩種常見算法,差別在先轉換再算區間、還是直接在機率尺度上算。 把第 365 天的兩組攤開來看:

組別存活率Greenwood(直接在機率尺度)log-log 轉換
標準治療組0.0710.005 到 0.1370.023 到 0.155
試驗治療組0.1100.030 到 0.1900.046 到 0.204

點估計是同一個 0.071,但標準治療組的下界 一個是 0.005、另一個是 0.023, 差了 4.7 倍。 原因是 Greenwood 的區間在機率尺度上是對稱的:點估計已經很接近 0 時, 下界會被推到 0 附近,再低一點就會壓到 0 以下——而存活率不可能是負的。 log-log 轉換先把估計值送到 log[logS^]\log[-\log \hat{S}] 這個沒有邊界的尺度上算區間、再轉回來, 所以區間永遠落在 0 與 1 之間,而且不對稱。腫瘤 RCT 慣用 log-log 就是為了這個。

自己跑一遍

library(survival)
data(cancer, package = "survival")
vet <- veteran
vet$trt_f <- factor(vet$trt, levels = c(1, 2), labels = c("Standard", "Test"))

# --- FH(rho, 0):survdiff 只接受 rho,權重就是 S(t-)^rho -------------------
survdiff(Surv(time, status) ~ trt_f, data = vet, rho = 0)    # 標準 log-rank
survdiff(Surv(time, status) ~ trt_f, data = vet, rho = 1)    # 偏重早期

# --- FH(0, 1):survdiff 做不到,要自己組風險集合表 -------------------------
et <- sort(unique(vet$time[vet$status == 1]))
nj <- sapply(et, function(t) sum(vet$time >= t))              # 風險集合大小
dj <- sapply(et, function(t) sum(vet$time == t & vet$status == 1))
Sminus <- c(1, cumprod(1 - dj / nj))[seq_along(et)]           # 左連續!
w_late <- 1 - Sminus                                          # FH(0, 1) 的權重

# U = sum(w * (O - E)),V 依權重平方加權;完整實作與健全性檢查見腳本
# figures/scripts/B3-09-weighted-logrank.R

# --- milestone 存活率與兩種區間轉換 ---------------------------------------
summary(survfit(Surv(time, status) ~ trt_f, data = vet, conf.type = "plain"),
        times = c(90, 180, 365))     # Greenwood,直接在機率尺度
summary(survfit(Surv(time, status) ~ trt_f, data = vet, conf.type = "log-log"),
        times = c(90, 180, 365))     # log-log 轉換

驗證環境:R 4.6.0 + survival 3.8.6;max-combo 的三元常態積分另外用 mvtnorm 1.4.2。survdiff() 只吃 rho,做不了 FH(0, 1)。

這一頁與其他頁的關係

  • log-rank 本體、KM 曲線怎麼讀——見 Kaplan-Meier 曲線與 log-rank 檢定。 那一頁講的是 wj=1w_j = 1 的版本,這一頁只換掉 wjw_j
  • 怎麼知道比例風險假設不成立、還有哪些補救路——見 比例風險假設:分層、時間分段、tt() 讓係數隨時間變。 那一頁也說明了為什麼「把追蹤期截短到假設成立為止」不是解法。
  • 想要一個有單位、可以對病人說的效果量——見 限制平均存活時間(RMST)。 加權 log-rank 給的仍然只是一個 p 值;它沒有效果量,這一點與標準 log-rank 完全一樣。 非比例風險的情境下,「差幾個月」或「第幾年差幾個百分點」比任何 p 值都好用。
  • 挑權重、挑時點的多重性怎麼算——見 多重比較與型一錯誤
  • 臨床試驗的統計章節整體怎麼讀——見 隨機對照試驗

常見誤用

誤用為什麼錯
標準 log-rank 沒過,再試幾個權重直到某個過了這是沒有校正的多重檢定;權重必須事先指定,或改用 max-combo
報 max-combo 的成分是事後決定的校正的是「取最大值」這個動作,不是「換一組成分再算一次」
把加權 log-rank 的 p 當成效果量的證據它跟 log-rank 一樣沒有效果量;要效果量請用 RMST 或 milestone 差值
曲線交叉還是報單一 HR那個 HR 是兩段相反效果的加權平均,沒有清楚的解釋
milestone 時點事後才決定與事後挑 τ、事後挑權重是同一種選擇性呈現
報 milestone 存活率不報該時點的風險人數讀者無法判斷那個百分比是幾個人撐起來的
存活率接近 0 或 1 時用 Greenwood 區間機率尺度上的對稱區間會越界;改用 log-log 轉換
兩種區間都算一遍,報比較窄的那個轉換方式要事先決定,事後挑等於挑結果
用 Ŝ(t) 而不是 Ŝ(t⁻) 當權重慣例是左連續;用錯不會報錯,統計量會系統性偏掉
加權檢定未達顯著就說「兩組效果相同」只能說未偵測到差異;要主張相當需要非劣性設計與事先設定的 margin

重跑本頁的所有數字

/opt/homebrew/bin/Rscript figures/scripts/B3-09-weighted-logrank.R

讀讀看這張圖

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

veteran 資料的治療分組,四種加權 log-rank 給出不同的 p 值,四個都不顯著。標準 log-rank 的 p 值是哪一個,它與另外幾個差在哪?

看答案與解析

正確答案: 0.928——標準 log-rank,對每一個事件時間一視同仁地加權

標準 log-rank 的定義就是不加權:每一個事件時間的貢獻同等看待,得到 0.928。0.491 與 0.351 是把權重往後期偏移的兩個版本——它們對「兩條曲線晚期才分開」比較敏感,代價是對早期的差別比較遲鈍。同一個對比、同一份資料,p 值從 0.928 一路走到 0.351:這裡三個都不顯著,所以結論沒有變;但只要其中一個掉到臨界值以下,而論文只報那一個,讀者就無從得知另外兩個長什麼樣子。

max-combo 檢定把數個加權統計量合起來。它的 p 值比其中最小的那個大,這個差額代表什麼?

看答案與解析

正確答案: 0.549——它是校正了「試過好幾種權重」之後的 p 值

0.549 是 max-combo 校正之後的 p 值,0.351 是那幾個檢定裡最小的一個。直接拿最小的當結論就是事後挑選:試了四種權重再挑最好看的一種,型一誤差已經不是名目上的那個水準了。兩者的差額正是這個多重性的代價,所以 max-combo 是一個誠實面對「試了好幾種權重」的工具,不是一個把結果變好看的工具。0.928 是標準 log-rank,max-combo 沒有理由回到它。

veteran 的 cox.zph 報表裡 GLOBAL 是顯著的。要知道是哪幾項違反了比例風險,該怎麼讀?

看答案與解析

正確答案: 逐列看,karno 的卡方是 12.81,它與另一項都違反,治療分組沒有

GLOBAL 只告訴你「模型裡有東西違反」,要知道是誰就得回到各項那幾列:karno 的卡方是 12.81,celltype 是 14.53,兩項都違反;治療分組的 0.21 沒有。只挑最大的那一項會漏掉另一個同樣違反的項,而最小的那個既不是整體也不是任何一種摘要。這一頁之所以要談加權 log-rank,正是因為比例風險在這份資料上站不住——標準 log-rank 在那種情況下不是錯,只是它把所有時間點一視同仁地加權。

素材來源與授權

本頁為原創內容

回報內容問題

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

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

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

一併送出的資訊

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