進階已經雙重審閱,尚未人工抽查

傾向分數配對

傾向分數把十幾個共變項壓成一個數字之後,配對到底在做什麼、caliper 擋掉的是哪些人、為什麼平衡要看 SMD 不看 p 值、配對後你估的已經不是原本那個母體,以及配對之後的變異數該怎麼算。

這一頁在解決什麼問題

世代研究那一章用的是最直接的作法:把干擾因子一起放進 Cox 模型。這個作法在共變項不多、事件夠多的時候很好用,但它有兩個限制。

第一,它把「誰接受治療」與「誰會發生事件」兩件事混在同一個模型裡估。共變項一多,你其實不知道模型是靠哪些人在支撐那個治療係數——可能兩組在某個共變項上根本沒有重疊,模型仍然會照著線性假設外插出一個數字給你,而且不會警告。

第二,事件數決定你能放幾個變項Cox 模型那頁提過每個變項至少十個事件的經驗法則。這份資料只有 47 個事件,照這個標準最多放四到五個變項——但臨床上值得校正的變項有十幾個。

傾向分數(propensity score)處理的正是這個困境:把十幾個共變項壓成一個數字,於是「校正」這件事的複雜度不再受事件數限制,只受治療組人數限制。

傾向分數是什麼

定義只有一句:在給定共變項的條件下,一個人接受暴露的機率。

e(x)=P(A=1X=x)e(\mathbf{x}) = P(A = 1 \mid \mathbf{X} = \mathbf{x})

實務上用邏輯迴歸估——結果變項是治療,不是臨床結果:

logite(x)=β0+β1x1++βpxp\operatorname{logit} e(\mathbf{x}) = \beta_0 + \beta_1 x_1 + \cdots + \beta_p x_p

它之所以有用,是因為一個不太直觀的性質:如果一組共變項足以去除干擾,那麼光是把這一個分數配平,就足以讓那一整組共變項在兩組間平衡。 換句話說,12 維的平衡問題被降成一維。這叫平衡分數(balancing score)性質。

這一頁的例子

沿用世代研究那一章的同一個世代:接受根除性前列腺切除術、術中輸血的病人,暴露是輸血用紅血球的儲存時間(最長那一組 vs 短/中),結果是生化復發。用同一個世代是刻意的——同一個臨床問題,用兩種方法回答。

資料集共 316 位病人,扣掉共變項有缺失的 29 位之後,進入分析的是 287 位(暴露組 99、對照組 188),其中 47 位發生生化復發。傾向分數模型放了 12 個共變項。

手把手配一次

下面這段是用 base R 從頭寫的:邏輯迴歸算分數、貪婪最近鄰配對、SMD 從定義算。實務上這一切 MatchIt 一行就做完了(本頁後面有那一行),但只看過那一行的人,其實不知道 caliper 到底擋掉了誰。

library(medicaldata); library(survival)
data(blood_storage, package = "medicaldata")
set.seed(20260822)

d <- blood_storage
d$treat <- as.integer(d$RBC.Age.Group == 3)      # 1 = 最長儲存
covs <- c("Age", "AA", "FamHx", "PVol", "TVol", "T.Stage", "bGS",
          "PreopPSA", "PreopTherapy", "Units", "sGS", "AnyAdjTherapy")
cc <- d[complete.cases(d[, c("TimeToRecurrence", "Recurrence", "treat", covs)]), ]

# ── 1. 傾向分數:結果變項是「治療」,不是臨床結果 ──────────────────
ps_fit <- glm(reformulate(covs, "treat"), data = cc, family = binomial())
cc$ps  <- fitted(ps_fit)
cc$lps <- qlogis(cc$ps)          # 在 logit 尺度上配,兩端才不會擠成一團

