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

交互作用與次族群分析

含交互作用的模型裡四個係數各代表什麼、為什麼 exp(交互作用項) 是「比值的比值」、怎麼從一個模型算回各層別的估計,以及次族群 forest plot 上最容易犯的那個錯:把顯著性的差異讀成差異的顯著性。

交互作用在回答什麼問題

一篇試驗的主要結果給的是平均效果。看完那個數字,臨床上的下一個問題幾乎一定是: 「這個藥對誰比較有效?年紀大的人呢?本來就高風險的人呢?」

這個問題有一個正式的名字:交互作用(interaction), 在流行病學的脈絡下也叫效果修飾(effect modification)。 它問的是——治療效果會不會隨著某個特徵而改變。

回答它的方式不是把資料切成幾塊、各自跑一次、然後比較誰的 p 值比較小。 那是本頁要拆掉的錯誤,而且它是臨床論文裡最常見的統計誤讀之一:

「女性顯著、男性不顯著」=「女性和男性的治療效果不同」

這個等號不成立。左邊是兩個各自對照 0 的檢定,右邊是一個兩者互相對照的檢定。 兩個層別的樣本數不同、事件數不同,光是這一點就足以讓其中一邊跨過 0.05 而另一邊沒有, 即使底下的效果完全沒有隨層別改變。這一頁會用同一份資料把兩種讀法擺在一起, 讓落差看起來像它實際的樣子。

這一頁的例子

沿用隨機對照試驗那一章的 medicaldata::indo_rct: 直腸給予 indomethacin 預防 ERCP 術後胰臟炎(post-ERCP pancreatitis, PEP)的隨機試驗。 602 位受試者,共 79 個 PEP 事件。

整體效果(未校正的邏輯迴歸):indomethacin 組 27/295(9.2%), 安慰劑組 52/307(16.9%), OR 0.49(0.30–0.81), p 0.005。

這份資料被選來教這一頁,是因為它自帶一整排預先指定的次族群變項, 而其中好幾個的層別估計看起來差很多、交互作用檢定卻完全沒有支持。 那正是最難教、也最需要被看見的組合。

library(medicaldata)
data(indo_rct, package = "medicaldata")

d <- indo_rct
d$y    <- as.integer(d$outcome == "1_yes")          # 1 = 發生 PEP
d$tx   <- as.integer(d$rx == "1_indomethacin")      # 1 = indomethacin
d$male <- as.integer(d$gender == "2_male")          # 0 = 女性(參考層)

# 一個含交互作用的模型,四個係數
fit <- glm(y ~ tx * male, data = d, family = binomial)
summary(fit)

# 層別 OR:不要把資料切開跑兩次,從同一個模型算回來
exp(coef(fit)["tx"])                                 # 女性層
exp(coef(fit)["tx"] + coef(fit)["tx:male"])          # 男性層

# 男性層的 CI 要用兩個係數的變異數「和」共變異數,不能只加兩個 SE
V  <- vcov(fit)
b  <- unname(coef(fit)["tx"] + coef(fit)["tx:male"])
se <- sqrt(V["tx", "tx"] + V["tx:male", "tx:male"] + 2 * V["tx", "tx:male"])
exp(c(OR = b, lcl = b - 1.96 * se, ucl = b + 1.96 * se))

# P for interaction:概似比檢定,不是 Wald
fit0 <- glm(y ~ tx + male, data = d, family = binomial)
anova(fit0, fit, test = "LRT")

# 對每個次族群變項重複同一件事
for (v in c("gender", "sod", "pep", "psphinc", "precut", "train", "bsphinc")) {
  f  <- droplevels(d[[v]])
  m0 <- glm(y ~ tx + f, data = d, family = binomial)
  m1 <- glm(y ~ tx * f, data = d, family = binomial)
  cat(v, anova(m0, m1, test = "LRT")[["Pr(>Chi)"]][2], "\n")
}

驗證環境:R 4.6.0 + medicaldata 0.2.0。交互作用檢定一律用 anova(..., test = "LRT"),理由見本頁最後一節。

四個係數各代表什麼

glm(y ~ tx * male) 展開之後是這樣一個模型:

