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

隨時間變動的區辨力

Harrell's C 把所有時點的區辨力壓成一個數字,而臨床決策發生在特定時點——你要跟病人講的是「五年」的風險。這一頁把同一個乳癌 Cox 模型的區辨力攤成一條隨時間變動的曲線:怎麼在時點 t 把存活資料切成 case 與 control、設限的人為什麼需要反機率設限加權、AUC(t) 為什麼系統性高於 Harrell's C,以及這條曲線平坦時該怎麼讀——平坦本身就是結論,因為你事前不知道它平不平。

一個 C 統計量看不出它自己漏掉了什麼

預測模型那一章與它底下的 內部驗證外部驗證校準三頁,整條線都建立在同一個 Cox 模型上, 而區辨力那一欄從頭到尾只有一個數字:Harrell’s C。

那個數字是這樣算出來的:把每一對「可以比較先後」的病人拿出來,看模型有沒有把先發生事件的 那位排在前面,然後對所有這樣的配對取平均。問題就在「所有」兩個字—— 它把兩年後的區辨力、五年後的區辨力、以及一對都拖到第六年才發病的病人,全部平均成一個數。

但臨床決策不發生在「所有可比配對的平均」上。 你要跟一位剛開完刀的乳癌病人講的是 「五年之內復發或死亡的機率」,或者是「兩年之內」——那是一個特定時點的問題。 審稿人問「五年的 AUC 是多少」,問的也是同一件事。

這一頁把那一個數字攤成一條曲線。

AUC(t) 在問的是一個時點上的問題

先把時間軸上的某一個 tt 釘住,例如五年。釘住之後,每個人在這個時點只有兩種身分:

  • case:在 tt 之前已經發生事件(本頁是復發或死亡,取先發生者)
  • control:到了 tt 仍然沒有發生事件

有了 case 與 control,就有了一張標準的二分類問題,於是也就有了 ROC 曲線與 AUC 的全套機器。這個版本叫 cumulative/dynamic AUC:case 是「累積到 tt 為止」發生事件的人(cumulative), control 是「tt 這個時刻仍然無事件」的人(dynamic)。寫成機率就是

AUC(t)=P(r^i>r^j    Tit,  Tj>t)\mathrm{AUC}(t) = P\bigl(\hat r_i > \hat r_j \;\big|\; T_i \le t,\; T_j > t\bigr)

其中 r^\hat r 是模型給的風險分數(Cox 模型就是線性預測值)。這與 那一頁的機率解釋是同一句話, 差別只在「誰是病人、誰不是」這件事現在tt 改變:同一個人在兩年時是 control, 在五年時可能已經變成 case。

這一頁沿用外部驗證那一頁的同一個模型

不重新認識一份資料,是刻意的:這一頁的整個論證是「一條曲線 vs 一個數字」, 而那個數字必須是讀者在外部驗證那一頁 已經見過的同一個 Harrell’s C,比較才誠實。

開發集是 survival::rotterdam(2982 位病人、 1713 個 recurrence-free survival 事件), 驗證集是 survival::gbsg(686 位、 299 個事件,最長追蹤 7.28 年)。 Cox 模型的四個共變項是:

  • 年齡(手術當時,連續變項)
  • 腫瘤大小(毫米);rotterdam 記的是分級區間,取各級中點以配合 gbsg 的毫米
  • 陽性淋巴結數(連續變項)
  • 腫瘤分級(第三級以上為 1,否則為 0);兩個世代分級的細緻度不同,所以併成二分變項

把區辨力攤開來看

折線圖。橫軸是預測時點,從 1.0 年到 6.0 年;縱軸是 time-dependent AUC(t),畫的是 0.50 到 1.00 的完整範圍,刻意不裁切到曲線實際占據的那一小段,否則一條平坦的曲線會被畫成戲劇性的起伏。深藍色折線帶圓點標出 11 個時點的 AUC(t),整條線在 0.669 到 0.711 之間小幅上下,最低點在 4.5 年、最高點在 6.0 年。淡藍色的 95% 信賴區間帶包在折線兩側,左端與右端都比中段寬,右端最寬(6.0 年時是 0.630 到 0.792)。一條紅色水平虛線畫在同一個模型的 Harrell's C = 0.642,整條藍色折線都落在這條紅線上方,但信賴區間帶在多數時點仍然蓋過它。底部另有一條棕色點虛線畫在 0.50,右下角標註 0.50 = coin flip,代表完全沒有排序能力。右上角是三項圖例,標題下方一行副標寫出全距與兩個極值的位置。
同一個模型在外部世代上的區辨力,逐時點估計(IPCW)。紅色虛線是它自己的 Harrell's C——一個數字,橫跨所有 t。縱軸畫滿 0.50 到 1.00 是刻意的:裁切縱軸會讓任何一條曲線看起來都有起伏。產圖腳本 figures/scripts/B5-08-time-dependent-auc.R