# ── 2. 貪婪最近鄰配對,1:1、不放回、caliper = 0.2 個 logit 標準差 ──
cal  <- 0.2 * sd(cc$lps)
tr   <- sample(which(cc$treat == 1))   # 隨機順序:貪婪法對順序敏感
pool <- which(cc$treat == 0)
pairs <- list()
for (i in tr) {
  if (!length(pool)) break
  dist <- abs(cc$lps[pool] - cc$lps[i])
  m <- which.min(dist)
  if (dist[m] > cal) next              # caliper 擋下來:這個人配不到,就不配
  pairs[[length(pairs) + 1]] <- c(i, pool[m])
  pool <- pool[-m]                     # 不放回:用掉的對照不再參與
}
matched <- cc[unlist(pairs), ]
matched$pair <- rep(seq_along(pairs), each = 2)

# ── 3. SMD 從定義算,分母固定用「配對前」的合併標準差 ──────────────
smd_denom <- sapply(covs, function(v) {
  x <- cc[[v]]; t <- cc$treat == 1
  sqrt((var(x[t]) + var(x[!t])) / 2)
})
smd <- function(dat) sapply(covs, function(v) {
  x <- dat[[v]]; t <- dat$treat == 1
  (mean(x[t]) - mean(x[!t])) / smd_denom[[v]]
})
round(cbind(before = smd(cc), after = smd(matched)), 3)

# ── 4. 效果估計,變異數認配對組 ────────────────────────────────────
coxph(Surv(TimeToRecurrence, Recurrence) ~ treat + cluster(pair), data = matched)

驗證環境:R 4.6.0 + survival 3.8.6 + medicaldata 0.2.0(配對本身只用 base R)

配出來的結果:91 對、共 182 人,暴露組有 8 位因為 caliper 內找不到夠近的對照而被排除。

重疊:配對之前要先看的那張圖

配對之前應該先問一個比「配得好不好」更前面的問題:兩組的傾向分數有沒有重疊? 沒有重疊的區域,無論用什麼統計方法都不可能有可比較的對照——那裡的估計只能靠模型外插。

兩張背對背直方圖。左圖是配對前傾向分數的分布,暴露組在上、對照組在下,對照組在低分數區有一小群暴露組完全沒有的人;右圖是配對後,兩組分布形狀接近對稱。
傾向分數的分布,暴露組朝上、對照組朝下。左邊是配對前:對照組在最低分數區有一小撮人,暴露組那裡一個人也沒有。右邊是配對後,兩側形狀大致鏡射。產圖腳本 figures/scripts/B6-02-psm.R

這份資料的傾向分數落在 0.125 到 0.613 之間;兩組共同支撐區(common support,兩組都有人的區間)是 0.233 到 0.609。落在共同支撐區外面的人,配對會自動把他們丟掉——這是配對相對於迴歸校正的一個實質優點:迴歸不會告訴你它在外插,配對會直接把配不到的人列出來。

平衡怎麼看:SMD 不是 p 值

配對做完之後唯一該檢查的東西是平衡。判準是標準化平均差(SMD),慣例上 |SMD| < 0.1 算平衡。SMD 的定義、為什麼它不隨樣本數膨脹、以及 0.1 這條線的來歷,在Table 1 與標準化平均差有完整說明,這裡不重複。

這裡只強調一件事:不要對配對前後做組間 p 值檢定。 配對之後樣本數變小,p 值本來就會變大——就算完全沒有改善平衡,「配對後 p 值不顯著」這句話一樣成立。它衡量的是樣本數,不是平衡。

Love plot:12 個共變項的絕對標準化平均差,圓點是配對前、三角是配對後,虛線標在 0.1。多數變項的三角比圓點更靠近零,但有 4 個變項配對後反而變遠。
Love plot。圓點是配對前、三角是配對後,虛線是 0.1 的慣例門檻。多數變項往左移了,但注意有 4 個變項是往右移的——配對改善的是整體,不保證每一格都變好。產圖腳本 figures/scripts/B6-02-psm.R
共變項配對前 SMD配對後 SMD
Age (years)-0.1350.033
African American0.0450.029
Family history0.156-0.026
Prostate volume (g)-0.132-0.002
Tumour volume (grade)-0.104-0.031
T stage-0.019-0.034
Biopsy Gleason score-0.003-0.032
Preoperative PSA0.026-0.015
Preoperative therapy0.011-0.065
Units transfused-0.010-0.067
Surgical Gleason score-0.017-0.014
Any adjuvant therapy0.1470.000