logp1p=β0+β1tx+β2male+β3(tx×male)\log \frac{p}{1-p} = \beta_0 + \beta_1 \cdot \text{tx} + \beta_2 \cdot \text{male} + \beta_3 \cdot (\text{tx} \times \text{male})

跑出來的四個係數:

它是什麼係數(log 尺度)標準誤取指數p
(Intercept)參考格(安慰劑 × 女性)的 PEP log-odds-1.55690.16780.2108< 0.001
tx女性層裡 indomethacin 相對安慰劑的 log OR-0.78970.28800.45400.006
male安慰劑組裡男性相對女性的 log OR-0.17770.39860.83720.656
tx:male男性層的治療 log OR 減去女性層的治療 log OR0.39270.61111.48090.521

一列一列看:

  • 截距。把 txmale 都設成 0,剩下的就是截距。 取指數之後 0.2108 是安慰劑組女性的 PEP 勝算—— 可以直接用手驗:那一格有 43 個事件、 204 個非事件,相除就是這個數字。

  • tx 的係數,是女性層的治療效果,不是「整體的治療效果」。 取指數 0.4540 就是女性層的 OR。 這是全篇最常被誤讀的一格。

  • male 的係數,是安慰劑組裡男性相對女性的效果, 不是「男性整體的風險」。取指數 0.8372。

  • tx:male 才是交互作用:男性層的治療 log OR 減去女性層的治療 log OR, 也就是兩個效果之間的差,而不是任何一個效果本身。

exp(交互作用項) 是「比值的比值」

把上面那個模型的定義展開,兩個層別的治療 OR 分別是:

ORfemale=exp(β1),ORmale=exp(β1+β3)\mathrm{OR}_{\text{female}} = \exp(\beta_1), \qquad \mathrm{OR}_{\text{male}} = \exp(\beta_1 + \beta_3)

兩式相除,β1\beta_1 消掉:

ORmaleORfemale=exp(β1+β3)exp(β1)=exp(β3)\frac{\mathrm{OR}_{\text{male}}}{\mathrm{OR}_{\text{female}}} = \frac{\exp(\beta_1 + \beta_3)}{\exp(\beta_1)} = \exp(\beta_3)

所以交互作用項取指數之後,是一個勝算比的勝算比(ratio of odds ratios)。 產圖腳本把等號兩邊都算出來對過:

算法數值
男性層 OR ÷ 女性層 OR = 0.6723 ÷ 0.45401.480909
exp(交互作用項) = exp(0.3927)1.480909
兩者相差0

這個恆等式是代數上的必然,不是這份資料的巧合——腳本裡用 stopifnot() 卡住,不成立就不會產出。

知道它是「比值的比值」之後,判讀方式就跟著決定了。 它的虛無值是 1(不是 0),要在 log 尺度上做檢定與算信賴區間, 而且它跟一般的 OR 一樣需要看區間: 這裡是 1.48 (0.45–4.91), 這個區間要翻回 OR 才讀得懂它有多寬,因為它是比值不是效果:把兩端各乘上女性層的 OR 0.45,就得到與資料相容的男性層 OR 範圍。

下界 0.447 對應男性層 OR 0.20—— 比女性層的 0.45 更低,也就是男性得到的保護反而更強。 上界 4.906 對應男性層 OR 2.23—— 已經越過 1,也就是在男性層可能反而偏向有害。

同一個區間同時容得下「男性得益更大」與「男性受害」這兩個相反的方向, 本研究並未偵測到治療效果隨性別而異。

從模型算回層別估計

有了四個係數,各層的 OR 直接由定義得到:參考層是 exp(第二個係數), 另一層是 exp(第二個係數 + 交互作用項)。

CI 就沒那麼直接了。男性層的 log OR 是兩個係數相加,它的變異數是

Var(β1+β3)=Var(β1)+Var(β3)+2Cov(β1,β3)\mathrm{Var}(\beta_1 + \beta_3) = \mathrm{Var}(\beta_1) + \mathrm{Var}(\beta_3) + 2\,\mathrm{Cov}(\beta_1, \beta_3)

漏掉那個共變異數項,區間就會算錯,而錯的方向由它的正負決定。 在這個模型裡它是負的,所以漏掉它會把區間算得太寬。 上面 R 程式碼裡的 vcov(fit) 就是在取它。

