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

邊際估計與 G-computation

模型給的是條件 OR,但 NNT、風險差這些能在族群層次解讀的量要另外算。這一頁教標準化(G-computation)怎麼做、為什麼條件 OR 與邊際 OR 本來就不相等(non-collapsibility),以及信賴區間為什麼要用 bootstrap。

這一頁在還兩張支票

邏輯迴歸那一頁講到 OR 不可塌縮時,撂下了一句 「想要一個可以在族群層次解讀的效果量(例如要算 NNT),應該報邊際估計」, 常見誤用表裡也寫了「拿模型的 OR 去算 NNT 或族群層次的影響」是錯的, 需要的是「標準化 / G-computation」。

兩句話都沒有說怎麼做。這一頁做給你看。

用的是同一份 MASS::birthwt、同一個結果變項(新生兒體重是否低於 2500 g), 共變項也刻意跟那一頁重疊,好讓兩頁的數字可以直接對照。

條件 OR 為什麼不能拿去算 NNT

邏輯迴歸的係數取指數之後是 OR,而且是條件(conditional)OR: 它的完整念法是「在年齡、產婦體重、族裔、高血壓病史、子宮易激性都固定不動的前提下, 抽菸讓低出生體重的勝算變成幾倍」。

問題就在「固定不動」這五個字。臨床上要回答的問題通常長這樣:

如果這個族群裡所有人都不抽菸,低出生體重會少多少?

這句話裡沒有任何東西被固定住。族群裡的人年齡不同、體重不同、族裔不同, 問的是整個族群的平均風險會怎麼變。條件 OR 沒有回答這個問題, 它回答的是「某一個特定共變項組合底下的那一小群人」會怎麼變。

再加上兩件事,條件 OR 就更不能直接拿來算 NNT:

  • NNT 的定義是風險差的倒數,而 OR 不是風險差,也不能單獨換算成風險差—— 同一個 OR 在不同基線風險下對應完全不同的風險差(這件事在 邏輯迴歸那頁的 OR 對 RR 那張圖上已經看過)。
  • OR 不可塌縮,所以就算你把它換算成某個「平均」的量,換出來的也不是族群層次的那個量。 這一點在談不可塌縮性那一節專門處理。

G-computation:三個步驟

標準化(standardisation)在迴歸模型上的做法叫 G-computation,只有三步:

  1. 照常配適一個模型。 這裡就是普通的邏輯迴歸,沒有任何特別之處。
  2. 把整份資料複製兩份。 第一份把每個人的暴露欄位都改成「有」,第二份都改成「沒有」, 其他欄位一個字都不動。所以每一位產婦在兩份資料裡都出現一次, 一次當作她抽菸、一次當作她沒抽菸。
  3. 各自預測後取平均。 兩個平均值就是兩個邊際風險,相減是風險差,相除是相對風險。

第二步是整個方法的重點:不動其他欄位,等於用這份研究自己的共變項分布當作標準人口。 所以算出來的量是「這個族群如果全部暴露 / 全部不暴露」,不是任何假想族群。

library(MASS)
data(birthwt, package = "MASS")

bw <- birthwt
bw$race_f  <- factor(bw$race,  levels = 1:3, labels = c("White", "Black", "Other"))
bw$smoke_f <- factor(bw$smoke, levels = 0:1, labels = c("No", "Yes"))

FORM <- low ~ smoke_f + age + lwt + race_f + ht + ui
fit <- glm(FORM, data = bw, family = binomial)

# 第二、三步:複製兩份、各自預測、取平均
gcomp <- function(model, data) {
  d1 <- d0 <- data
  d1$smoke_f <- factor("Yes", levels = levels(data$smoke_f))
  d0$smoke_f <- factor("No",  levels = levels(data$smoke_f))
  r1 <- mean(predict(model, d1, type = "response"))
  r0 <- mean(predict(model, d0, type = "response"))
  c(risk1 = r1, risk0 = r0, RD = r1 - r0, RR = r1 / r0, NNT = 1 / (r1 - r0))
}
gcomp(fit, bw)

# 邊際 OR 要從兩個平均風險換算,不是拿模型係數
r <- gcomp(fit, bw)
(r["risk1"] / (1 - r["risk1"])) / (r["risk0"] / (1 - r["risk0"]))
exp(coef(fit))["smoke_fYes"]        # 條件 OR,數值不一樣

# 信賴區間:重抽個體,整個流程重跑一次
set.seed(20260823)
boot <- replicate(2000, {
  ix <- sample(nrow(bw), replace = TRUE)
  d  <- bw[ix, ]
  gcomp(glm(FORM, data = d, family = binomial), d)
})
apply(boot, 1, quantile, c(0.025, 0.975))

驗證環境:R 4.6.0 + MASS 7.3.65。實務上 marginaleffects 或 stdReg 套件可以一行做完,這裡手寫是為了讓每一步看得見。

算出來是什麼

