基礎已經雙重審閱,尚未人工抽查

Censoring 與存活資料的結構

為什麼「還沒發生事件」不是遺漏值、右/左/區間設限各自長什麼樣、風險集合怎麼隨時間縮小、非資訊性設限這個假設撐著整套方法,以及 immortal time bias 是怎麼把一個未偵測到效果的治療變成救命神藥。

這一頁在解決什麼問題

你收了 228 位晚期肺癌病人,追蹤到研究結束。有些人死了,你知道確切日期;有些人到最後一次回診都還活著;還有人搬到中南部、電話打不通了。現在要回答「這群人活多久」。

第一個直覺是算平均存活天數。但你馬上會卡住:那些還活著的人,存活天數該填多少?填最後一次回診的日子,等於假設他們那天就死了;空著不算,等於把所有活得久的人都刪掉。

這不是資料品質的問題,是這類資料本來的形狀。 一個追蹤了 400 天還沒死的人,他的資料不是遺漏值,而是一句明確的陳述:「這個人的存活時間 > 400 天。」存活分析整套工具存在的理由,就是把這種「只知道一邊界限」的觀察算進去,而不是丟掉。

我們用 survival::lung 這份資料當例子:228 人,其中 165 人在追蹤期間死亡、63 人(27.6%)到最後仍未觀察到事件。

28 位肺癌病人的追蹤時間長條圖,每人一條橫線,紅點代表觀察到死亡、藍色箭頭代表追蹤結束時仍存活,長度差異很大。
從 survival::lung 隨機抽 28 人。每一條橫線是一個人的追蹤期。紅點是觀察到的死亡,藍色箭頭是右設限——箭頭的意思正是「還沒結束,時間至少這麼長」。產圖腳本 figures/scripts/B3-01-censoring.R

三種設限,長得不一樣

「設限」(censoring)在中文文獻裡也譯作「censor」「檢查」「設限」,指的是事件時間沒有被精確觀察到,只知道它落在某個範圍。臨床研究裡幾乎只會遇到第一種,但知道另外兩種存在,你才知道自己遇到的是哪一種。

類型你知道的事典型情境
右設限(right-censoring)事件時間 > 某個已知時點研究結束時仍存活、失去追蹤、退出研究
左設限(left-censoring)事件時間 < 某個已知時點第一次檢驗就已經陽性,不知道何時感染的
區間設限(interval-censoring)事件時間落在兩次觀察之間每半年做一次影像,這次發現復發——只知道在前後兩次之間

另外要跟設限區分開的是截切(truncation)。設限是「這個人在資料裡,只是他的時間不完整」;截切是「這個人根本不會出現在資料裡」。例如只收「來到醫學中心的病人」的登錄資料,發病後立刻死在急診的人永遠不會被納入——這是左截切,資料裡看不到缺口,但族群已經被篩選過了。

左截切還有一個較溫和、也更常撞到的形態:人進得來,只是進得晚——健保或院內登錄的收案日晚於診斷日,或者以年齡當時間軸時每個人在自己的年齡處才進入風險集合。這種情況下 Surv(time, event) 不會報錯,只會把存活率系統性地高估。做法、代價與實際偏誤有多大,見 左截切與延遲進入

風險集合:曲線的分母隨時間在縮小

存活分析的每一個估計,本質上都是在一連串時點上問同一件事:在這一刻還「有可能被觀察到發生事件」的人裡面,發生了幾件? 這群人叫做該時點的風險集合(risk set)。

風險集合只會縮小,理由有兩個:有人發生了事件(離開)、有人被設限(也離開)。在上面那張圖裡,日 300 那條虛線就是在示範——橫線有跨過虛線的人,才屬於第 300 天的風險集合。

survival::lung 的風險集合實際縮成這樣:

時點(天)仍在風險集合KM 估計的存活率
10019686.4%
3009253.1%
5004129.3%
80087.8%

非資訊性設限:整套方法的地基

上面所有做法都靠同一個假設撐著:非資訊性設限(non-informative censoring,也叫獨立設限 independent censoring)。

白話說法是:一個人在時間 tt 被設限,不能夠透露他在時間 tt 的風險高低。 更精確一點,被設限的人在被設限那一刻的預後,應該與那一刻仍在追蹤、條件相同的人一樣。

這個假設在什麼時候成立、什麼時候破功:

設限原因假設成立嗎為什麼
研究結束、大家一起收工(行政設限)✅ 通常成立收工日期與病人狀況無關
隨機的失聯(搬家、換醫院)✅ 大致成立前提是搬家與病情無關
病情惡化所以退出試驗❌ 破功被設限的人風險比留下來的高,KM 會高估存活率
感覺好了所以不回診❌ 破功方向相反,KM 會低估存活率
換到另一種治療所以停止追蹤❌ 破功換藥的理由通常就是療效不好