配對前有 5 個共變項的 |SMD| 超過 0.1,最大的是 0.156;配對後超過門檻的剩下 0 個,最大值降到 0.067,平均值從 0.067 降到 0.029。

雙重穩健:為什麼「再校正一次」不是多此一舉

上一節說「配對後仍不平衡的重要變項,就在配對之後的模型裡再校正一次」。那句話背後有一個名字, 叫雙重穩健(doubly robust)。

配對或加權需要傾向分數模型是對的;配對後的迴歸需要結果模型是對的。兩個都可能錯。 雙重穩健的性質是:兩個模型只要有一個是對的,估計就仍然一致——你不必賭中哪一個, 你買到的是兩張票裡中一張就好。

這也是為什麼「配對之後就不要再放共變項了,不然是重複校正」這種說法是錯的。 配對之後再校正一次,付出的代價是幾個自由度,換到的是一次補救的機會。實務上這是預設做法, 不是例外處理。

配對的三個選擇

配對不是一個動作,是三個獨立的決定。三種設定在同一份資料上的結果:

設定暴露組對照組暴露組被丟掉最大 |SMD|超過 0.1 的變項數HR95% CI
1:1, caliper 0.2 SD919180.06701.020.51–2.03
1:1, no caliper999900.08701.030.51–2.09
1:2, caliper 0.2 SD76152230.10210.830.40–1.74

三個決定各自在買什麼:

  • 1:1 還是 1:k。 對照組人多的時候,1:k 可以增加事件數、提高精確度。但第 k 個最近鄰一定比第一個遠,所以k 越大、平衡越差——上表第三列就是這樣:1:2 讓最大 |SMD| 越過了 0.1。
  • caliper(配對半徑)。 慣例是 0.2 個 logit 傾向分數標準差(這份資料是 0.067)。它擋掉的是「找不到夠像的人」的那些暴露組個案。上表第一列丟掉 8 位、換到更好的平衡;第二列不設 caliper,一個人都不丟,但最大 |SMD| 比較大。這是偏誤與樣本數之間直接的交換。
  • 有無放回(with / without replacement)。 上面的程式碼是不放回:用過的對照從池子裡拿走。放回的作法允許同一位對照被配給多位暴露組,平衡通常更好,但同一個人被重複計算,變異數估計必須另外處理,而且如果少數幾位對照被反覆使用,實際的有效樣本數會遠小於名目人數。

表上最後兩欄是三種設定各自的 HR 與信賴區間(都用配對組穩健變異數)。它們列在這裡不是要你挑一個,而是因為下一段要求的「全部一起報」,長的就是這個樣子:三個點估計從 0.83 到 1.03,信賴區間彼此高度重疊、全部跨過 1。看起來離 1 最遠的是 1:2, caliper 0.2 SD(0.83,0.40–1.74),而它同時是丟掉最多暴露組(23 位)、平衡最差(最大 |SMD| 0.102、1 個變項越線)的那一個——點估計動得最多的那一個設定,也正是樣本被換掉最多的那一個。

敏感度分析的讀法是看這三個數字散得多開,不是從裡面選一個。在這份資料上它們散得不算開,但三個區間都寬到無法排除中等程度的效應,所以散不散開本身也說明不了什麼。

配對後的樣本已經不是原本的母體

這是最容易被忽略、卻直接影響結論怎麼寫的一點。

1:1 配對(尤其是加了 caliper 的)保留的是每一位配得到對照的暴露組個案,加上跟他們最像的那些對照。所以配對後的樣本代表的是「長得像暴露組的那群人」,估到的量是 ATT(average treatment effect on the treated,對已接受治療者的平均效果),不是全體母體的 ATE。