次族群 forest plot 怎麼讀

這份試驗有 7 個二元的預先指定次族群變項。 把每一層的治療 OR 與 P for interaction 一起畫出來,就是論文裡那張圖:

次族群森林圖。最上面一列是整體效果,OR 0.49(0.30–0.81),畫成紅色菱形,位置在 OR = 1 的左側。往下依序是 7 個次族群變項,每個變項底下兩列層別,共 14 列。每一列左側標出 indomethacin 組與安慰劑組各自的事件數/人數,右側標出該層的 OR 與 95% 信賴區間;P for interaction 只標在變項標題那一列的最右欄。圖上有兩條垂直線:實線在 OR = 1,紅色虛線在整體效果處。14 個層別的點估計全部落在 OR = 1 的左側,其中 6 條的信賴區間跨過 1。Precut 括約肌切開術「是」那一列的區間左端畫成箭頭,表示區間延伸到座標軸範圍之外。
橫軸夾在 OR 0.05 與 4 之間;區間端點超出範圍時畫成箭頭,代表真正的區間比圖上看到的更長。最右欄是各次族群變項的 P for interaction,不是各層別自己的 p 值——這個欄位放什麼,決定了讀者會得到什麼結論。產圖腳本 figures/scripts/B2-06-interaction.R

同一批數字列成表:

次族群n事件OR95% CI層別 pP for interaction
性別女性476630.450.26–0.800.0060.52
男性126160.670.23–1.930.461
疑似 Sphincter of Oddi 功能異常107160.370.11–1.240.1080.60
495630.530.31–0.910.022
曾發生過 post-ERCP 胰臟炎506560.540.30–0.960.0370.49
96230.360.13–0.980.046
胰管括約肌切開術259320.330.14–0.770.0100.23
343470.630.33–1.170.142
Precut 括約肌切開術570740.520.31–0.860.0110.49
3250.230.02–2.360.217
有受訓醫師參與操作319310.490.22–1.080.0760.95
283480.470.25–0.900.023
膽管括約肌切開術258330.310.13–0.720.0060.16
344460.660.35–1.230.192

讀這張圖的順序:

  1. 先看整體效果那一列,它是所有層別的參考點。這裡是 OR 0.49。
  2. 看每個變項的 P for interaction,判斷有沒有證據支持效果隨這個特徵改變。 本頁 8 個次族群變項的 P for interaction 落在 0.16 到 0.95 之間; 低於 0.05 的有 0 個。
  3. 最後才看各層的點估計與區間,而且是看它們有沒有一致地落在整體效果附近, 不是看誰的區間有沒有壓到 1。
  4. 順便看每一層的 n 與事件數。Precut 括約肌切開術「是」那一層只有 32 人、5 個事件, 區間 0.02–2.36 涵蓋了從「幾乎完全防住」到「風險增為兩倍以上」的範圍。這種層別的點估計不含什麼資訊, 而它在圖上跟其他列長得一樣顯眼。

顯著性的差異,不是差異的顯著性

現在把倒數第二欄拿出來看。在 7 個二元次族群變項裡, 有 6 個 出現「一層的 p 低於 0.05、另一層不低於」的組合。 如果拿層別 p 值當判準,這份試驗可以生出 6 種不同的 「這個藥只對某某人有效」的故事。

而同一批資料的 P for interaction 全部不顯著。下面這張圖把兩種讀法擺在一起:

左右兩個面板。左邊面板 A 是三個次族群變項(性別、胰管括約肌切開術、膽管括約肌切開術),每個變項兩列,橫軸是層別 OR 的對數尺度,垂直線在 OR = 1。每個變項有一塊淺色帶狀區域,標出兩個層別的信賴區間共同涵蓋的 OR 範圍;三個變項的帶狀區域都很寬。層別 p 低於 0.05 的畫成藍色實心方塊、其餘畫成褐色空心方塊,每列右側標該層的 p 值。三個變項都呈現同一種樣子:上面那一列(實心)的區間整段落在 1 左側,下面那一列(空心)的區間右端越過 1,而兩條區間大幅重疊。右邊面板 B 是同樣三個變項各一列,畫的是兩個層別 OR 的比值與其 95% 信賴區間,紅色菱形;三條區間全部涵蓋 1,每列右側標出比值的點估計、信賴區間與 P for interaction。
同一組數字的兩種讀法。左邊看起來像兩個不同的效果,右邊把那個「不同」直接估出來——每一條區間都涵蓋 1,區間寬到無法排除任何一個方向。產圖腳本 figures/scripts/B2-06-interaction.R