中位追蹤時間:一個很多人算錯的數字

論文 Results 第一句常常是「中位追蹤時間 X 個月」。這個數字不是所有人觀察時間的中位數——因為死掉的人的觀察時間是他的存活時間,不是他被追蹤了多久。

正確做法是反轉 Kaplan-Meier(reverse KM):把事件指標對調,讓「還在被追蹤」變成事件,再取中位數。

survival::lung 裡兩個算法差很多:

  • 直接取觀察時間的中位數:255.5 天(錯的)
  • 反轉 KM 的中位追蹤時間:588 天(95% CI 511–1010)

差了兩倍以上。前者其實比較接近中位存活時間,拿它當追蹤時間會讓讀者以為追蹤期比實際短得多。

Immortal time bias:把未偵測到效果的治療變成神藥

這是臨床研究裡最常見、也最容易在自己的分析裡犯的設計錯誤。它的機制其實很單純:如果分組的依據要等到基線之後才知道,那麼「等到知道」的那一段時間,被分到某一組的人是不可能死的——不然他們不會被分到那組。

那一段時間叫做 immortal time(不死時間)。它被錯誤地計入了治療組的「存活時間」,於是治療組憑空多出一截保證活著的時間。

最乾淨的教學例子是史丹佛換心試驗(survival::jasa):103 位病人上了等待名單,其中 69 位最後接受了移植、34 位沒有。

左圖是依「是否曾接受移植」分組的 KM 曲線,移植組明顯較高;右圖是六位真實病人的時間軸,移植前的等待期以粗黃線標出。
左:把「是否曾接受移植」當成基線分組畫出來的 KM 曲線,移植組看起來好非常多。右:六位實際接受移植的病人,黃色是他們在等待器官的期間——在這段時間裡,他們不可能死,否則就不會出現在「移植組」。這段時間被算進了移植組的存活。產圖腳本 figures/scripts/B3-01-censoring.R

兩種分析法,同一份資料:

分析方式移植的 HR95% CIp
天真做法:依「是否曾移植」在基線分組0.270.17–0.43< 0.001
正確做法:移植當作時間相依變項1.140.63–2.050.67

天真做法說移植讓死亡風險降到 26.6%,p 值小到不行;正確做法未偵測到差異(信賴區間跨過 1)。兩者的差別,核心機制就是那 2600 人天的等待時間(中位數 25 天)被算成了移植組的存活——這段時間裡他們依定義不可能死,卻被記在移植組的帳上。

動手跑一次

library(survival)
data(cancer, package = "survival")   # lung 放在 cancer 這包資料裡
data(heart,  package = "survival")   # 同時載入 jasa 與 heart

# 存活資料的最小單位:Surv(時間, 事件指標)
# lung 的 status:1 = censored, 2 = dead
head(Surv(lung$time, lung$status))   # 有 "+" 號的就是設限

# 風險集合怎麼縮小:KM 物件的 n.risk 欄
fit <- survfit(Surv(time, status) ~ 1, data = lung)
summary(fit, times = c(100, 300, 500, 800))

# 中位追蹤時間:反轉事件指標,讓「還在追蹤」變成事件
survfit(Surv(time, 1 - (status == 2)) ~ 1, data = lung)

# ── immortal time bias ──
# 錯的:把「是否曾移植」當基線分組
jasa$tx <- factor(jasa$transplant, 0:1, c("No", "Yes"))
coxph(Surv(futime, fustat) ~ tx, data = jasa)

# 對的:heart 已經是 (start, stop] 長表,transplant 隨時間改變
head(heart[heart$id %in% c(2, 4), ])
coxph(Surv(start, stop, event) ~ transplant, data = heart)

驗證環境:R 4.6.0 + survival 3.8.6

怎麼讀報表

看到一篇存活分析的論文,這一頁對應的是 Methods 與 Results 開頭那幾句,重點看四件事:

  1. 時間零是哪一天,全部人都一樣嗎。 隨機分配日?診斷日?手術日?如果兩組的時間零定義不同,後面所有數字都不用看了。
  2. 中位追蹤時間有沒有給,而且是怎麼算的。 有些論文寫「中位追蹤 X 個月(IQR …)」但實際上是觀察時間的中位數;如果這個數字明顯小於中位存活時間,通常就是算錯了。
  3. 失去追蹤多少人、為什麼。 CONSORT/STROBE 流程圖要看得到。比例高又沒交代原因,非資訊性設限的假設就是懸空的。
  4. 分組依據是不是基線就知道。 只要看到「反應組 vs 無反應組」「完成療程 vs 未完成」「接受手術 vs 未接受」這種標題,先假設有 immortal time bias,再去 Methods 找他們有沒有用 landmark 或時間相依變項處理。