估計量問的是什麼誰的族群
ATE如果所有人都接受治療 vs 都不接受,平均差多少全體
ATT實際接受治療的那些人,治療比不治療好多少治療組
ATO治療與否勢均力敵的那些人,差多少重疊區

這三者在效果隨個人特徵變化(效果修飾)時數值會不同,而且回答的是不同的臨床問題。ATT 問的是「這些已經在用藥的人,用對了嗎」;ATE 問的是「如果推廣到所有人會怎樣」。逆機率加權那一頁會示範同一份資料在三種估計量下跑出來的數字。

這一頁的模型丟掉了 8 位暴露組個案,所以嚴格說連 ATT 都不是——它是「配得到對照的那部分暴露組」的 ATT。這句話要寫進 limitation,而且要報出被丟掉的人數。

配對後的變異數

配對之後的兩列不是獨立的——它們是因為傾向分數相近才被放在一起的。理論上這代表變異數估計要考慮配對結構,否則信賴區間會失準。作法是在 Cox 模型裡加上 cluster(pair),改用穩健(sandwich)變異數。

模型nHR95% CIp
未校正(全部人)2871.020.56–1.850.945
多變項 Cox 校正(全部人)2871.270.67–2.400.462
1:1 配對後1821.020.51–2.040.954
1:1 配對後 + 配對組穩健變異數1821.020.51–2.030.954

這張表的四列不是同一個估計量,不要拿 HR 的大小來判定哪種方法比較好。「多變項 Cox 校正(全部人)」是全樣本、給定共變項的 conditional HR;「1:1 配對後」則是在配對樣本、以 ATT(average treatment effect on the treated)為目標族群的 HR。兩者的估計對象與目標族群都不同;而且 Cox 的 HR 具有 non-collapsibility——即使完全沒有未校正的干擾,conditional 與 marginal 的數值也不必相等。數字不一樣本身不是「配對改善了估計」的證據。

一行版的 MatchIt

上面手寫的那三十行,實務上就是這一行:

library(MatchIt)
m <- matchit(treat ~ Age + AA + FamHx + PVol + TVol + T.Stage + bGS +
               PreopPSA + PreopTherapy + Units + sGS + AnyAdjTherapy,
             data = cc, method = "nearest", distance = "glm",
             caliper = 0.2, std.caliper = TRUE, replace = FALSE)
summary(m)                 # 平衡表,含配對前後 SMD
md <- match.data(m)        # 配對後的資料,帶 subclass 與 weights 欄
coxph(Surv(TimeToRecurrence, Recurrence) ~ treat + cluster(subclass), data = md)

MatchIt 4.7.2 配出 92 對、HR 1.04(0.51–2.13),與手寫版的 91 對、HR 1.02 只差在貪婪配對的隨機順序不同。兩邊數字不完全相同是預期內的——貪婪配對對「先處理誰」敏感,這也是為什麼配對腳本一定要 set.seed()

實務上請用套件:它處理好了 optimal matching、full matching、放回的權重、以及一整套平衡診斷。手寫版的用途是讓你知道套件在做什麼,以及在它給你奇怪結果的時候知道要去看哪裡。

結果怎麼寫

三種模型的點估計都落在 1 附近,信賴區間全部跨過 1。正確的寫法是:在這個世代中,未偵測到紅血球儲存時間與生化復發的關聯。

不可以寫成「儲存時間與復發無關」,也不可以把配對後的 HR 1.02 講成「風險增加」——信賴區間 0.51–2.03 同時容納了實質的保護與實質的危害,這份資料的事件數(配對後 32 例)不足以排除中等程度的效應

常見誤用