估計值95% CI(bootstrap)怎麼念
邊際風險,全部抽菸43.1%31.2–54.7%這個族群如果人人抽菸,低出生體重的比例
邊際風險,全部不抽菸23.8%16.6–31.9%同一群人如果人人不抽菸
風險差(RD)19.2 個百分點4.2–33.8兩個邊際風險相減
相對風險(RR)1.8081.15–2.86兩個邊際風險相除
NNT5.22.9–21.7風險差的倒數
邊際 OR2.4191.22–4.78兩個邊際風險換成勝算後相除
條件 OR(模型係數)2.7941.29–6.05其他變項固定時的勝算比

NNT 念成「這個族群裡每有這麼多位產婦戒菸,就少一個低出生體重的新生兒」。 這句話裡沒有「其他變項固定」,因為 G-computation 本來就沒有固定任何東西。

Non-collapsibility:兩個 OR 不相等,而且不是誰算錯

同一個模型、同一批人、同一個對比,卻有兩個 OR:

  • 條件 OR 2.794
  • 邊際 OR 2.419

差距是 0.375,比值 1.155。 條件 OR 離 1 比較遠——永遠是這個方向

兩格對照圖。左格是三個比值型指標的森林圖,橫軸為對數尺度,虛線畫在 1:最上面是條件 OR 2.79(1.29–6.05),中間是邊際 OR 2.42(1.22–4.78),最下面是邊際相對風險 1.81(1.15–2.86)。三條橫線由上而下依序往 1 靠近,但三者整段都落在虛線右側。右格是風險差,虛線畫在 0,點估計 19.2 個百分點、區間 4.2 到 33.8,整段落在 0 的右側,下方另標了 NNT 5.2(2.9–21.7)。
同一個模型、同一批人,四個不同的量。條件 OR 離 1 最遠,邊際 OR 次之,邊際相對風險最接近 1。左右兩格的橫軸尺度不同,不能互相比長度。產圖腳本 figures/scripts/B2-08-marginal.R

為什麼會這樣?因為勝算比是不可塌縮的效果量:各次群勝算比的平均,不等於平均起來那一群的勝算比。理由不是「非線性」——log 連結一樣是非線性,它的發生率比卻塌縮得好好的。

把一群風險高低不同的人各自算出勝算、再平均,跟先把他們的風險平均起來、再換成勝算, 本來就不是同一件事。模型給每個人的預測風險散得很開—— 抽菸情境下從 6.9% 到 90.7%, 不抽菸情境下從 2.6% 到 77.7%—— 散得越開,兩個 OR 差越多。

信賴區間為什麼要 bootstrap

邊際風險差是所有共變項與所有係數的一個非線性函數,不是任何一個係數。 模型報表裡沒有它的標準誤,summary() 那一欄也不是它的。

標準做法是非參數 bootstrap:重抽個體、重新配適模型、把整個 G-computation 流程再跑一次, 重複 2000 次,取百分位數。重點在「整個流程再跑一次」—— 只重抽而不重新配適模型,等於假裝係數是已知的。

那如果偷懶呢?下面三種做法在同一份資料上跑出來的風險差區間:

做法風險差的 95% CI(百分點)區間寬度相對 bootstrap
非參數 bootstrap(重抽個體,全流程重跑)4.2–33.829.5
Delta method(共變項分布視為固定)5.2–33.328.10.95 倍
只把暴露係數的 Wald 上下限代回去4.3–35.531.21.06 倍

NNT 的信賴區間是歪的,不要寫成正負

NNT 是風險差的倒數,而倒數會把「接近 0」放大成「接近無限大」。 本頁的風險差下限是 4.2 個百分點,離 0 不遠, 於是 NNT 的上限被推到 21.7:

  • 點估計 5.2
  • 95% CI 2.9 到 21.7

下界離點估計只有 2.3, 上界卻離了 16.5。

預測機率圖:它是標準化曲線,不是 partial plot

兩格圖。左格是散點圖,橫軸為產婦體重(磅),縱軸為預測的低出生體重風險:紅點是把每個人都設成抽菸時的預測值,藍點是都設成不抽菸時的預測值,兩群點都隨體重增加而下降,紅點整體位在藍點上方;兩條水平虛線分別標出兩群點的平均值 0.431 與 0.238。右格是兩條下降的標準化風險曲線加 bootstrap 信賴帶,紅色(抽菸)整段在藍色(不抽菸)上方:體重約 90 磅時兩條線分別約 0.567 與 0.346,到約 218 磅時降到約 0.171 與 0.075;兩條信賴帶在整個體重範圍內都有重疊。圖下方以短刻度標出實際觀察到的產婦體重分布。
左:模型給的是每一位產婦各自的風險,邊際估計是把它們平均起來。右:把產婦體重也標準化之後的兩條風險曲線,陰影是 bootstrap 信賴帶。產圖腳本 figures/scripts/B2-08-marginal.R

