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

2×2 表、敏感度與特異度

四個格子與四個邊際各自回答什麼問題、為什麼敏感度與特異度是檢驗的性質而不是族群的性質、SpPIN / SnNOUT 這組助記在什麼時候會騙人,以及診斷研究最常見的偏誤——只有檢驗陽性的人去做 gold standard 時,敏感度與特異度會同時往相反方向跑。

這一頁在解決什麼問題

臨床上大部分的檢驗最後都會被壓成一個二元判斷:這個結果算陽性還是陰性。抽血值過了某個門檻、影像被判讀成有或沒有、快篩出現第二條線。

問題是,「這個檢驗準不準」這句話沒有單一答案。它至少要拆成四個不同的問題,而這四個問題的答案在同一份資料上可以差非常多:

  • 有病的人裡,檢驗抓到幾成?
  • 沒病的人裡,檢驗放行幾成?
  • 檢驗說陽性的人裡,真的有病的佔幾成?
  • 檢驗說陰性的人裡,真的沒病的佔幾成?

前兩個問題的答案是檢驗的性質,後兩個不是。 這一頁處理前兩個(敏感度與特異度),後兩個在預測值與盛行率。把這件事分清楚,是診斷準確度整個家族的地基。

四個格子與四個邊際

把「檢驗結果」與「參考標準(reference standard,也就是 gold standard)說的真相」交叉,就是 2×2 表:

真的有病真的沒病合計
檢驗陽性真陽性 TP偽陽性 FP所有陽性者
檢驗陰性偽陰性 FN真陰性 TN所有陰性者
合計所有病人所有非病人總人數

四個格子加起來是總人數。真正要記的是除的時候是往哪個方向除

Sensitivity=TPTP+FN,Specificity=TNTN+FP\text{Sensitivity} = \frac{TP}{TP + FN}, \qquad \text{Specificity} = \frac{TN}{TN + FP} PPV=TPTP+FP,NPV=TNTN+FN\text{PPV} = \frac{TP}{TP + FP}, \qquad \text{NPV} = \frac{TN}{TN + FN}

敏感度與特異度是沿著直行除(分母是疾病狀態的人數),預測值是沿著橫列除(分母是檢驗結果的人數)。

這一頁的例子

pROC::aSAH 是 113 位蜘蛛膜下腔出血(subarachnoid haemorrhage, SAH)病人的資料, 記錄了兩個生物標記與六個月後的神經學結果。把結果二分成好(Good)與差(Poor): 41 人結果差、72 人結果好,也就是這個世代的盛行率是 36.3%。

這一頁用 S100β(一種星狀膠細胞的蛋白質,腦損傷時會上升)當作檢驗,切點訂在 0.20 µg/L。

點圖,橫軸是 S100β 濃度取對數,上排是結果好的病人、下排是結果差的病人,虛線標出切點;四個象限分別標示 TN、FP、FN、TP 的人數。
每一個點是一位病人。垂直虛線就是切點,它把每一排各切成兩塊,於是產生 2×2 表的四個格子。移動這條線,四個數字全部一起動——這正是 ROC 曲線在畫的東西。產圖腳本 figures/scripts/B4-01-sens-spec.R
結果差(有病)結果好(沒病)合計
S100β ≥ 0.20TP = 26FP = 1440
S100β < 0.20FN = 15TN = 5873
合計4172113

四個指標與它們的 95% 信賴區間(比例的區間用 Wilson score interval,它在接近 0 或 1 時不會跑出 [0, 1] 之外):

指標算式估計值95% CI
敏感度 Sensitivity26 / 4163.4%48.1–76.4%
特異度 Specificity58 / 7280.6%70.0–88.0%
陽性預測值 PPV26 / 4065.0%49.5–77.9%
陰性預測值 NPV58 / 7379.5%68.8–87.1%

信賴區間值得多看一眼:敏感度的區間是 48.1–76.4%,寬達 28.3 個百分點。診斷準確度研究的樣本數常常不足,因為它的有效樣本數是「有病的人數」與「沒病的人數」分開算的,而不是總人數;這裡的敏感度只由 41 個人決定。看到一篇診斷研究報 sensitivity 而不報區間,先假設它很寬。

動手跑一次

library(pROC)
data(aSAH, package = "pROC")