為什麼整條曲線都在 Harrell’s C 上面

讀者看這張圖的第一個反應,通常是「那兩個數字哪一個算錯了」。 網格上 11 個時點裡,有 11 個時點的 AUC(t) 點估計高於同一個模型的 Harrell’s C(0.642);連曲線最低的那一點(4.5 年) 都還高出 0.028,落差最大處是 0.069。

沒有人算錯,因為兩者估的根本不是同一個量。

  • AUC(t) 只問一個問題:一位在 tt 之前發病的病人,分數有沒有比一位在 tt 時仍無事件的 病人高。它從來不比較兩位都在 tt 之前發病的人誰先誰後——在 tt 這個時點上,他們是同一類。
  • Harrell’s C 會比。它對所有可比配對取平均,其中包含「一位在第五年發病、一位在第六年發病」 這種配對。要把兩位預後都很差、只差幾個月的病人排對,接近擲銅板——而這類配對在長追蹤的 世代裡數量很多,它們把平均值往 0.5 拉。

還有第二個來源:Harrell’s C 的計算會受到設限分布的影響(哪些配對「可比」取決於誰何時被設限), 而本頁的 AUC(t) 已經用 IPCW 把設限的影響校正掉了。

不過方向一致不等於每一點都分得開:11 個時點裡, 有 8 個時點的 95% 信賴區間仍然把 Harrell’s C 包在裡面。 所以這一段話的正確版本是「AUC(t) 系統性地落在 Harrell’s C 上方」, 而不是「每一個時點都與 Harrell’s C 有統計上可分辨的差距」——後者本資料撐不起來。

平坦本身就是這一頁的結論

現在誠實面對這條曲線的形狀。

在這個模型、這個外部世代上,11 個時點的 AUC(t) 落在 0.669(4.5 年)到 0.711(6.0 年)之間,全距 0.042, 標準差 0.014。

但只引全距會高估這條曲線的起伏。 最低點與最高點的 95% 信賴區間是重疊的(0.620–0.719 對上 0.630–0.792)—— 也就是說整條曲線的上下擺動落在它自己的不確定度之內,本資料未偵測到 AUC(t) 隨時間變動。 這句話不等於「AUC(t) 不隨時間變動」:11 個時點的區間都不窄, 中等幅度的變化它一樣排除不掉。

臨床論文實際會引用的三個時點差距更小:

時點AUC(t)95% CI仍在風險集合累積事件數
2.0 年0.6740.624–0.723458165
3.0 年0.6940.649–0.739331224
5.0 年0.6750.620–0.731121285

這三個時點彼此相差 0.020。

右端的信賴區間為什麼變寬

整條網格攤開來看,會看到全距是怎麼來的:

時點AUC(t)95% CI區間寬度仍在風險集合累積事件數
1.0 年0.6840.607–0.7610.15460256
1.5 年0.6870.632–0.7420.110530117
2.0 年0.6740.624–0.7230.098458165
2.5 年0.6810.634–0.7270.093383205
3.0 年0.6940.649–0.7390.090331224
3.5 年0.6890.644–0.7350.091277245
4.0 年0.6730.626–0.7210.095228263
4.5 年0.6690.620–0.7190.099183274
5.0 年0.6750.620–0.7310.111121285
5.5 年0.7080.647–0.7700.12374290
6.0 年0.7110.630–0.7920.16236296

最後一欄與倒數第二欄解釋了圖上那個往右變寬的喇叭口:時間愈往後, 還留在風險集合裡的人愈少(從 602 位掉到 36 位), control 那一邊可用的人就愈少,而 IPCW 的權重同時愈來愈大—— 兩件事都會讓估計變不穩。整條曲線最寬的區間出現在 6.0 年,寬 0.162, 比前面那個全距 0.042 還大了好幾倍。