以性別為例,把左邊面板的兩列拆開來看:

nOR95% CI層別 p
女性4760.450.26–0.800.006
男性1260.670.23–1.930.461

點估計 0.45 與 0.67 看起來確實有距離。 但是:

  • 女性層的整個信賴區間,被男性層的信賴區間完全包住。 重疊區間是 0.26–0.80, 佔較窄那條區間的 100%。 也就是說,凡是與女性層資料相容的 OR,沒有一個與男性層資料不相容。
  • 兩層的樣本數相差 3.78 倍 (476 對 126),事件數相差 3.94 倍 (63 對 16)。 男性層的區間比女性層寬得多,光靠這一點就足以讓它跨過 1。
  • 把「差異」本身估出來:兩個 OR 的比值是 1.48 (0.45–4.91), P for interaction 0.52。

你其實做了幾次檢定

還有一件事讓次族群的層別 p 值更不可靠:它們不只一個。

本頁一共測了 8 個次族群變項。 假設治療效果完全不隨任何一個特徵改變,每個檢定各有 0.05 的機率誤報, 至少出現一個 p 小於 0.05 的機率是

1(1α)k1 - (1 - \alpha)^{k}

代入之後是 33.7%—— 超過三分之一。而這還只算了交互作用檢定; 如果拿的是層別 p 值,檢定次數要再乘以層數,機率更高。

實務上真實論文的次族群數量往往不只這些,多重比較的代價也就更大。 完整的機制、校正方法與「預先指定」為什麼是唯一有效的防線, 見多重比較與次族群分析

層別越多,先崩掉的是層別估計,不是檢定

這份試驗還有一個 4 層的次族群變項:收案中心。 它沒有被畫進上面那張 forest plot,理由值得單獨看一次:

中心n事件OR95% CI
1_UM164360.410.19–0.91
2_IU413410.550.28–1.07
3_UK2221.220.07–22.40
4_Case30估不出來——

最後一個中心只收了 3 位受試者、 0 個事件,2×2 表有整整一列是零,OR 根本不存在。 倒數第二個中心有 22 位、2 個事件, OR 算得出來,但區間 0.07–22.40 的上界是下界的 336 倍,同樣不含資訊。

而交互作用檢定仍然算得出來,也不被那一層拖累:它是在 3 個自由度上做的整體檢定, P for interaction 0.89。

但要知道那個自由度裡有一格是空的。4_Case 只有 3 位受試者、0 個事件, 與它有關的交互作用參數在概似函數上不可辨識——資料裡沒有任何東西可以決定它的值。 R 是照名目上的參數個數報 3 個自由度, 不是照「資料真的支撐得起幾個參數」報。實際有效的自由度比這個數字小。

在這個例子上結論不受影響:P for interaction 0.89 不論放在 3 還是少一個自由度下都遠離顯著。 但若某個檢定剛好落在門檻附近,這件事就會變成結論的關鍵—— 看到多層次族群的整體檢定,先去數每一層的事件數,再決定那個 df 該不該相信。

LRT 還是 Wald

交互作用檢定有兩條路:

  • Wald 檢定:直接看 summary() 裡交互作用項那一列的 p 值。
  • 概似比檢定(likelihood ratio test, LRT):比較「有交互作用」與「沒有交互作用」兩個模型的概似, 也就是 anova(fit0, fit, test = "LRT")

