隨機對照試驗
RCT 的統計章節在做什麼、怎麼一步步讀完、以及教科書很少講但真實試驗一定會遇到的幾件事——期中分析、提前中止、以及那個被「照樣算進去」的病人。
這一章在回答什麼
你翻開一篇隨機對照試驗(randomised controlled trial, RCT),從 CONSORT 流程圖看到 Table 1、看到主要結果的那個 p 值、再看到一排 subgroup 的 forest plot。這一章要回答的是:這些東西各自在防什麼問題,以及哪一格數字如果不對勁,你應該停下來。
我們全程用同一個真實試驗當例子:Elmunzer 等人 2012 年發表在 NEJM 的研究,測試 ERCP 術後塞一顆 indomethacin 栓劑能不能預防胰臟炎。選它有三個理由——它的個案層級資料是公開的(medicaldata 套件裡就有,一行程式碼拿得到)、它的設計裡有教科書很少示範的期中分析與提前中止、而且它剛好有一個非常適合講意向治療分析的真實狀況。
隨機化到底買到了什麼
觀察性研究最難處理的問題是:接受治療的人和沒接受治療的人,本來就不一樣。醫師會把藥開給看起來需要的人,於是「用藥」這件事同時帶著「病情較重」的資訊,兩者混在一起分不開。這叫做依適應症的干擾(confounding by indication)。
隨機分配(randomisation)用一個很粗暴但有效的方式解決它:讓分組完全由隨機數決定,與病人的任何特徵無關。這樣一來,兩組在所有特徵上——包括你想到的、沒想到的、根本測不到的——的期望值都相同。這是隨機化真正的價值:它處理的是未知的干擾因子,而統計校正只能處理你有測量到的那些。
在這個試驗裡,隨機分配是在 ERCP 做完之後才執行的。這個安排乍看奇怪,其實是必要的:納入條件裡有一半是「手術過程中才會知道」的因素(例如插管嘗試超過八次、做了 precut sphincterotomy),手術沒做完根本不知道病人合不合格。
分配隱匿與盲性是兩道不同的防線
「隨機分配」這個詞底下壓著三件不同的事,而它們壞掉的方式不一樣。隨機分配(randomisation)決定誰進哪一組,由機率決定。分配隱匿(allocation concealment)是讓收案的人在把病人納入之前,無法預先知道下一個人會分到哪一組。盲性(blinding)則是在分組之後,讓相關的人不知道分組結果。一個試驗可以只做到第一件、略過後兩件,摘要裡照樣寫「randomised」。
分配隱匿防的是選擇偏誤。 如果收案的人能預先知道下一個分配結果,他就可以據此行動——等一個狀況好一點的病人再納入、替眼前這位病人找到一個「其實不符合條件」的理由、或者把排序調換。這些都不需要惡意;一個相信這個藥有效的醫師,很難完全不把病情較重的病人往治療組帶。而且傷害發生在分配的那一瞬間:兩組從此不再可比,那正是上一節說隨機化已經解決掉的依適應症干擾。在這個試驗裡,隨機分配表是由協調中心集中產生、並依收案中心分層。論文沒有描述每一次的分配是怎麼傳到內視鏡室的,所以隱匿做得多緊,這一頁不能替它斷言;論文說的是隨機化在 ERCP 做完、資格確定之後才執行——那從一開始就拿掉了招募者「預見分配再挑病人」的機會。集中式隨機分配、由藥局控管分配、依序編號的不透光密封信封,是常見的三種機制;只寫「密封信封」而沒有編號與不透光,不算其中之一。
盲性防的是另一件事——分組之後發生什麼。 要一組一組問「誰被蒙在鼓裡」:病人、照護他們的醫療人員、判定與裁決結果的人、以及做分析的人。知道自己分在哪一組的病人,症狀陳述會不一樣;知道分組的臨床人員,給出的其他處置會不一樣(執行偏誤,performance bias);知道分組的結果判讀者,對臨界個案的分類會不一樣(測量偏誤,detection bias)。這個試驗用的是外觀完全相同的安慰劑栓劑,而術後照護與記錄追蹤資料的人員也不知道分組結果。這在這一章特別重要,因為主要結果是「新發生的上腹痛 + 胰臟酵素超過門檻 + 決定讓病人住院」的複合定義——其中兩項都要經過臨床判斷。
第一步:CONSORT 流程圖
CONSORT 流程圖回答一個問題——從「有人被看過一眼」到「有人被算進最終分析」,中間掉了多少人、為什麼掉。
先看一張標準版長什麼樣。下面這張照 CONSORT 的格子畫,但裡面每一個數字都是為了示範結構而構造的,不是這個試驗、也不是任何已發表試驗的:
figures/scripts/D3-consort-subgroup.R四個階段由上而下:Enrolment(評估合格性、排除人數與各項排除理由,然後隨機分派)、Allocation(分到兩組各多少人、各組實際接受與未接受分配處置的人數及理由)、Follow-Up(各組失聯與中止處置的人數)、Analysis(各組納入分析與排除於分析之外的人數及理由)。
Follow-Up 與 Analysis 這兩層的關係值得先記著。示範圖裡治療組有 5 人中止了分配到的處置,他們仍然被算進分析;真正離開分母的是 9 位沒有主要結果資料的人。這就是〈第四步〉的意向治療分析畫成格子的樣子。
它分成上下兩半。下半段——Allocation 那一層以下——可以從個案資料重建:
library(medicaldata)
data(indo_rct, package = "medicaldata")
# 隨機分配後的兩組人數
table(indo_rct$rx)
# 各組的主要結果(post-ERCP pancreatitis)
table(indo_rct$rx, indo_rct$outcome)驗證環境:R 4.6.0 + medicaldata 0.2.0
import statsmodels.api as sm
# medicaldata 是 R 套件,Python 端透過 Rdatasets 取得同一份資料
indo = sm.datasets.get_rdataset("indo_rct", "medicaldata").data
print(indo["rx"].value_counts())
print(indo.groupby(["rx", "outcome"]).size())算出來是:602 人接受隨機分配,295 人分到 indomethacin、307 人分到安慰劑,兩組都完成了追蹤。
上半段拿不到。 也就是示範圖最上面那兩格——「評估合格性」與「排除」以及它底下的理由清單。示範圖在那兩格填了 1,240 與 690,換成本章這個真實試驗,那兩格這一頁填不出來。「篩選了幾人、排除了幾人、各是什麼原因」不在這份資料裡,因為個案層級檔案依定義只包含被隨機分配的人——被篩掉的人從來就不是檔案裡的一列。我們也查了原文的 Methods 與 Results 全文,這些數字在句子裡一次都沒出現,只存在於 Figure 1 那張圖裡面。
唯一的辦法是把圖打開、把數字抄下來。怎麼讀一篇臨床研究論文那一章示範了這個動作,並把抄回來的數字重畫成一張流程圖。抄回來之後會看到一件更麻煩的事:連原文那張 Figure 1 都不是從「評估合格性」開始的——它的最上格是簽了同意書的 799 人,被看過一眼的總人數在這篇論文的任何地方都沒有記載。
第二步:Table 1,以及那個不該出現的 p 值
Table 1 列出兩組的基線特徵。你要檢查的是:兩組看起來夠像嗎? 但檢查的方式不是找 p 值。
在 RCT 裡對 Table 1 做組間檢定是一個常見但錯誤的做法,理由很簡單:虛無假設「兩組來自同一母體」在隨機分配下依定義為真。你已經知道它是真的,再去檢定它不會得到任何新資訊;任何顯著的結果都只是型一錯誤(type I error)。
正確的做法是看標準化平均差(standardised mean difference, SMD)。它不隨樣本數膨脹,慣例上 |SMD| < 0.1 視為平衡。這個工具在傾向分數配對裡也是同一套邏輯,方法頁另有詳述。
第三步:主要結果與效果量
n_indo <- sum(indo_rct$rx == "1_indomethacin")
n_plac <- sum(indo_rct$rx == "0_placebo")
e_indo <- sum(indo_rct$rx == "1_indomethacin" & indo_rct$outcome == "1_yes")
e_plac <- sum(indo_rct$rx == "0_placebo" & indo_rct$outcome == "1_yes")
p_indo <- e_indo / n_indo # 治療組風險
p_plac <- e_plac / n_plac # 對照組風險
arr <- p_plac - p_indo # 絕對風險下降
nnt <- 1 / arr # 益一需治數
rr <- p_indo / p_plac # 相對風險
rrr <- 1 - rr # 相對風險下降
fisher.test(matrix(c(e_indo, n_indo - e_indo,
e_plac, n_plac - e_plac), nrow = 2))Fisher's exact test 是這個試驗的 protocol 事先指定的檢定,不是事後選的。
from scipy.stats import fisher_exact
n_indo = (indo["rx"] == "1_indomethacin").sum()
n_plac = (indo["rx"] == "0_placebo").sum()
e_indo = ((indo["rx"] == "1_indomethacin") & (indo["outcome"] == "1_yes")).sum()
e_plac = ((indo["rx"] == "0_placebo") & (indo["outcome"] == "1_yes")).sum()
p_indo, p_plac = e_indo / n_indo, e_plac / n_plac
arr = p_plac - p_indo
nnt = 1 / arr
rr = p_indo / p_plac
odds_ratio, p_value = fisher_exact([[e_indo, n_indo - e_indo],
[e_plac, n_plac - e_plac]])結果是:
| 指標 | 數值 | 怎麼讀 |
|---|---|---|
| 治療組風險 | 27/295 = 9.2% | 用了藥還是發生胰臟炎的比例 |
| 對照組風險 | 52/307 = 16.9% | 沒用藥的基準風險 |
| 相對風險(RR) | 0.54(95% CI 0.35–0.84) | 風險變成原來的 0.54 倍 |
| 相對風險下降(RRR) | 46% | 聽起來最漂亮的那個數字 |
| 絕對風險下降(ARR) | 7.8 個百分點 | 實際少掉的風險 |
| 益一需治數(NNT) | 13 | 每治療 13 人可預防一例 |
| Fisher’s exact p | 0.0053 | 見下一節,門檻不是 0.05 |
RRR 與 ARR 的落差是這張表最值得停下來的地方。 同一組數字,說成「降低 46%」和說成「降低 7.8 個百分點」,給人的印象天差地遠。相對指標不受基準風險影響,所以看起來永遠比較大;但病人實際拿到多少好處,取決於他本來的風險有多高。基準風險低的族群,同樣的 RRR 換算出來的 ARR 會小到沒有臨床意義。
看到只報 RRR 不報 ARR 的論文或藥廠簡報,這是要警覺的訊號。
第四步:意向治療分析
意向治療分析(intention-to-treat, ITT)的規則是:依照隨機分配的組別分析每一個人,不管他實際上接受了什麼。
這條規則違反直覺——一個沒吃到藥的人,為什麼要算進吃藥組?答案在於,一旦你開始把人移出去,就必須用「隨機分配之後」才知道的資訊來決定移誰。而「有沒有把藥吃下去」本身往往與預後有關(病情重的人比較容易吐掉、比較容易退出),移除他們就等於把隨機化辛苦買來的可比性又還回去了。
這個試驗剛好有一個乾淨的例子。原文寫得很明白:有一位分到 indomethacin 組的病人無法保留栓劑,但仍然被納入 indomethacin 組進行分析。 這就是 ITT 的字面執行。
相對地,遵從方案分析(per-protocol, PP)只算完整照著做的人。它回答的是不同的問題——「在完全遵從的情況下效果如何」——代價是失去隨機化的保護。兩者的常見關係是:
- 優效性試驗(superiority trial):ITT 較保守,不遵從會把兩組往中間拉,效果被低估。所以 ITT 是主要分析。
- 非劣性試驗(non-inferiority trial):反過來,ITT 的稀釋效應會讓兩組看起來更像,反而容易做出「不劣於」的結論。所以非劣性試驗要同時報 ITT 與 PP,兩者結論一致才算數。
第五步:樣本數、期中分析與提前中止
這一節是教科書最少講、但真實試驗一定會遇到的部分。
原始計畫要收 948 人(每組 474 人),依據是:假設對照組風險 10%、治療組降到 5%,雙側 α = 0.05,達到 80% 檢定力(檢定用 Fisher's exact)。
實際上只收了 602 人就停了。原因是資料安全監測委員會(data and safety monitoring board, DSMB)在期中分析看到治療組明顯有利,依照事先訂好的停止規則建議提前中止。
這裡有一個關鍵細節:因為做了 2 次期中分析,最終判定顯著的門檻不是 0.05,而是 0.041。
為什麼?每多看一次資料,就多一次犯型一錯誤的機會;若每次都用 0.05,整體的錯誤率會遠高於 0.05。O'Brien-Fleming 邊界搭配 Lan-DeMets α 消耗函數的作用,就是把總共 0.05 的「預算」分配給每一次分析——期中分析用掉一點(門檻訂得很嚴,難以提前中止),剩下的留給最終分析,於是最終門檻略小於 0.05。
第六步:亞組分析
亞組的 forest plot 通常放在論文最後。讀它的時候要記得三件事:
- 亞組分析的檢定力永遠不足。 整個試驗的樣本數是為了主要結果算的,切成亞組之後每一格都小得多,看不到差異是常態,不代表該亞組沒效。
- 要看的是交互作用檢定(test for interaction),不是各亞組各自的 p 值。 「A 組顯著、B 組不顯著」完全可能只是兩組樣本數不同造成的,這不等於「A 組和 B 組的效果不一樣」。後者需要交互作用檢定來回答。
- 事先指定(pre-specified)與事後(post hoc)要分開看。 事後亞組分析屬於探索性,只能用來產生假說。
這一點在本試驗身上也要用一次。原文的探索性亞組分析裡,同時包含事先指定與事後追加的亞組,論文並未把兩類分開呈現。因此下面那句「各亞組的效果方向一致」只能當成與主要結果相容的支持性觀察,不能把落在 post hoc 格子裡的結果當成確認性證據——那些格子沒有事先的統計計畫保護,型一錯誤率無從控制。
這個試驗的亞組分析未偵測到亞組間的交互作用,包括有沒有放胰管支架、有沒有 sphincter of Oddi dysfunction 的懷疑——各亞組的效果方向一致。這比「只有某個亞組特別有效」的結果可信得多,但要接著記住上面第 1 點:亞組的檢定力本來就不足,「未偵測到交互作用」不等於「各亞組的效果真的相同」。
原文那張亞組 forest plot 不能轉載,但同一份公開資料可以自己重算一張。下面就是重算的結果,6 個亞組變項全部取自 indo_rct 已經有的欄位:
figures/scripts/D3-consort-subgroup.R重算不等於複製原文的亞組表。原文混用事先指定與事後追加的亞組,也沒有交代每一列用的是哪個模型;這張圖能給的只是「同一份資料在一個講清楚的模型下長什麼樣」——各層的相對風險用 Katz 信賴區間,交互作用 p 值用 log 連結的 Poisson 模型加穩健變異數,對交互作用項做 Wald 檢定。
拿它對照上面那三件事。有沒有放胰管支架那一組最能說明第 2 點:沒放支架那一層的點估計是 0.30、放了的是 0.60,差了大約一倍——但效果看起來比較大的那一層(RR 離 1 比較遠的那層),信賴區間反而跨過了無效線(它只有 106 人),另一層沒有跨。只看各層自己的 p 值,會讀成「只有放了支架的人才有效」;而這個變項的交互作用 p 值是 0.29,這份資料並未偵測到兩層的效果不同。六個變項裡最小的交互作用 p 值是 0.23,仍然沒有小到值得據以分族群。
12 個分層的點估計全部落在無效線左側,與主要結果相容;但每一層的信賴區間都比最上面那一列(全體)更寬,其中 6 層跨過無效線。那是第 1 點——檢定力不足,不是「該亞組沒效」。
它與 B7-04 那張統合分析的 forest plot 是兩種不同的圖,只是長得像。那裡的每一列是一個研究,菱形是把多個研究合起來的估計,讀的重點是異質性;這裡的每一列是同一個試驗裡的一個分層,菱形是全體,讀的重點是最右欄那一個交互作用 p 值。讀 RCT 論文時撞到的通常是後者。
常見誤用
| 誤用 | 為什麼錯 |
|---|---|
| 對 Table 1 做組間 p 值檢定 | 虛無假設在隨機化下依定義為真,檢定它得不到資訊 |
| 只報 RRR 不報 ARR 或 NNT | 相對指標與基準風險脫鉤,會誇大實際獲益 |
| 因為病人沒遵從就把他移出主要分析 | 破壞隨機化買到的可比性,且移除本身與預後相關 |
| 非劣性試驗只報 ITT | ITT 的稀釋效應在非劣性情境下偏向「證明不劣」 |
| 把提前中止試驗的效果量當作最佳估計 | 提前中止系統性高估效果 |
| 用亞組各自的 p 值宣稱「某族群才有效」 | 應該用交互作用檢定,且亞組檢定力本來就不足 |
| 未達統計顯著就說「兩組沒有差異」 | 只能說這項研究未偵測到差異;要主張相當需要非劣性設計與事先設定的 margin |
重跑本頁的所有數字
/opt/homebrew/bin/Rscript figures/scripts/D3-rct-indo.R
/opt/homebrew/bin/Rscript figures/scripts/D3-consort-subgroup.R
輸出 figures/out/D3-rct-indo.stats.json 與 figures/out/D3-consort-subgroup.stats.json,本頁引用的每一個數字都從這兩個檔讀出來,沒有一個是手打的。第二個檔裡的 CONSORT 區塊標了 hypothetical: true——那些是示範用的構造值,亞組區塊則是從公開資料實算的。
讀讀看這張圖
答案取自產生本頁圖表的同一份統計輸出,不是另外打上去的。
同一組事件數可以寫成相對風險下降,也可以寫成絕對風險下降。摘要只寫了前者,這對讀者有什麼影響?
看答案與解析
正確答案: 有影響。絕對下降只有 0.078,每一百人少掉不到八個事件,而相對指標與基準風險脫鉤
0.460 與 0.078 是同一組事件數的兩種寫法,但給讀者的印象差很遠:相對指標不受基準風險影響,所以在任何基準風險下都長得一樣大;病人實際拿到多少好處則取決於他本來的風險有多高。基準風險低的族群,同樣的相對下降換算出來的絕對下降會小到沒有臨床意義。相對風險 0.540 同樣是相對量,它也回答不了「我會少掉多少風險」。只報相對風險下降、不報絕對風險下降或益一需治數,是誇大獲益的標準手法。
這個試驗做了兩次期中分析,最終判定顯著的門檻不是雙側 0.05。哪一個說法對?
看答案與解析
正確答案: 最終門檻是 0.041,因為每多看一次資料就多一次犯型一錯誤的機會,總預算被期中分析先用掉了一點
α 消耗函數把總共 0.050 的型一錯誤預算分配給每一次分析:期中那幾次先用掉一點——門檻訂得很嚴,難以提前中止——剩下的留給最終分析,於是最終門檻略小於 0.050,這裡是 0.041。若每一次都用 0.050,整體錯誤率會遠高於 0.050。0.005 是這個試驗實際算出來的 p 值,它是被拿去跟門檻比的那個數,不是門檻本身;把算出來的結果當成門檻,等於讓每一個試驗都自動顯著。
亞組 forest plot 裡,沒放胰管支架那一層的相對風險看起來比放了支架那一層更遠離無效線,而且只有前者的信賴區間跨過無效線。該怎麼讀?
看答案與解析
正確答案: 這個變項的交互作用 p 值是 0.29,這份資料並未偵測到兩層的效果不同
要回答「兩層的效果一不一樣」,看的是交互作用檢定,不是各層各自的區間。0.29 沒有小到值得據以分族群。至於為什麼效果看起來比較大的那一層反而跨過無效線:沒放支架那層只有一百多人,區間自然更寬。0.30 與 0.60 差了大約一倍,但兩個估計都很不精確,這種差距在亞組分析裡是常態。六個變項的交互作用 p 值全都不小,而十二個分層的點估計全部落在無效線左側——那與主要結果相容,不是「某個族群才有效」的證據。
示範用的 CONSORT 流程圖最上面兩格填了評估合格性與排除的人數。本章那個真實試驗的對應兩格,這一頁填不出來。為什麼?
看答案與解析
正確答案: 因為被篩掉的人從來就不是個案層級檔案裡的一列;示範圖能寫出 690 是因為它是構造的
個案層級資料依定義只包含被隨機分派的人,所以流程圖的下半段重建得出來,上半段不行。示範圖能填出 1240 與 690,是因為那兩個數字是為了示範結構而構造的。真實試驗的篩選與排除人數在 Methods 與 Results 全文裡一次都沒出現,只畫在 Figure 1 裡——這代表全文檢索、grep 與語言模型讀摘要都抓不到它。至於 266,那是示範圖裡進入分析的人數,它屬於下半段,本來就重建得出來;而且中止處置的人仍然被算進分析,真正離開分母的是沒有主要結果資料的那幾位。
有一位分到 indomethacin 組的病人無法保留栓劑,原文仍然把他算在 indomethacin 組。這樣做的理由是什麼?
看答案與解析
正確答案: 因為決定移走誰要用隨機分派之後才知道的資訊,而那個資訊與預後有關;治療組的 27 個事件因此以分派組別為準
意向治療的規則是依照隨機分派的組別分析每一個人。理由不是形式主義:能不能把藥留住本身與預後有關——病情重的人比較容易吐掉、比較容易退出——所以任何以「實際接受了什麼」為準的移動,都是在用隨機分派之後才知道的資訊重新分組,把隨機化買到的可比性還回去。把這位病人併進安慰劑組的 52 個事件裡也一樣違規,而且方向更糟:那等於把一個因為狀況不好而失敗的人記到對照組頭上。治療組的 27 個事件就是照分派組別算出來的。第一個說法把意向治療讀成樣本數管理。它不是為了湊人數:就算把這位病人整個刪掉,檢定力的損失也小到不會改變結論。它守住的是可比性——一旦允許依「實際接受了什麼」移動人,被移走的就系統性地是預後較差的那一群,而那個偏誤的大小與樣本數無關,事後多收幾個人也補不回來。相對地,遵從方案分析回答的是另一個問題,在優效性試驗裡它不是主要分析。
這是一個四中心試驗。摘要寫「多中心」,讀者可以據此推論外推性比較好嗎?
看答案與解析
正確答案: 不能。602 位受試者裡有 413 位來自同一家,收案來源並沒有平均分散
四家中心裡,Indiana University 一家就收了 413 位,其餘三家合計不到一半,最少的一家只有個位數。「多中心」這個標籤只告訴你有幾個掛名的地點,沒有告訴你收案量怎麼分布。讀者評估外推性時要問的是納入條件、照護流程與主要收案中心的病人像不像自己要套用的族群。另一個推論也不成立:收案量最少的那幾家,任何中心別的估計都會極不精確,所以不適合拿這份資料事後切中心亞組來宣稱中心效果。
本章用到的統計方法
延伸觀看
Randomized control trial (RCT) explained
Intention-to-treat analysis: What is it and why is it important?
臨床試驗 01. 課程簡介素材來源與授權
- CONSORT 2025 Statement:Updated Guideline for Reporting Randomised TrialsCC BY本章的骨架與各節順序依 CONSORT 2025 的項目編排,文字為原創改寫。
- Elmunzer BJ, et al. A Randomized Trial of Rectal Indomethacin to Prevent Post-ERCP Pancreatitis. N Engl J Med 2012;366:1414-22original本章的樣本數、期中分析、ITT 處理等設計細節引自該文 Methods 與 Results;引用為事實陳述,非文字改作。