而 6.0 年剛好就是全域最大值所在。 換句話說,那個全距有相當一部分是這一點的雜訊撐出來的,不是模型在後期真的變厲害了。 讀 AUC(t) 曲線的第一個習慣,就是先看那個時點還剩幾個人,再看曲線的高低。

固定一個時點的 ROC 長什麼樣子

AUC(t) 是一個面積,面積底下有一條曲線。把 tt 釘在 5.0 年畫出來:

正方形的 ROC 圖,時點寫在主標題裡:t = 5.0 年。副標把 case 與 control 的定義攤開——case 是到 5.0 年為止已經復發或死亡的人,control 是 5.0 年時仍然無事件的人。橫軸是 1 減特異度(偽陽性率),縱軸是敏感度,兩軸都從 0 到 1。深藍色的階梯曲線從左下角起步後陡升,在偽陽性率還很低的時候就已經爬到將近四成的敏感度,中段出現一小段接近水平的平台,之後再度上升,最後在右上角收於 (1, 1);曲線與橫軸之間以淡藍色填滿,那塊面積就是 AUC(t)。灰色對角虛線是 chance line,代表 AUC 0.5。右下角有三行標註:AUC(5.0 y) = 0.675(紅色)、95% CI 0.620 到 0.731、以及 121 still at risk at 5.0 years,最下面是 chance line 的圖例。
時點寫在主標題而不是只寫在圖說裡:一張沒有附時間的 time-dependent ROC,是這種圖最常被誤讀的方式。曲線的階梯很細,因為 5.0 年這個切面上有 285 個累積事件撐著;121 位仍在風險集合的人構成 control 那一側。產圖腳本 figures/scripts/B5-08-time-dependent-auc.R

這張圖與一般的 ROC 曲線看起來一模一樣, 但底下的資料結構差了三件事,而三件都會影響你怎麼讀它:

一般的 ROCtime-dependent ROC
誰是 case結果已知、固定不變t 而定;同一個人在不同 t 可能換邊
不知道結果的人通常直接排除(而且該說明排除了幾位)設限的人以 IPCW 加權留下來
一張圖夠不夠夠,那就是全部的資訊不夠,每個 t 都有自己的一張

動手跑一次

library(survival)
library(timeROC)
data(cancer, package = "survival")

# 兩個世代的欄位調和與外部驗證那一頁完全相同,一個字都沒有改
rot <- rotterdam
rot$rfs_time  <- pmin(rot$rtime, rot$dtime)
rot$rfs_event <- as.integer(rot$recur == 1 | rot$death == 1)
rot$size_mm   <- c("<=20" = 15, "20-50" = 35, ">50" = 60)[as.character(rot$size)]
rot$grade3    <- as.integer(rot$grade >= 3)

ext <- gbsg
ext$rfs_time  <- ext$rfstime
ext$rfs_event <- ext$status
ext$size_mm   <- ext$size
ext$grade3    <- as.integer(ext$grade >= 3)

v   <- c("age", "size_mm", "nodes", "grade3")
fit <- coxph(Surv(rfs_time, rfs_event) ~ age + size_mm + nodes + grade3, data = rot)

# 風險分數:自己算 X %*% beta,不用 predict() 的置中版本(與 B5-05 一致)
lp <- as.numeric(as.matrix(ext[, v]) %*% coef(fit))

# 一個數字
concordance(Surv(ext$rfs_time, ext$rfs_event) ~ lp, reverse = TRUE)

# 一條曲線。weighting = "marginal" 就是 Kaplan-Meier 的設限權重(IPCW)
grid <- seq(1, 6, by = 0.5) * 365.25
roc  <- timeROC(T = ext$rfs_time, delta = ext$rfs_event, marker = lp,
                cause = 1, times = grid, weighting = "marginal", iid = TRUE)

auc <- as.numeric(roc$AUC)
se  <- as.numeric(roc$inference$vect_sd_1)   # iid = TRUE 才有
cbind(years = grid / 365.25, auc = auc,
      lcl = auc - 1.96 * se, ucl = auc + 1.96 * se)

# 每個時點還剩幾個人——讀曲線之前先看這個
sapply(grid, function(t) sum(ext$rfs_time >= t))

# 某個時點的 ROC 座標,可以自己畫
k <- which(roc$times == 5 * 365.25)
plot(roc$FP[, k], roc$TP[, k], type = "l", xlim = c(0, 1), ylim = c(0, 1))
abline(0, 1, lty = 2)