cut <- 0.20                       # 先訂好的切點,不是挑出來的
pos <- aSAH$s100b >= cut
dis <- aSAH$outcome == "Poor"

tab <- table(factor(pos, c(TRUE, FALSE)), factor(dis, c(TRUE, FALSE)))
tab                                # 左上角就是 TP

tp <- tab[1, 1]; fp <- tab[1, 2]; fn <- tab[2, 1]; tn <- tab[2, 2]
sens <- tp / (tp + fn)
spec <- tn / (tn + fp)
c(sensitivity = sens, specificity = spec,
  ppv = tp / (tp + fp), npv = tn / (tn + fn))

# 比例的信賴區間:Wilson,不要用 sens ± 1.96*SE(在接近 0/1 時會出界)
prop.test(tp, tp + fn, correct = FALSE)$conf.int
binom.test(tp, tp + fn)$conf.int   # Clopper-Pearson,保守但永遠涵蓋

# 匯出給 Python 用
write.csv(aSAH, "aSAH.csv", row.names = FALSE)

驗證環境:R 4.6.0 + pROC 1.19.0.1。aSAH 隨 pROC 一起安裝,不需要另外下載。

敏感度與特異度不隨盛行率變——這句話的意思是什麼

課本說敏感度與特異度是「檢驗的性質」。要看清楚這句話,最乾脆的辦法是改變病人組成再算一次

下面三個世代都是從同一批 113 位病人來的,做法是把某一類的人整批複製(不是隨機抽樣——複製才能讓數字完全乾淨,不受抽樣變異干擾):

病例 : 對照的複製比總人數盛行率敏感度特異度PPVNPV
1:432912.5%63.4%80.6%31.7%93.9%
1:111336.3%63.4%80.6%65.0%79.5%
4:123669.5%63.4%80.6%88.1%49.2%

盛行率從 12.5% 走到 69.5%,敏感度與特異度一格都沒動,而 PPV 從 31.7% 跳到 88.1%。

SpPIN 與 SnNOUT,以及它們什麼時候會騙人

臨床上流傳的助記是:

  • SpPINSp(specificity)高的檢驗,Positive 結果可以rule IN(確立診斷)
  • SnNOUTSn(sensitivity)高的檢驗,Negative 結果可以rule OUT(排除診斷)

道理是對的:特異度高表示沒病的人幾乎不會被判成陽性,所以真的出現陽性,多半不是誤判。把同一個標記換三個切點,就能看到這組助記的兩端:

切點(µg/L)敏感度(95% CI)特異度(95% CI)TP / FP / FN / TNLR+LR−
0.0597.6%(87.4–99.6)6.9%(3.0–15.2)40 / 67 / 1 / 51.050.35
0.2063.4%(48.1–76.4)80.6%(70.0–88.0)26 / 14 / 15 / 583.260.45
0.6022.0%(12.0–36.7)100.0%(94.9–100.0)9 / 0 / 32 / 72無法估計0.78

最低的那個切點敏感度 97.6%,看起來是漂亮的 SnNOUT;最高的那個切點特異度 100.0%(偽陽性一個都沒有),看起來是完美的 SpPIN。兩個都有問題:

驗證性偏誤:診斷研究最常見的坑

到目前為止,本頁預設每一位病人都做了參考標準。真實的診斷研究常常不是這樣:參考標準往往是侵入性的、貴的、或有風險的(切片、血管攝影、開刀、長期追蹤),於是實務上只有檢驗陽性的人會被送去做,陰性的人就回家了。

這叫部分驗證偏誤(partial verification bias),也叫 work-up bias。後果不是「數字有點抖」,是方向固定的系統性偏誤

把本頁的資料當成完整驗證的真相,然後假設只有一部分檢驗陰性者被送去做參考標準,剩下的人從分析中消失:

檢驗陰性者被驗證的比例進入分析的人數算出來的敏感度算出來的特異度
100%11363.4%80.6%
70%9171.2%74.4%
50%7777.6%67.4%
30%6285.2%55.4%
10%4794.5%29.3%
折線圖,橫軸是檢驗陰性者接受參考標準的比例(由左往右遞減,左端是完整驗證),縱軸是算出來的敏感度與特異度;隨著驗證比例下降,敏感度往上跑、特異度往下掉,兩條虛線是真值。
部分驗證偏誤的方向是固定的:檢驗陰性的人愈少被驗證,敏感度愈被高估、特異度愈被低估。橫軸左端是完整驗證(也就是真值)。產圖腳本 figures/scripts/B4-01-sens-spec.R