誤用為什麼錯
用 PS 模型的 AUC 判斷模型好壞AUC 高代表重疊差;唯一判準是配對後的平衡
把結果變項放進傾向分數模型PS 模型的結果變項是治療,放入結果會造成過度配適與偏誤
對配對前後做組間 p 值檢定樣本數變小 p 值就會變大,衡量的不是平衡
只報整體平衡良好、不報逐個變項配對保證分數配得近,不保證每個共變項都更平衡
不報有多少暴露組因 caliper 被丟掉那是一個沈默的篩選步驟,會改變估計的族群
把配對後的估計說成全體母體的效果配對估的是 ATT,且只是配得到對照的那部分
配對後不看共同支撐區兩組沒有重疊的區域,任何方法都只能外插
配對後直接跑一般模型不理配對結構變異數估計的前提被破壞;成本只是一行程式碼
換好幾組 caliper 只報最好看的這是讀者從論文上看不出來的選擇性呈現
配對之後就宣稱因果傾向分數只能平衡有測量到的共變項,見 B6-01
傾向分數模型漏掉關鍵共變項還說已校正沒進模型的變項不會被平衡到,配對不會替你發現它

重跑本頁的所有數字

/opt/homebrew/bin/Rscript figures/scripts/B6-02-psm.R

讀讀看這張圖

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

配對之後整體平衡改善了,但 love plot 上有四個共變項是往壞的方向動的。哪一句正確說明這四個變項?

看答案與解析

正確答案: 配對後最大的絕對標準化平均差是 0.067,它就落在這四個裡面——貪婪配對配的是分數,不是每一個共變項

往壞的方向動的那四個共變項,配對前本來就都貼著零,而配對把其中一個推到 0.067——那正好是配對後的最大值。0.156 是配對前的最大值,屬於另一個共變項,而它配對之後反而變好了,所以「本來就是最不平衡的幾個」正好說反。0.029 是配對後的平均,整體看確實漂亮,但平均值正是把這四個變項藏起來的那個數字;而它們的惡化也不是波動,配對是決定性的演算法,同一份資料跑幾次都是同一批人。分數相同的兩個人可以靠不同的共變項組合湊出同一個分數,所以平衡表要一列一列取絕對值來看,不能只報「整體平衡良好」。

上表第一列設了 caliper、第二列沒設,第一列少了八位暴露組個案。哪一句正確說明這兩列的差別?

看答案與解析

正確答案: 設了 caliper 那一列的最大絕對標準化平均差是 0.067,比不設的那一列低,代價是八位配不到對照的人被丟掉

設 caliper 那一列的最大值是 0.067,不設的那一列是 0.087——方向與「拿平衡換樣本數」相反,caliper 買到的正是比較好的平衡,賣掉的是八位找不到夠像的對照的暴露組個案。兩個數值都在慣例門檻以下,所以這裡真正該報的不是誰過關,而是那八個人被丟掉這件事:它是一個篩選步驟,要說明被丟掉的是誰。0.835 是一比二那一列的風險比,不屬於這兩列的任何一列;而且用「點估計離一多遠」來挑配對設定,正是這一節警告的事:三個設定的區間高度重疊、全部跨過一,而點估計動得最多的那一個設定,同時也是樣本被換掉最多的那一個。

配對組的穩健標準誤是 0.350,未修正的是 0.354,穩健的反而小一點點。教科書說不認配對結構會低估變異數,這份資料卻反過來。哪一句正確說明這件事?

看答案與解析

正確答案: 未修正的標準誤是 0.354,與穩健的 0.350 幾乎相同,而差多少由配對後兩列有多像決定,那是資料決定的

修正的方向與大小取決於配對後兩列有多像,而那是資料決定的:這份資料的共變項配對前本來就相當平衡,配對能造出的組內相關有限,所以 0.354 與 0.350 幾乎沒有差別。兩個錯選項的毛病是同一個——把一份資料上的結果升級成一條規則。「不認配對結構會高估變異數」與「這一步可以省下來」都要先算了穩健變異數才知道,用算出來的結果回頭主張不必算,是循環的。在兩組差異更大的資料上,同一行程式碼會改出可觀的差別,而你事先不知道它會不會有影響,所以照做——這是一行程式碼的成本,付得起。