驗證環境:R 4.6.0 + survival 3.8.6 + timeROC 0.4.1。iid = TRUE 是必要的:沒有它 timeROC 不會算標準誤,也就畫不出信賴區間帶。cause = 1 指的是「事件」這個編碼,不是競爭風險的第一型事件(要做競爭風險下的 AUC(t) 要另外把 delta 編成多類)。

這一頁與其他頁的關係

  • Harrell’s C 本身怎麼算、為什麼表面表現一定偏高 —— 見 內部驗證。本頁只是把同一個量攤到時間軸上, 樂觀偏誤那一整套問題並沒有因此消失:在開發集自己身上算 AUC(t),一樣是偏高的。
  • 本頁的模型、世代與那個 Harrell’s C 從哪裡來 —— 見 外部驗證。那一頁還示範了另一件事: 光看鑑別力分不出三種驗證設計的優劣,差別要看校準才顯現。
  • 區辨力好不等於機率算得準 —— 見校準。 AUC(t) 與 Harrell’s C 一樣只看排序:把每個人的預測風險同乘一個常數, 這一頁的曲線一位小數都不會動,而每個人的預測機率都錯了。 一條漂亮的 AUC(t) 曲線不能替校準背書。
  • 一般的 ROC 與 AUC —— 見 ROC 曲線與 AUC。 兩者的機率解釋是同一句話,差別在 case 與 control 的身分會不會隨時間改變。
  • 設限到底是什麼、獨立設限假設在假設什麼 —— 見 設限與追蹤時間。IPCW 是把那條假設用進估計裡, 不是把它拿掉。
  • 整章的臨床脈絡 —— 見預測模型研究

讀論文裡的 AUC(t) 時要問的四件事

  1. 時點是哪些,為什麼是這些? 事後挑一個 AUC 最漂亮的時點來報, 與挑切點是同一種問題(見切點選擇)。時點應該由臨床決定。
  2. 那個時點還剩幾個人在風險集合裡? 論文很少寫,但這決定了信賴區間的寬度。 沒有附風險人數就把最右端那一點當結論,是這類圖最常見的誤讀。
  3. 設限怎麼處理的? 有沒有寫 IPCW(或等價的加權)。只寫「以 ROC 分析評估」的, 多半是把設限的人當 control 或直接刪掉。
  4. 有沒有一起報校準? 只報 AUC(t) 的外部驗證,仍然只報了 三件套的三分之一。

常見誤用

誤用為什麼錯
報 time-dependent AUC 卻不寫時點AUC(t) 沒有 t 就沒有定義,讀者無法重現也無法比較
把設限的人當成 control等於假設失聯的人都沒事,區辨力被灌高
把設限的人直接刪掉剩下的人不再代表原世代,而失聯通常與預後有關
事後挑 AUC 最高的時點來報與挑切點同一種問題,報出來的數字不再是無偏的
把曲線畫到追蹤尾端還在下結論風險集合已經剩很少人,那一段是雜訊不是訊號
看到曲線平坦就說「AUC(t) 沒有隨時間變化」只能說本資料未偵測到變化;區間不窄,中等幅度的變化排除不掉
因為某個模型的曲線平坦,就宣稱不必畫平不平是算完才知道的,不能事前假設
把 AUC(t) 與 Harrell’s C 當成同一個數字互相檢查兩者估的不是同一個量,不相等是正常的
縱軸裁切到曲線所在的範圍任何一條平坦的曲線都會被畫成戲劇性的起伏
有了漂亮的 AUC(t) 就不報校準區辨力對整條風險刻度的平移與縮放完全免疫
在開發集自己身上算 AUC(t) 並稱為驗證樂觀偏誤照樣存在,換一個指標並不會讓它消失

重跑本頁的所有數字

/opt/homebrew/bin/Rscript figures/scripts/B5-08-time-dependent-auc.R

讀讀看這張圖

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

圖上的 AUC(t) 折線整段都在同一個模型的 Harrell's C 上方,最大落差 0.069。哪一個說法對?

看答案與解析

正確答案: Harrell's C 是 0.642,它會去比兩位都已經發病的病人誰先誰後,而 AUC(t) 從來不比這種配對