方向可以用算式看出來,不必背:偽陰性(FN)只出現在檢驗陰性的人裡,所以少驗證陰性者 = 少發現 FN = 敏感度的分母縮水 → 敏感度被高估;真陰性(TN)同樣只出現在陰性者裡,少驗證 = 少發現 TN,而偽陽性(FP)一個不漏地全在 → 特異度被低估

當陰性者只有 10% 被驗證時,敏感度從真值 63.4% 被推到 94.5%,特異度從 80.6% 掉到 29.3%。一篇報告「敏感度超過九成」的診斷研究,如果沒交代陰性者怎麼被驗證,這個數字本身不能拿來用。

常見誤用

誤用為什麼錯
把敏感度與 PPV 講成同一件事一個沿著疾病狀態的行除、一個沿著檢驗結果的列除,分母完全不同
報敏感度不報信賴區間有效樣本數是有病者人數,通常遠小於總人數,區間往往很寬
在特異度接近 1、偽陽性為 0 時宣稱「完全不會誤判」零偽陽性只保證上界,區間下界仍可能明顯低於 1;此時 LR+ 也無法估計
只憑高敏感度就說陰性可以排除診斷要看的是 LR−;敏感度高但特異度極低時 LR− 仍可能接近 1
把文獻的敏感度直接套到自己的病人身上病情組成不同會改變測到的敏感度(光譜效應),這是要檢查的假設
只驗證檢驗陽性者卻照常報敏感度與特異度部分驗證偏誤會同時高估敏感度、低估特異度
陽性與陰性用不同的參考標準卻合併分析差異驗證偏誤,兩種標準定義的疾病不是同一件事
用整體正確率(accuracy)當主要指標在盛行率低時,全部判陰性就能得到很高的正確率
自己從資料挑一個最好看的切點再報它的敏感度樂觀偏誤,見 切點選擇與兩條 ROC 的比較
把連續的檢驗值二分之後就不提切點沒有切點,敏感度與特異度沒有定義

重跑本頁的所有數字

/opt/homebrew/bin/Rscript figures/scripts/B4-01-sens-spec.R

讀讀看這張圖

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

113 位病人裡有 41 位結果差。在切點 0.20 上,敏感度是 0.634、特異度是 0.806。一位讀者說「所以來做這個檢驗的人有六成三會被正確判讀」。哪一個說法對?

看答案與解析

正確答案: 不對。整批人被正確判讀的比例是 0.743,敏感度的分母只有結果差的那 41 位

敏感度的分母是那 41 位結果差的病人,不是 113 位受檢者——它沿著直行除。整批人被正確判讀的比例是 0.743,比 0.634 高,因為特異度那一側的人比較多、也被判得比較準。0.650 則是陽性預測值,沿著橫列除,回答的是「一張陽性報告代表什麼」,不是「有多少人被判對」。三個數字都在同一張表上,差別只在分母算誰;把敏感度讀成整體正確率,等於把一個沿著直行除的量當成整張表的摘要。

把同一批人的對照組整批複製,世代盛行率從 36.3% 降到 12.5%,四個指標再算一次。這張表在示範什麼?

看答案與解析

正確答案: 盛行率最低那一列的 PPV 掉到 0.317,而敏感度與特異度一格都沒動

複製對照組只改變了兩群人的相對大小,沒有改變任何一個人的檢驗結果。敏感度在病例那一群裡算、特異度在非病例那一群裡算,所以整張表上它們一格都沒動;PPV 與 NPV 的分母是檢驗陽性者與檢驗陰性者,混了兩群人,於是跟著比例移動——0.317 與 0.881 是同一個檢驗在兩個世代的 PPV。至於 0.939 這個 NPV,高是因為那個世代大多數人本來就沒病,不是因為檢驗變強了:不做檢驗、看到誰都說沒病,正確率也差不多。

切點 0.60 那一列,72 位結果好的病人裡一個偽陽性都沒有,特異度的點估計是 1.000。可以說這個切點不會誤判沒病的人嗎?

看答案與解析

