選擇偏誤
選擇偏誤不是「樣本不夠有代表性」這麼含糊的一句話,而是一條可以寫下來的規則:誰進到分析裡。本頁用同一個世代跑四種不同的失訪規則(外加一列全員基準),證明其中兩種完全不傷估計值、一種只傷相對風險不傷勝算比、一種把兩者都毀掉——以及為什麼校正共變項救不回來。
它跟干擾不是同一件事,所以校正救不回來
干擾(confounding)是被比較的兩群人本來就不一樣,而那個「不一樣」同時影響暴露與結果。 它發生在你收到的資料裡面,所以只要那個變項有被量到,把它放進模型就能處理。
選擇偏誤是誰進到這份資料這件事本身就取決於暴露或結果。它發生在資料產生之前, 你手上的每一列都已經是被篩過的。把年齡、性別、共病全部放進模型,改變不了「沒被選進來的人不在這裡」。
醫院裡的兩個病,明明無關卻看起來互斥
先看一個乾淨到不可能被誤讀的例子。母體有兩個病, 盛行率分別是 8% 與 12%, 在程式碼裡是各自獨立抽的,所以真實的勝算比就是 1。 兩者都不會影響對方,也沒有任何共同原因。
唯一發生的事情是:得到其中任何一個病,住院的機率都會上升。 兩個病都沒有的人,住院機率是 2.0%; 只有 A 病是 36.3%,只有 B 病是 31.4%, 兩個都有是 55.4%。最後那一格是整件事的引擎: 同時有兩個病的人最容易被收進來,於是住院族群裡「兩個都有」被相對放大, 而「一個都沒有」幾乎被清空——剩下的多半是只有其中一個。
| 分析的對象 | 人數 | A 與 B 的勝算比 |
|---|---|---|
| 整個母體 | 400,000 | 0.990 |
| 只看住院的人 | 32,317 | 0.097 |
在住院的人裡面,兩個病看起來強烈互斥。這就是 Berkson 偏誤(Berkson’s bias,又稱入院率偏誤)。 機制不神秘:住院的人多半至少有一個病,所以在這群人裡「沒有 A 病」幾乎就意味著「有 B 病」。 關聯是被選擇這個動作創造出來的,不是母體裡本來就有的。
figures/scripts/B8-03-selection-bias.R左圖那條曲線值得多看一眼:它是連續的。醫院不是「有偏誤 / 沒偏誤」的開關, 而是越專門、收治門檻越高,被創造出來的關聯就越強。 最左端那個 0.5% 的住院率,把真值為 1 的勝算比壓到 0.025;最右端所有人都「住院」時,估計值回到 0.990——那一格就是整個母體。
四種「誰沒進到分析」的規則,代價完全不同
Berkson 是極端案例。更常見的問題是世代研究的失訪:有些人沒有追蹤到底。 下面用同一個世代(400,000 人,暴露盛行率 40%)跑五次。真實相對風險是 1.5 (未暴露風險 10%),真實勝算比是 1.588。
第一列是全員分析的基準,第二列是與暴露、結果都無關的隨機流失(留存率一律 35%)。第三到第五列才是要互相比較的三列:它們的留存機率一律在 90% 與 20% 之間二選一,唯一的差別是依什麼決定。
| 誰留下來 | 分析人數 | 未暴露風險 | 暴露風險 | 相對風險 | 勝算比 |
|---|---|---|---|---|---|
| 全員分析(基準) | 400,000 | 9.9% | 15.1% | 1.523 | 1.616 |
| 與暴露、結果都無關的流失 | 139,036 | 9.9% | 15.1% | 1.526 | 1.620 |
| 只取決於暴露的流失 | 192,103 | 9.9% | 15.0% | 1.516 | 1.608 |
| 只取決於結果的流失 | 114,310 | 32.9% | 44.1% | 1.341 | 1.610 |
| 同時取決於暴露與結果的流失 | 97,037 | 9.8% | 44.3% | 4.518 | 7.318 |
比較的基準是第一列,不是理論真值:這是一次抽樣,第一列的 1.523 與 1.616 就是這份資料在完全沒有流失下能得到的答案, 它與理論值的差距是模擬誤差的尺度。逐列讀,這張表說了四件不一樣的事:
- 與暴露、結果都無關的流失(第二列)——即使丟掉超過六成的人,兩個估計值都還在真值上。 損失的是精確度(信賴區間變寬),不是正確性。
- 只取決於暴露的流失(第三列)——一樣沒有偏誤。這一列常被誤讀: 「暴露組的追蹤率比較高」聽起來很嚴重,但只要在每一個暴露層裡,留下來的人與離開的人的風險一樣, 層內的風險就沒有被動到,而相對風險是層內風險的比值。
- 只取決於結果的流失(第四列)——相對風險掉到 1.341, 但勝算比是 1.610,幾乎沒有動。下一節專講這件事。
- 同時取決於暴露與結果的流失(第五列)——相對風險 4.52、 勝算比 7.32,相對真值分別高估 3.0 倍與 4.6 倍。
為什麼病例對照研究報勝算比,不報相對風險
上一節第四列不是巧合,是一個代數事實:依結果抽樣,會把每一個暴露層的勝算乘上同一個常數, 所以兩層的比值不變。風險就沒有這個性質——風險有上限 1,乘上一個常數會被壓縮。
病例對照研究做的正是「依結果抽樣」:先找到病例,再找對照。所以它天生就是第四列那個情境, 而它報勝算比不報相對風險,不是慣例或偏好,是只有勝算比在那個抽樣設計下還估得到。
知道選擇機率就能加權回去——而知道它才是難的地方
最後一列那個被毀掉的估計(下表第一列,與上表第五列是同一次抽樣、同一批人), 是可以救的:如果你知道每個人被留下來的機率, 就用它的倒數當權重(inverse probability of selection weighting),把留下來的人放大回他們代表的那群人。
| 相對風險 | 勝算比 | |
|---|---|---|
| 直接分析留下來的人 | 4.518 | 7.318 |
| 以選擇機率的倒數加權 | 1.532 | 1.626 |
| 真值 | 1.500 | 1.588 |
加權之後兩個估計值都回到真值附近。但這個示範有一個作弊的地方,而且那正是重點: 在模擬裡,選擇機率是程式碼寫死的,我知道它精確到小數點以下每一位。
真實研究裡沒有人知道那個機率。你只能用「還在的人」與「離開的人」身上有記錄到的變項去建一個模型預測它, 而如果離開的原因裡有任何一部分是沒被記錄到的(病情惡化、搬家、死亡未被通報), 那個模型就系統性地錯,加權只是把錯誤搬個位置。
臨床研究裡最常見的幾種,以及它們是哪一列
| 名稱 | 誰被篩掉 | 對應上表哪一列 |
|---|---|---|
| Berkson/入院率偏誤 | 只看住院者,而兩個病都提高住院機率 | 比第五列更極端:選擇同時取決於「暴露」與「結果」 |
| 健康工作者效應 | 在職者比一般人健康,而能不能在職與暴露有關 | 第五列 |
| 盛行使用者偏誤 | 只納入「目前正在用藥」的人,早期停藥與早期死亡的人被排除 | 第五列 |
| 志願者/應答偏誤 | 願意參加或回覆問卷的人,其暴露與結果都與不回覆者不同 | 第五列 |
| 差別失訪 | 暴露組裡出事的人特別容易失聯 | 第五列 |
| 依結果抽樣(病例對照) | 刻意依結果抽 | 第四列——這是設計,不是偏誤,只要報勝算比 |
| 依暴露抽樣(暴露世代) | 刻意依暴露抽 | 第三列——同樣是設計 |
讀論文時看哪裡
- 流程圖的每一個框都要有數字與理由。 CONSORT 或 STROBE 的流程圖如果只寫「排除 n 人」, 沒寫排除的原因與各原因的人數,讀者無法判斷這是第三列還是第五列。
- 失訪要分組報。 總失訪率藏得住差別失訪;暴露組與對照組各自的失訪率藏不住。
- 失訪者的基線特徵。 好的論文會附一張「留下來的人 vs 離開的人」的比較表。 沒有這張表,就只能相信作者說的。
- 納入條件裡有沒有「目前正在用」「能夠回診」「同意參加」這類詞。 這些詞每一個都定義了一條選擇規則。
- 對照組是從哪裡來的。 醫院對照、鄰居對照、母體登錄對照,三者的偏誤結構完全不同。
動手跑一次
set.seed(20260823)
n <- 200000
# A cohort with a known truth: risk 0.10 unexposed, risk ratio 1.5
E <- rbinom(n, 1, 0.4)
Y <- rbinom(n, 1, ifelse(E == 1, 0.15, 0.10))
rr <- function(e, y) mean(y[e == 1]) / mean(y[e == 0])
rr(E, Y) # the truth, computed on everybody
# Selection that depends on the exposure ONLY — still unbiased
keep <- rbinom(n, 1, ifelse(E == 1, 0.9, 0.2))
rr(E[keep == 1], Y[keep == 1])
# Selection that depends on BOTH — this is the one that breaks
keep2 <- rbinom(n, 1, ifelse(E == 1 & Y == 1, 0.9, 0.2))
rr(E[keep2 == 1], Y[keep2 == 1])
# Weight back by the inverse of the (here: known) selection probability
p <- ifelse(E == 1 & Y == 1, 0.9, 0.2)[keep2 == 1]
Es <- E[keep2 == 1]; Ys <- Y[keep2 == 1]; w <- 1 / p
wrisk <- function(g) sum(w[Es == g] * Ys[Es == g]) / sum(w[Es == g])
wrisk(1) / wrisk(0)驗證環境:R 4.6.0。本頁所有數字都是模擬產生的,seed 固定為 20260823。
import numpy as np
rng = np.random.default_rng(20260823)
n = 200_000
E = rng.binomial(1, 0.4, n)
Y = rng.binomial(1, np.where(E == 1, 0.15, 0.10))
def rr(e, y):
return y[e == 1].mean() / y[e == 0].mean()
print(rr(E, Y)) # truth
keep = rng.binomial(1, np.where(E == 1, 0.9, 0.2))
print(rr(E[keep == 1], Y[keep == 1])) # exposure-only: unbiased
keep2 = rng.binomial(1, np.where((E == 1) & (Y == 1), 0.9, 0.2))
print(rr(E[keep2 == 1], Y[keep2 == 1])) # both: brokennumpy 就夠了;重點不在套件,在於你要先寫得出真值,才有東西可以比。
常見誤用
| 誤用 | 為什麼錯 |
|---|---|
| 用多變項模型「校正選擇偏誤」 | 校正處理的是資料裡的干擾;沒進到資料的人不在模型裡 |
| 看到失訪率高就判定有偏誤 | 與暴露、結果都無關的流失只損失精確度 |
| 看到失訪率低就判定沒問題 | 少量但高度差別的失訪一樣可以翻轉結論 |
| 只報總失訪率 | 差別失訪只在分組的數字裡看得見 |
| 病例對照研究報相對風險 | 依結果抽樣後風險已經不是母體的風險,只有勝算比還估得到 |
| 用醫院對照卻不討論對照疾病與暴露的關係 | 這正是 Berkson 的入口 |
| 只納入「目前正在用藥」的人並稱之為世代研究 | 盛行使用者偏誤;早期停藥與早期事件被系統性排除 |
| 報了 IPW 校正卻不說權重模型的內容 | 校正的可信度完全來自那個模型的假設,不寫等於沒報 |
重跑本頁的所有數字
/opt/homebrew/bin/Rscript figures/scripts/B8-03-selection-bias.R讀讀看這張圖
答案取自產生本頁圖表的同一份統計輸出,不是另外打上去的。
母體裡 A 病與 B 病是各自獨立抽的,所以真實的勝算比就是 1,而整個母體算出來是 0.990。只看住院的人,勝算比變成多少,那代表什麼?
看答案與解析
正確答案: 0.097。在住院這群人裡兩個病看起來強烈互斥,而那個關聯是「只看住院者」這個動作創造出來的
0.097 是住院族群的勝算比,而母體的 0.990 已經告訴我們真實關聯是沒有的。差距不是抽樣誤差,是選擇本身:住院的人多半至少有一個病,所以在這群人裡「沒有 A 病」幾乎就等於「有 B 病」。0.784 與 0.025 都不是這批住院資料那一格,兩個都來自左圖那條把收治門檻換成別的值再算一次的曲線;拿其中一格回答「這家醫院會給什麼」,等於換了一家醫院再回答,而 0.784 附帶的「偏誤量級不大」正是這樣得來的。至於生物學上的拮抗,模擬裡兩個病是各自獨立抽的,沒有任何機制可以產生它。把被選擇創造出來的關聯講成疾病之間的關係,是這一頁最想擋掉的誤讀。
同一個世代跑五種流失規則。第三列「只取決於暴露的流失」把暴露組留下 90%、未暴露組只留下 20%,聽起來很嚴重。它的相對風險是多少?
看答案與解析
正確答案: 1.516,與全員分析的基準幾乎一樣。只要在每一個暴露層裡,留下來的人與離開的人風險相同,層內風險就沒被動到
1.516 幾乎就是全員分析的基準,這一列沒有偏誤。相對風險是暴露層內的風險除以未暴露層內的風險,而依暴露決定誰留下來,動到的是每一層有多少人,不是層內的風險——兩層各自被隨機抽稀,比值不變。這也是為什麼依暴露抽樣的世代研究是設計而不是偏誤。4.518 是第五列,那裡的流失同時取決於暴露與結果,兩層的風險本身都被扭曲了,才會炸到那個量級。1.341 是第四列,流失只取決於結果,它壓低的是相對風險而不是勝算比。三列的失訪率都很高,代價卻完全不同——該問的不是掉了幾成,而是掉的規則是什麼。
第四列「只取決於結果的流失」把相對風險壓到 1.341。同一列的勝算比是多少,這件事為什麼重要?
看答案與解析
正確答案: 1.610,幾乎沒有動。依結果抽樣會把每一個暴露層的勝算乘上同一個常數,兩層的比值因此不變——這正是病例對照研究報勝算比的理由
1.610 與全員分析那一列幾乎相同,所以只取決於結果的流失沒有動到勝算比。代數上的原因是它把每一個暴露層的勝算乘上同一個常數,比值於是不變;風險沒有這個性質,因為風險有上限,乘上常數會被壓縮——這就是同一列的相對風險掉到 1.341 的來源。7.318 是第五列,那裡的流失同時取決於暴露與結果,連勝算比都保不住。1.588 是理論真值,而這張表要比的基準是第一列的全員分析,不是理論值:同一次抽樣裡,第一列才是「完全沒有流失時這份資料會給的答案」。病例對照研究做的正是依結果抽樣,所以它報勝算比不報相對風險不是慣例,是只有勝算比在那個設計下還估得到。
第二列與第五列丟掉的比例是同一個量級。哪一個說法對?
看答案與解析
正確答案: 第二列丟掉超過六成,相對風險仍是 1.526,與全員分析一致——失訪率高低本身不是判準,掉的規則才是
1.526 是第二列:丟掉超過六成之後,相對風險仍與全員分析一致,因為與暴露、結果都無關的流失損失的是精確度,不是正確性。4.518 是第五列,它的失訪率與第二列同一個量級,估計值卻被毀掉——所以失訪率高不能當判準,掉的規則是什麼才能。1.523 是第一列的全員分析,把它安到第五列頭上會得出完全相反的結論。實務上該追的問題是:暴露組裡發生結果的人是不是特別容易被追蹤到。論文只報一個總失訪率的話這個問題無法回答,所以分組失訪率與失訪者的基線特徵才是該找的東西。
第五列那個被毀掉的估計,用選擇機率的倒數加權之後,相對風險回到多少?這代表加權法可以在真實研究裡照做嗎?
看答案與解析
正確答案: 回到 1.532,貼著真值。但模擬裡的選擇機率是程式碼寫死的,真實研究只能用有記錄到的變項去估它,估錯了加權只是把錯誤搬個位置
1.532 貼著真值,加權確實把第五列救回來了:它把留下來的人依被留下的機率放大回他們代表的那一群。但這個示範作弊,而那正是重點——選擇機率是模擬寫死的,精確到小數點以下每一位。真實研究裡沒有人知道它,只能用還在的人與離開的人身上有記錄到的變項建模型去估;離開的原因裡只要有一部分沒被記錄到(病情惡化、搬家、死亡未通報),那個模型就系統性地錯。4.518 是未加權的那一列,加權若不動點估計就不叫校正了。1.500 是理論真值,而加權後的估計只是落在它附近,不是等於它;把「回到真值附近」讀成「完全消掉偏誤」,正是加權法最常被過度解讀的地方。加權真正的產出是一組可以被檢驗的假設:權重模型放了什麼、為什麼那些變項夠。
左圖那條曲線的橫軸是「兩個病都沒有的人的住院率」。這個住院率從 0.5% 一路放寬到全體時,勝算比怎麼走?
看答案與解析
正確答案: 從 0.025 一路往上爬回真值附近。醫院不是有偏誤或沒偏誤的開關,收治門檻越高,被創造出來的關聯越強
曲線是連續的:最左端門檻最嚴的那一格把真值為 1 的勝算比壓到 0.025,最右端所有人都算住院時回到 0.990,而那一格就是整個母體。所以偏誤的大小是「醫院有多挑」的函數,不是一個是非題。0.990 是最右端那一格,把它當成整條曲線的樣子等於宣稱選擇不造成偏誤,而左端的 0.025 就在旁邊否定它。0.591 是曲線中段的一格,方向也反了——門檻放寬時被納入的「兩個病都沒有」的人變多,關聯是往真值回,不是被壓得更低。讀醫院為基礎的病例對照研究時,該問的是對照組從哪裡來、他們住院的原因與暴露有沒有關係。
用到這個方法的章節
延伸觀看
Principles of Epidemiology 09. Case-Control Study 2: M-H Methods & Selection Bias
Confounding, chance, and bias素材來源與授權
本頁為原創內容