AUC(t) 只問一件事:一位在時間視野之前發病的病人,分數有沒有比一位當時仍無事件的病人高。它不比較兩位都已經發病的人誰先誰後,Harrell's C 會——那類配對接近擲銅板,在追蹤長的世代裡數量又多,把平均往下拉。所以 0.642 與整條曲線之間的落差不是誰算錯,是兩個量的定義不同;Harrell's C 還會受設限分布影響,而這一頁的 AUC(t) 已經用 IPCW 把設限校正掉了。至於 0.675 那個上界:曲線多數時點的點估計高於它,但高於上界不等於分得開,網格上仍有一部分時點的 AUC(t) 信賴區間把 Harrell's C 包在裡面,所以正確的說法是系統性偏高,不是每一點都有統計上可分辨的差距。0.686 是曲線自己的平均。

網格上各時點的 AUC(t) 全距只有 0.042,標準差 0.014。這代表什麼?

看答案與解析

正確答案: 同一條曲線最寬的信賴區間就有 0.162,比全距大上好幾倍——曲線的上下擺動落在它自己的不確定度之內

全距是兩個點估計的差,而每一個點估計都帶著自己的不確定度。這條曲線最寬的區間 0.162 比全距 0.042 大上好幾倍,最低點與最高點的信賴區間彼此重疊,所以要說的是本資料未偵測到 AUC(t) 隨時間變動——不是隨時間變動但幅度不大,後者把一個沒有被偵測到的差異當成已經量到的。0.711 出現在最後一個時點沒錯,可是那個時點的區間正好是全曲線最寬的,用它主張長期排序比較好是拿雜訊當訊號。反過來,這句話也不等於 AUC(t) 一定不隨時間變動:各時點的區間都不窄,中等幅度的變化一樣排除不掉。0.669 是最低點。

整條曲線最寬的信賴區間出現在最後一個時點,寬 0.162,而全域最大值也落在同一點。讀這張圖時該先做什麼?

看答案與解析

正確答案: 先看那個時點還剩幾個人——最後一個時點只剩 36 人,那個最大值有一部分是雜訊撐出來的

時間愈往後,還留在風險集合裡的人愈少——從 602 掉到 36——控制那一側可用的人就愈少,而 IPCW 的權重同時愈來愈大,兩件事都讓估計變不穩。所以最後一個時點的區間寬 0.162,比整條曲線的全距大上好幾倍,而全域最大值剛好落在這一點。累積事件數 296 確實是全曲線最多的,但 AUC(t) 要的是事件那一側與當時仍無事件那一側配對,控制那一側只剩 36 人,事件再多也補不上。讀 AUC(t) 曲線的第一個習慣,是先看那個時點還剩幾個人,再看曲線的高低。

AUC(t) 的網格停在六年,沒有畫到八年。理由是什麼?

看答案與解析

正確答案: 這個外部世代最長的追蹤只有 7.28 年,八年那個時點在這批人身上根本觀察不到

網格停在六年,是因為再往後不是估計而是外插:這個外部世代最長的追蹤是 7.28 年,八年那個時點在這批人身上根本觀察不到。4.50 年是追蹤時間的中位數,它決定不了網格的上限——中位數連一半的人都還沒看完,而上限問的是資料裡還有沒有人。0.16 是六年那一點的區間寬度;把 IPCW 估計推到資料之外不會報錯,它只會給你一個信賴區間寬到無法反駁的數字,而那正是這一族頁面存在的目的所要防止的事。

把時點釘在五年畫出一條 ROC 曲線,看它的曲線下面積。這個面積在說什麼?

看答案與解析

正確答案: 0.675 是隨機挑一位五年內發病與一位當時仍無事件的病人、模型給前者較高分的機率

三個選項擺的是同一個數字,要選的是它被讀成什麼。它是一個機率:隨機挑一位在時間視野之前發病的病人與一位當時仍無事件的病人,模型給前者較高分數的機率是 0.675。讀成正確率是錯的——正確率要先挑一個切點把每個人判成陽性或陰性,而 ROC 曲線正是把所有切點一次畫完,還沒選切點就談分對的比例,等於在問一個這條曲線沒有回答的問題。讀成校準也是錯的:面積只認排序,把每個人的預測風險一律減半,誰高誰低完全不變,面積一個字都不會動,而校準已經整個垮掉。排序與校準是兩個問題,同一個模型可以排序尚可而校準不足,那正是校準那一頁在同一個模型上量到的事。

素材來源與授權

本頁為原創內容

回報內容問題

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

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

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

一併送出的資訊

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