正確答案: 不能。95% 信賴區間的下界是 0.949,與這份資料相容的偽陽性率仍可到五個百分點左右

零個偽陽性只是把點估計頂到上界,它不保證真值就在那裡:72 個人裡零個事件,對應的下界是 0.949,也就是與這份資料相容的偽陽性率最高仍有五個百分點左右。而且這個切點的代價並不小——敏感度只剩 0.220,多數結果差的病人會被判成陰性;同時 LR+ 的分母是零,根本估不出來。把「這份樣本沒看到誤判」讀成「這個檢驗不會誤判」,是把區間讀成保證。

切點 0.05 的敏感度是 0.976,看起來是漂亮的 SnNOUT。這個切點的陰性結果真的能排除疾病嗎?

看答案與解析

正確答案: 不能。同一列的特異度只有 0.069,這個切點幾乎把所有人都判成陽性,陰性之所以少見,是因為它幾乎不放過任何人

SnNOUT 的成立條件不是敏感度單獨高,而是陰性結果本身帶有訊息。這個切點的特異度只有 0.069,也就是結果好的人裡九成以上同樣被判成陽性——敏感度 0.976 是靠「幾乎不放過任何人」換來的。衡量陰性結果價值的量是 LR−,這裡是 0.351:它把疾病的勝算降到大約三分之一,是有用的一步,但離排除還很遠。要把陰性當成排除,通常要求 LR− 再小一個數量級。

一份診斷研究只把部分檢驗陰性者送去做參考標準。用本頁的模擬,當陰性者只有一成被驗證時,算出來的敏感度是 0.945、真值是 0.634。特異度會往哪個方向跑?

看答案與解析

正確答案: 特異度掉到 0.293——少驗證陰性者會少發現真陰性,而偽陽性一個都不會漏掉

偽陰性只出現在檢驗陰性的人裡,少驗證陰性者等於少發現偽陰性,敏感度的分母縮水,於是被高估到 0.945。真陰性同樣只出現在陰性者裡,跟著一起消失;而偽陽性全在陽性那一側,一個都不會漏掉——特異度的分子縮水、分母裡的偽陽性不動,於是被低估到 0.293。兩個指標往相反方向跑,這是固定的方向,不是隨機的抖動。0.806 是完整驗證時的特異度真值,0.776 則是另一個驗證比例下的敏感度,不是特異度。所以一篇沒交代陰性者怎麼被驗證的診斷研究,它報的敏感度不能照面值用。

敏感度是 0.634,95% 信賴區間 0.481 到 0.764,比同一張表上特異度的區間明顯寬。為什麼?

看答案與解析

正確答案: 因為敏感度只由 41 位結果差的病人決定,而特異度的分母是另外那 72 位結果好的人

診斷準確度研究的有效樣本數要分兩邊算:敏感度的分母是 41 人,特異度的分母是 72 人,兩個區間的寬度各自由這兩個數字決定,而不是由 113 這個總人數決定。40 是檢驗陽性的人數,它是 PPV 的分母,不是敏感度的。所以看到一篇診斷研究只報總收案數、不分開報兩群的人數,先假設敏感度那一側的區間很寬。

延伸觀看

Sensitivity and specificity – explained in 3 minutes
ENGlobal Health with Greg Martin· 3 min三分鐘建立 2×2 的心像。讀第二節之前先看這支,後面的定義會變成「本來就該這樣」。
Calculating Sensitivity and Specificity using a 2x2 table
ENClinical Information Sciences· 2 min一分四十秒,把概念變成會算的動作,補上前一支只講概念沒算數的缺口。
醫學統計 EP17 敏感度、特異度與預測值
繁中EDMAN MURMURS· 9 min繁中、臨床醫師視角,術語的中文說法在這裡有對照。
Machine Learning Fundamentals: Sensitivity and Specificity
ENStatQuest with Josh Starmer· 12 min同一組定義從機器學習的角度講一次,之後讀 recall / precision 的文獻不會卡住。
Principles of Epidemiology 10. Diagnosis, Tests, and Screening
繁中臺大開放式課程 NTU OCW· 60 min繁中完整課程。本頁最後一節談的驗證性偏誤,在這裡有更完整的流行病學脈絡。

素材來源與授權

本頁為原創內容

回報內容問題

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

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

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

一併送出的資訊

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