一比一加 caliper 的配對丟掉了一部分暴露組個案。配對後估到的量代表誰?

看答案與解析

正確答案: 留下來的 91 位暴露組就是目標族群,估到的是配得到對照的那部分暴露組的效果

配對保留的是每一位配得到對照的暴露組個案,加上跟他們最像的那些對照,所以配對後的樣本代表的是「長得像暴露組的那群人」。這裡還有一部分暴露組因為 caliper 找不到夠像的人被丟掉,所以嚴格說連對已治療者的平均效果都不是,而是那 91 位的版本——這句話要寫進 limitation,被丟掉的人數也要報。被丟掉的那幾位暴露組正是「配不到對照」的那些人,把他們排除掉恰恰就是目標族群縮小的那一步,不是與目標族群無關的技術細節。97 是被丟掉的對照人數:對照被丟掉多少不決定目標族群是誰,暴露組被丟掉多少才會。

同一份資料,未校正的風險比是 1.02,多變項 Cox 校正之後是 1.27,一比一配對後是 1.02。哪一句正確說明這三個數字的並排?

看答案與解析

正確答案: 校正後的區間上界是 2.40,三個區間都跨過一,這幾個數字的差落在雜訊的量級裡

三個點估計都落在一附近,信賴區間全部跨過一,而校正後的區間一路跨到 2.40。更根本的是這幾列不是同一個估計量:多變項 Cox 給的是給定共變項的條件風險比,配對後給的是以已治療者為目標族群的風險比,加上 Cox 的風險比具有 non-collapsibility,就算完全沒有未校正的干擾,兩個數值也不必相等。p 值 0.46 雖然比未校正那一個小,但兩個都遠離顯著,把「比較小」讀成「推出了關聯」是把未達統計顯著的差異當成訊號。1.02 在兩種方法裡重複出現也不是互相印證:它們用的是同一批人、同一個結果,而這份資料的事件數不足以排除中等程度的效應。

這份資料的傾向分數落在 0.125 到 0.613 之間,兩組的共同支撐區是 0.233 到 0.609。落在共同支撐區外面的人會怎樣?

看答案與解析

正確答案: 共同支撐區的下界是 0.233,配對會把落在它外面的人丟掉,迴歸校正不會,它照樣把那些人算進去

沒有重疊的區域裡沒有可比較的對照,任何方法在那裡都只能外插。配對相對於迴歸校正的一個實質優點就在這裡:配不到的人會被列出來丟掉,你看得見;迴歸不會告訴你它在外插,也不會把那一段補起來——「找最近的對照」在共同支撐區外面找到的正是不夠像的人,那就是 caliper 存在的理由。0.336 是對照組的平均傾向分數,兩組平均接近不代表分布重疊,重疊要看分布的兩端,那正是背對背直方圖存在的理由。0.125 比共同支撐區的下界 0.233 還低,那一段就是對照組有人、暴露組一個人也沒有的地方。

延伸觀看

資料小探 – 傾向分數配對法
繁中資料科科講· 4 min繁中最短的入門,四分鐘建立畫面感,讀本頁之前先看這支。
Propensity scores: Everything you need to know in 5min
ENMichael Fralick· 7 min臨床脈絡的總覽,講清楚「傾向分數在模仿隨機化的哪一部分」。
How Propensity Scores Work | NEJM Evidence
ENNEJM Group· 5 minNEJM 官方的動畫版,適合拿來跟同學解釋。
Propensity score matching: an introduction
ENBen Lambert· 9 min計量經濟背景,把「為什麼一個分數就足以平衡所有共變項」推得比醫學頻道清楚。
Outcome research: Causal inference & Propensity score II 傾向分數
繁中陳冠甫(長庚)· 64 min繁中完整課程,本頁每一節在這裡都有更長的版本。

素材來源與授權

本頁為原創內容

回報內容問題

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

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

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

一併送出的資訊

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