常見誤用

誤用為什麼錯
把設限的人當作「沒發生事件」直接算比例低估風險;他們可能在你沒看到的時候發生了事件
把設限的人整個排除通常高估風險,剩下的都是已發生事件的人,且破壞了隨機化
用觀察時間的中位數當作中位追蹤時間死亡者的觀察時間是存活時間不是追蹤時間;正解是反轉 KM
依基線之後才知道的狀態分組(反應/完成療程/接受手術)Immortal time bias,效果會被系統性地誇大
從診斷日開始計算追蹤,卻用「首次領藥」定義暴露組同上,藥物流行病學最常見的版本
把定期檢查發現的日期直接當成精確事件時間,且不提檢查間隔實際是區間設限,事件時間被系統性地往後推;兩組檢查頻率不同時還會製造組間差異
失去追蹤比例高卻不做敏感度分析非資訊性設限假設無法檢定,只能靠敏感度分析畫出結論的可能範圍
KM 圖不附風險人數表讀者無法判斷曲線尾端是幾個人畫出來的
用「失去追蹤的人與留下來的人基線特徵相似」證明設限是非資訊性的基線相似不代表設限那一刻的預後相似;這個假設關心的是後者

重跑本頁的所有數字

/opt/homebrew/bin/Rscript figures/scripts/B3-01-censoring.R

讀讀看這張圖

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

報表同時印了好幾個以天為單位的數字。要回答「這份研究到底看了多久」,該讀哪一個?

看答案與解析

正確答案: 588.0 天——把設限與事件互換之後算出來的追蹤中位數

追蹤中位數問的是「還在追蹤中的人被看了多久」,所以要把設限當成事件、事件當成設限再跑一次 KM,得到 588.0 天。255.5 天是觀察時間的中位數:一個追蹤第三天就失去聯絡的人,在這個算法裡只貢獻三天,於是研究看起來比實際短很多——一份追蹤其實不夠久的研究,會因此被誤讀成追蹤太短。而 511.0 天只是 588.0 那個估計的信賴區間下界,換一批人就會變,它不是任何人被追蹤的時間。

同一份心臟移植資料,把「有沒有接受移植」當成固定分組跑一次,當成時間相依變項再跑一次。哪一個結果可以拿來說移植的效果?

看答案與解析

正確答案: 風險比 1.14,來自時間相依——每個人在手術前算未移植、手術後才算移植

固定分組給的 0.27 看起來像移植讓死亡風險降到四分之一,但那段等待器官的時間裡病人依定義不會死,而它被算給了移植組——這就是 immortal time bias。0.43 是同一個有偏估計的信賴區間上界,一樣繼承了那個偏差。把同一個變項改成隨時間變動之後得到 1.14,它的 95% 信賴區間同時涵蓋可觀的益處與可觀的害處,未達統計顯著;這份資料既不足以支持移植改變死亡風險,也不足以排除。

有人看著報表上被設限的人數說「所以有這麼多人活下來了」。下面哪一個說法才站得住?

看答案與解析

正確答案: 被設限的是 63 人,而設限只代表從那個時間點之後我們不知道發生了什麼

被設限的意思只有一個:從這個時間點之後,我們不知道發生了什麼。63 人裡既有追蹤結束時仍然存活的人,也有中途失去聯絡、甚至可能已經過世卻沒被記錄的人,所以不能整批當成存活者。165 是確實觀察到的死亡數,但「其餘的都還活著」正是同一個誤讀換句話說;228 減去 165 得到的也只是被設限的人數,不是存活人數。設限比例高代表資訊少,不代表預後好。

延伸觀看

Censoring and Truncation [Survival Analysis 2/8]
ENzedstatistics· 14 min本頁的最佳搭配。把 censoring 與 truncation 的差別講得比多數教科書清楚,讀完本頁第二節再看會更有感。
Survival Analysis [Simply Explained]
ENnumiqo· 13 min十三分鐘的整體導覽,適合完全沒接觸過存活分析的人先看,建立整張地圖再進來看細節。
存活分析(Survival Analysis)
繁中Ming-Chieh Shih· 16 min繁中,從資料結構講起,中文術語對照可以直接拿來用。
【Lecture】L19 Survival Analysis (1)
繁中MeDA(臺大公衛洪弘教授)· 44 min繁中研究所等級的完整講授,想把設限的數學基礎補起來看這一集。

素材來源與授權

本頁為原創內容

回報內容問題

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

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

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

一併送出的資訊

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