右格容易被誤認成一般的 partial dependence plot(「其他變項固定在平均值,只讓體重動」)。 它不是。 那條曲線的每一個點是這樣算出來的:把所有人的體重都改成同一個值, 其他欄位維持各自原樣,預測後取平均。也就是把 G-computation 再套用一次到體重上。

差別不是吹毛求疵:

  • 固定在平均值的做法會產生一個現實中可能不存在的「平均病人」—— 平均年齡、平均體重、族裔卻是某個小數點。共變項越多、越是類別變項,這個虛構人物越荒謬。
  • 標準化沒有虛構任何人,它保留了整個族群的異質性,只換掉被指定的那一個變項。

兩條曲線之間的垂直距離,就是「在那個體重水準下的邊際風險差」。 體重越大,兩條線的絕對距離越小——從左端約 22.0 個百分點 縮到右端約 9.6 個百分點。

什麼時候該報哪一個

你要回答的問題該報的量
兩個各方面條件相同的病人,暴露與否差多少條件估計(模型係數)
這個族群整體如果都暴露 / 都不暴露,差多少邊際估計(G-computation)
要算 NNT、要估政策影響、要跟決策者講話邊際風險差
要跟其他論文的效果量比對兩個都報,並寫明是哪一個

常見誤用

誤用為什麼錯
拿條件 OR 直接算 NNTNNT 是風險差的倒數,OR 不是風險差也換不出來
把條件 OR 與邊際 OR 的落差當成殘留干擾那是 OR 不可塌縮的代數性質,沒有干擾也會發生
說「兩個 OR 不一樣一定有一個算錯」兩個都對,回答的是不同的問題
用模型係數的標準誤當邊際風險差的標準誤邊際估計是所有係數與共變項的非線性函數,不是任一係數
bootstrap 時只重抽、不重新配適模型這是壞掉的 bootstrap,不是上表那兩種捷徑;它把係數當成已知,會低估不確定性
把 NNT 寫成「點估計 ± 某個數」它的抽樣分布嚴重不對稱
風險差區間跨過 0 卻照樣報 NNT 區間那個區間是不連續的,寫成一段會誤導
把標準化曲線說成「其他變項固定在平均值」那是 partial plot,會造出一個不存在的平均病人
用兩條信賴帶有沒有重疊來判斷有沒有差異要看差異本身的信賴區間;帶子重疊比它保守得多
以為 G-computation 讓結論自動變成因果算術不處理未測量的干擾,因果解讀要另外的假設

延伸

重跑本頁的所有數字

/opt/homebrew/bin/Rscript figures/scripts/B2-08-marginal.R

讀讀看這張圖

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

同一個模型、同一批人,模型印出來的 conditional odds ratio 是 2.79,G-computation 的 marginal odds ratio 是另一個數字。兩者為什麼不同?

看答案與解析

正確答案: marginal 是 2.42——odds ratio 不具備 collapsibility,兩者本來就不會相等

marginal OR 是 2.42、conditional 是 2.79,而 2.02 是完全不校正的粗 OR——marginal 不等於粗估,它是校正過的、只是被平均到整個族群上。兩者不同既不是誰算錯,也不是殘餘混淆:odds ratio 不具備 collapsibility,就算共變項與暴露完全無關,把它平均掉仍然會改變數值。這是 OR 特有的性質,risk ratio 與 risk difference 沒有這個問題。

G-computation 給出的 marginal risk difference 描述的是什麼?

看答案與解析

正確答案: 0.192——全體都抽菸與全體都不抽菸兩個情境下的平均風險之差

0.192 是把每一列複製兩份、一份強制抽菸一份強制不抽,算出每個人在兩種情境下的預測風險再各自平均、最後相減得到的,所以它描述整個族群而不是某一個特定的共變項組合。0.153 是完全不校正的粗風險差;0.238 只是其中一個情境下的平均風險,不是差。這個區別在寫結論時很要緊:marginal 估計回答的是政策式的問題(如果這群人都不抽菸會怎樣),不是某一個病人的問題。

同一個 risk difference,三種信賴區間算法給出不同的寬度。哪一種承認的不確定性最完整?

看答案與解析

正確答案: bootstrap 百分位法,寬度 0.295——重抽樣讓共變項分布與所有係數一起變動

bootstrap 的 0.295 讓整個估計流程重跑,共變項分布與所有係數一起變動,承認的不確定性最完整。delta method 的 0.281 把共變項分布當成固定的,因此少算了一塊。0.312 確實最寬,但寬不等於完整:它只讓抽菸那一個係數在它的 Wald 界限之間移動,其他係數全部釘死——它在一個方向上過度保守,在另一個方向上根本沒有考慮。三個都自稱是同一個估計值的 95% 區間,而報表上它們看起來一樣正當。

素材來源與授權

本頁為原創內容

回報內容問題

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

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

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

一併送出的資訊

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