在性別這個例子上,兩者幾乎沒有分別: Wald 0.5205、 LRT 0.5222。 本頁全程用 LRT,理由有三個:

  1. 多層變項只有 LRT 做得到。 收案中心有 4 層, 交互作用是 3 個係數, 要問的是「這 3 個係數是不是同時為零」。 Wald 只給你單一係數的 p 值,逐個看等於又做了一次多重比較。
  2. Wald 對參數化方式敏感,LRT 不敏感。 換一個參考層、把變項重新編碼, Wald 的 p 值會變,LRT 不會。
  3. 樣本數小或格子稀疏時 Wald 會失真,而且失真的方向是低估顯著性 (這就是邏輯迴歸那頁完全分離的極端版本: 標準誤爆炸、Wald 的 p 值趨近 1)。次族群分析正好是最容易踩到這件事的場合。

常見誤用

誤用為什麼錯
比較兩個層別各自的 p 值,宣稱效果不同那是兩個各自對照虛無值的檢定;要回答「不同嗎」需要交互作用檢定
P for interaction 大於 0.05 就寫成「各族群效果一致」檢定力不足時無法支持等式;只能寫「未偵測到」
把含交互作用模型的主效果讀成「整體效果」那是參考層的效果,參考層一換數字就變
連續修飾因子不置中就讀主效果主效果是「該變項等於 0」時的效果,可能落在資料範圍外
把資料切開跑兩次當成交互作用分析產不出 P for interaction,且共變項的效果被重複估計
算另一層的 CI 時忘記共變異數項兩個係數相加的變異數含 2×Cov,漏掉它區間就會算錯,方向由 Cov 的正負決定
事後挑出次族群才報選擇後的 p 值失效,只能當作產生假說
次族群數量不揭露讀者無法判斷多重比較的規模
只報乘法尺度的交互作用就談臨床決策決策看絕對得益,需要風險差尺度的估計
層別事件數極少仍照樣畫點估計估計可能根本不存在,圖上卻與其他層一樣顯眼
多層變項逐層兩兩比較應該用自由度大於 1 的整體檢定
事後拿收案中心切次族群中心混雜了病人組成、操作者與流程,不是一個可以單獨解讀的修飾因子
用 Wald 檢定多層變項的交互作用Wald 只測單一係數,且對參數化方式敏感

重跑本頁的所有數字

/opt/homebrew/bin/Rscript figures/scripts/B2-06-interaction.R

讀讀看這張圖

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

次族群森林圖上,女性那一層的 OR 顯著、男性那一層不顯著。要判斷治療效果在兩性之間是否不同,該讀哪一個 p 值?

看答案與解析

正確答案: 0.522——交互作用檢定,它直接問兩層的效果差得夠不夠多

交互作用 p 值是 0.522,離顯著很遠:這份資料沒有偵測到治療效果在兩性之間有差別。0.006 與 0.461 是兩層各自的 p 值,而「一層有星號、另一層沒有」不是交互作用的證據——那多半只反映兩層的人數不一樣,人多的那層檢定力較高。用層內 p 值去回答層間差異,是拿兩個各自的檢定去湊一個從來沒被做過的檢定。

這篇試驗檢定了八個次族群變項。其中六個出現「一層顯著、另一層不顯著」的樣子。這說明了什麼?

看答案與解析

正確答案: 說明「一邊有星號一邊沒有」在 6 個變項上出現,而這種景象幾乎必然會發生

八個變項裡有六個出現這種樣子,而交互作用檢定達到顯著的變項數是 0——六比零這個對比就是重點:只要各層人數不平均,「一邊有星號一邊沒有」幾乎是必然會出現的景象,它讀起來卻很像發現了什麼。8 是檢定過的變項總數。次族群森林圖之所以危險,正是因為它把這種必然的視覺樣式呈現得像是結果。

要把「兩性的治療效果差多少」量化成一個估計值,該用哪一個數字?

看答案與解析

正確答案: 1.48——兩層 OR 的比值,這才是「差多少」本身的估計

「差多少」的估計值是兩個 OR 的比值 1.48,也就是 ratio of odds ratios,它與交互作用檢定問的是同一件事。0.45 與 0.67 是兩層各自的 OR:任何一個單獨拿出來都是該層的效果,不是層間的差;而在比值尺度上要用除的不是減的。這個比值的信賴區間很寬——次族群分析真正的問題不是「有沒有差」,而是這份資料原本就沒有能力回答這個問題。

素材來源與授權

本頁為原創內容

回報內容問題

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

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

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

一併送出的資訊

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