邊際結構模型與時間相依 IPTW
病人會中途開始用藥、停藥、換藥,而決定他換不換的那個指標,本身又是前一次治療造成的。這種變項放進迴歸會同時造成過度校正與碰撞子偏誤,不放進去則干擾沒處理——兩個都錯。加權是第三條路:這一頁用一份真值已知的模擬,把三條路的估計並排放,並示範穩定化權重怎麼配、變異數為什麼不能直接讀、IPCW 怎麼跟 IPTW 相乘。
為什麼這個方法值得有自己的一頁
你在論文裡撞到這一頁的時機大概是三種:Methods 寫著 patients who switched treatment、 寫著 time-updated covariates、或者寫著 per-protocol analysis 而且不只是把不服藥的人刪掉。 自己手上那份資料撞到的時機只有一種:病人會中途開始用藥、停藥、換藥,而不是在第一天就被分好組、 從此不動。
這種資料有一個標準迴歸處理不了的形狀。決定病人第二次要不要治療的那個指標—— CD4 數值、血壓、腎功能、疾病活動度——本身就是第一次治療造成的。它同時是後續治療的干擾因子, 又是先前治療的後果。標準迴歸對這種變項只有兩個選項:放進去,或不放。 這一頁要說明的是為什麼兩個都錯,以及第三條路長什麼樣。
站上有三個地方把讀者送到這裡:時間相依共變項那頁講到 「時間相依的中介兼混淆不能用標準 Cox 處理」、IPTW 那頁講到 「暴露會隨時間改變時只有時間相依的加權走得通」、目標試驗模擬 那頁講到「per-protocol 要處理隨時間改變的暴露」。這一頁是那三條線的終點, 所以它自己不再把你轉介回去——底下有完整的權重形式、可以照跑的程式碼、 以及一份真值已知的模擬拿來對答案。
治療與干擾互相餵養:feedback 是什麼
把時間軸縮到最短——兩次門診——就足以把問題整個攤開:
- visit 0:病人接受或不接受治療,記作 。在這份模擬裡它是隨機分派的。
- visit 1:先量到一個臨床指標 ,再根據它決定第二次治療 。
- 結局 :例如一年內的某個事件。
站在一個尷尬的位置。它是 的後果(治療改變了指標), 又是 的干擾因子(指標決定了第二次要不要治療,而指標本身也預測結局)。 這就是 treatment-confounder feedback:治療改變共變項,共變項再回頭決定下一次治療。
figures/scripts/B6-08-msm.R圖上還有一個沒被觀察到的 ,它同時造成 與 。臨床上這種東西到處都是: 沒被記錄下來的疾病嚴重度、體能狀態、社經條件、家屬支持——它們影響指標,也影響結局, 而資料庫裡沒有欄位。
模擬用的生成過程把上面每一條箭頭都寫成一行式子:
| 變項 | 怎麼生出來的 | 意思 |
|---|---|---|
U | rnorm(n) | 未測量的共同原因,分析時完全看不到 |
A0 | rbinom(n, 1, 0.5) | 隨機分派,所以 visit 0 沒有干擾 |
L1 | logit 為 -0.50 + 1.50·A0 + 1.20·U | 被前一次治療推動,也被 U 推動 |
A1 | logit 為 -0.50 + 1.20·L1 + 0.30·A0 | 看著指標決定,這就是 confounding by indication |
Y | logit 為 -1.50 + -0.70·A0 + -0.70·A1 + 1.00·U | 兩次治療各自有保護效果,U 有害 |
實際生出來的世代長這樣:20,000 個人,事件率 13.4%。 沒治療的人裡有 40.4% 的 是陽性,有治療的則是 68.5%——這是 那支箭頭在資料裡的樣子。 而 陰性的人有 40.3% 接受第二次治療,陽性的則是 70.6%——這是 。兩件事同時成立,feedback 就成立了。
「放進去」與「不放」,兩個都錯
不放進去也沒有比較好: 是 真正的干擾因子(它同時預測 與 , 後者經由 ),不處理就是 confounding by indication 原封不動留在估計裡。
三條路跑出來的合計 log odds ratio( 與 兩期都治療對兩期都不治療)是這樣:
| 做法 | 合計 | 離真值多遠 | ||
|---|---|---|---|---|
| 把 L1 放進迴歸 | -0.835 | -0.592 | -1.427(-1.544, -1.309) | 0.180 |
| 完全不校正 | -0.622 | -0.353 | -0.975(-1.073, -0.872) | 0.272 |
| MSM,穩定化權重 | -0.593 | -0.580 | -1.173(-1.275, -1.064) | 0.074 |
| 真值(g-computation) | -0.624 | -0.624 | -1.247 | — |
方向是可以事先推出來的,而資料也照著走:把 放進迴歸偏過頭 (-1.427,比真值離虛無值更遠),完全不校正偏不足(-0.975,比真值更靠近零), 加權落在中間而且最靠近真值(-1.173,差 0.074)。 三條路各偏一個方向,這正是這份模擬要示範的東西。
figures/scripts/B6-08-msm.R加權是第三條路:MSM 在估什麼
先把估計目標寫清楚。用潛在結果的記號, 是「假如這個人在 visit 0 被指定 、在 visit 1 被指定 ,他會不會發生事件」。四種靜態策略各對應一個全世代的風險:
| visit 0 | visit 1 | 全世代風險 |
|---|---|---|
| 不治療 | 不治療 | 22.2% |
| 治療 | 不治療 | 13.4% |
| 不治療 | 治療 | 13.4% |
| 治療 | 治療 | 7.6% |
邊際結構模型就是直接對這四個風險配一個模型,而不是對「觀察到的 Y 條件在共變項上」配模型:
「邊際」的意思是左邊沒有條件在任何共變項上——它講的是整個世代的風險,不是某一種病人的風險。 「結構」的意思是左邊是潛在結果,不是觀察到的結果。 就是上面表裡 「兩期都治療」對「兩期都不治療」的 log odds ratio,也就是這一頁一路在追的那個數字。
那要怎麼在只有觀察資料的情況下估這個模型?把每個人依他實際接受的治療機率的倒數加權, 造出一個假想世代(pseudo-population):在那個世代裡,每個時點的治療分派都跟當時的共變項無關, 就像有人在每次門診擲了一次硬幣。
穩定化權重長什麼樣
未穩定化的權重是每個時點治療機率倒數的連乘。它會爆炸:只要某個人在某個時點接受的是 「以他的狀況幾乎不會有人給」的治療,分母就趨近零,那一個人就可以主宰整個分析。
穩定化權重在分子放回一個只用過去治療、不用共變項的機率:
上面那條橫槓表示「到該時點為止的全部歷史」。分母負責把共變項造成的干擾拿掉, 分子負責把「純粹由過去治療決定的那一部分」放回去,於是權重會集中在 1 附近而不是散開。 分子裡不可以放共變項,放了就等於把要拿掉的東西又加回來。
這份模擬的兩個模型是:分子 A1 ~ A0,分母 A1 ~ A0 + L1。
權重的樣子:平均 1.000、標準差 0.284、範圍 0.653 到 1.517,有效樣本數(ESS)18,505, 是名目 n 的 92.5%。穩定化是它這麼乖的原因。 同一組權重的未穩定化版本——分子換成 1、不是邊際治療機率——落在 2.76 到 7.43 之間,平均 4.00, 最大值是穩定化版本的 4.90 倍、標準差是 5.20 倍。其中 2.00 倍只是記帳問題 ( 在這裡是隨機分派,visit 0 的分子是個常數),但有效樣本數還是掉到 17,597,是名目 n 的 88.0%, 對比穩定化版本的 92.5%。
figures/scripts/B6-08-msm.R| A0 / L1 / A1 | 人數 | 穩定化權重 | 第二次治療跟著指標走嗎 |
|---|---|---|---|
| 1 / 0 / 0 | 1,731 | 0.653 | 跟著走(被下加權) |
| 0 / 1 / 1 | 2,707 | 0.743 | 跟著走(被下加權) |
| 0 / 0 / 0 | 3,717 | 0.812 | 跟著走(被下加權) |
| 1 / 1 / 1 | 4,964 | 0.879 | 跟著走(被下加權) |
| 0 / 0 / 1 | 2,283 | 1.307 | 沒跟著走(被上加權) |
| 1 / 1 / 0 | 1,836 | 1.324 | 沒跟著走(被上加權) |
| 1 / 0 / 1 | 1,402 | 1.424 | 沒跟著走(被上加權) |
| 0 / 1 / 0 | 1,360 | 1.517 | 沒跟著走(被上加權) |
這張表值得多看兩眼,因為它把「加權到底在做什麼」變成看得見的東西: 權重大於 1 的四群,全部都是 與 不一致的人——指標陽性卻沒治療、 或指標陰性卻治療了,共 6,881 人。他們在真實世界裡是少數, 但在假想世代裡被放大到跟其他人一樣多,好讓「治療」與「指標」脫鉤。 權重小於 1 的四群則相反,是照著指標走的多數人,他們被縮小。
換句話說,加權是靠那些沒有照著臨床邏輯走的病人把干擾撬開的。 這也解釋了為什麼正性(positivity)是這個方法的命門:如果指標陽性的人沒有一個不治療, 那一格就是空的,權重會趨近無限大或者根本估不出來,而你在平均權重上看不見這件事—— 要看 ESS、看最大值、看每一個型態的人數,也就是上面這張表。
動手跑一次:長表、兩階段
實作分兩個階段,中間隔著一份權重。這是 MSM 跟一般迴歸最不一樣的地方: 你會配兩次模型,而第一次配的那個模型的係數,你一眼都不會看—— 它只是拿來算權重的中間產物。
# 產生寬表 d。U 是未測量的干擾——少了它,把 L1 條件化只會過度校正,
# naive 模型看起來就會是對的,整頁的論點會被自己的數字推翻。
set.seed(20260823)
n <- 20000
U <- rnorm(n) # 未測量的干擾,非有不可
A0 <- rbinom(n, 1, 0.5)
L1 <- rbinom(n, 1, plogis(-0.5 + 1.5 * A0 + 1.2 * U))
A1 <- rbinom(n, 1, plogis(-0.5 + 1.2 * L1 + 0.3 * A0))
Y <- rbinom(n, 1, plogis(-1.5 - 0.7 * A0 - 0.7 * A1 + 1.0 * U))
d <- data.frame(A0, L1, A1, Y)
# ── 階段一:權重模型(長表,一人一個 visit 一列)──────────────────────
# d 是寬表:一人一列,欄位 A0 / L1 / A1 / Y。先攤成長表,
# 因為權重是「每個時點各算一次再連乘」,長表讓迴圈只寫一次。
n <- nrow(d)
long <- rbind(
data.frame(id = 1:n, visit = 0, A = d$A0, L = 0, lagA = 0),
data.frame(id = 1:n, visit = 1, A = d$A1, L = d$L1, lagA = d$A0)
)
K <- 1 # 最後一個 visit 的編號
sw <- rep(1, n) # 每個人一個累積權重,從 1 開始連乘
for (k in 0:K) {
dk <- long[long$visit == k, ]
dk <- dk[order(dk$id), ]
# 分子:只放「過去的治療」。分母:再加上「當下的共變項」。
# 分子絕對不能放共變項——放了就等於把要拿掉的干擾又加回來。
num <- glm(A ~ lagA, data = dk, family = binomial())
den <- glm(A ~ lagA + L, data = dk, family = binomial())
pn <- predict(num, type = "response") # P(A = 1 | 過去治療)
pd <- predict(den, type = "response") # P(A = 1 | 過去治療 + 共變項)
# 每個人拿的是「他實際接受的那個治療」的機率,不是 P(A = 1)
sw <- sw * ifelse(dk$A == 1, pn, 1 - pn) / ifelse(dk$A == 1, pd, 1 - pd)
}
# ⚠️ 在這份模擬裡 visit 0 的 lagA 與 L 都是常數,兩個模型都退化成截距模型,
# 那一期的權重因此恰好是 1(R 會把常數項的係數報成 NA,那是預期的)。
# 真實資料的 visit 0 有基線共變項 L0,分母是 A ~ L0,權重不會是 1。
summary(sw) # 平均要接近 1;離 1 很遠就是模型設錯了
sum(sw)^2 / sum(sw^2) # 有效樣本數 ESS
# ── 階段二:加權的結果模型,就是 MSM ─────────────────────────────────
# 左邊是結局,右邊只有治療——沒有 L1。共變項只出現在階段一的分母裡。
# ⚠️ suppressWarnings 是刻意的,不是在蓋掉配壞的模型:加權後
# w_i * y_i 不再是整數,glm 因此丟 non-integer #successes in a binomial glm!
# 點估計是正確的加權 score 解,只有 glm 內部的二項式帳本不高興。
msm <- suppressWarnings(
glm(Y ~ A0 + A1, data = d, family = binomial(), weights = sw)
)
coef(msm)["A0"] + coef(msm)["A1"] # 兩期都治療 vs 兩期都不治療
# ⚠️ 不要讀 summary(msm) 的標準誤。理由見下一節。驗證環境:R 4.6.0 + jsonlite 2.0.0。全部用 base R,不需要任何額外套件;ipw 或 WeightIt 這類套件做的是同一件事的包裝版。
import numpy as np
import pandas as pd
import statsmodels.api as sm
import statsmodels.formula.api as smf
# 產生寬表 d。U 是未測量的干擾——少了它,把 L1 條件化只會過度校正,
# naive 模型看起來就會是對的,整頁的論點會被自己的數字推翻。
rng = np.random.default_rng(20260823)
n = 20000
# numpy 的亂數流與 R 不同,同一個 seed 不會給出同一批人,
# 所以這一側的數字會接近但不等於上面那張表。
expit = lambda x: 1 / (1 + np.exp(-x))
U = rng.standard_normal(n)
A0 = rng.binomial(1, 0.5, n)
L1 = rng.binomial(1, expit(-0.5 + 1.5 * A0 + 1.2 * U))
A1 = rng.binomial(1, expit(-0.5 + 1.2 * L1 + 0.3 * A0))
Y = rng.binomial(1, expit(-1.5 - 0.7 * A0 - 0.7 * A1 + 1.0 * U))
d = pd.DataFrame({"A0": A0, "L1": L1, "A1": A1, "Y": Y})
# d 是寬表 DataFrame,欄位 A0 / L1 / A1 / Y
n = len(d)
long = pd.concat([
pd.DataFrame({"id": range(n), "visit": 0, "A": d.A0, "L": 0, "lagA": 0}),
pd.DataFrame({"id": range(n), "visit": 1, "A": d.A1, "L": d.L1, "lagA": d.A0}),
])
sw = np.ones(n)
for k in (0, 1):
dk = long[long.visit == k].sort_values("id")
# visit 0 的 lagA 與 L 是常數,patsy 會直接把它們吃掉;
# 真實資料的 visit 0 要換成基線共變項。
rhs_num = "lagA" if dk.lagA.nunique() > 1 else "1"
rhs_den = " + ".join(c for c in ("lagA", "L") if dk[c].nunique() > 1) or "1"
pn = smf.glm(f"A ~ {rhs_num}", dk, family=sm.families.Binomial()).fit().fittedvalues
pd_ = smf.glm(f"A ~ {rhs_den}", dk, family=sm.families.Binomial()).fit().fittedvalues
a = dk.A.to_numpy()
sw = sw * np.where(a == 1, pn, 1 - pn) / np.where(a == 1, pd_, 1 - pd_)
# var_weights,不是 freq_weights:權重不是「這一列重複幾次」
msm = smf.glm("Y ~ A0 + A1", d, family=sm.families.Binomial(),
var_weights=sw).fit()
print(msm.params["A0"] + msm.params["A1"])
# 這裡印出來的 bse 同樣不能用,要自己 bootstrapstatsmodels 的 GLM 用 freq_weights 會把權重當成「這一列重複了幾次」,非整數權重要用 var_weights;兩者在點估計上一致,但印出來的標準誤都不能用(理由見下一節)。Python 端沒有等同於 R 的 ipw 套件的成熟實作,長表與權重連乘一律自己寫。
真值不是那個結構係數:非塌縮性
生成 的那條式子裡, 與 的係數各是 -0.70, 合計 -1.40。但這一頁一路在對的真值是 -1.247,不是 -1.40。 兩個數字不一樣,而且不是誰算錯。
差別在於它們是兩個不同的參數:
- -0.70 是條件參數——「在 固定的前提下,治療讓 log odds 變多少」。
- -1.247 是邊際參數——「整個世代都治療 vs 整個世代都不治療,全世代風險的 log odds 差多少」。
odds ratio 是不可塌縮(non-collapsible)的:即使沒有任何干擾, 把一個有預測力的變項(這裡是 )從模型裡拿掉,係數也會往零縮。 這是 odds ratio 這個尺度本身的性質,不是偏誤——風險差與相對風險就沒有這個問題。
MSM 不是魔法,它也是一個模型
上面那張表裡,MSM 離真值還差 0.074。這個殘餘落差有一部分不是雜訊, 是模型設錯——而且錯在這一頁自己寫的那個工作模型上。
那個模型假設 的效果與 的取值無關(右邊只有 與 兩個主效果,沒有交互作用項)。 但真值算出來的兩個對比其實不相等: 固定在不治療時, 的對比是 -0.612; 固定在治療時是 -0.635, 差了 0.022。工作模型把這兩個不同的量當成同一個參數在估, 所以它報出來的東西是兩者的一種折衷。
這件事值得寫出來,因為 MSM 最常被誤解的地方就是這裡:加權處理的是干擾, 它不會替你把結果模型設對。 你仍然要決定要不要放交互作用項、要不要放時間的函數、 劑量要不要當連續變項。權重模型設錯會讓權重失真,結果模型設錯會讓估計目標本身跑掉, 兩個是各自獨立的失敗管道。
變異數為什麼不能直接讀
兩條可用的路:
- Bootstrap(本頁採用)。 重抽個體,而且每一次重抽都要重配權重模型—— 只重跑結果模型、把權重當成算好的常數帶過去,就等於把上面第 2 點的錯誤原封不動搬進 bootstrap 裡。 本頁跑 500 次,表上所有的區間都來自它。
- 穩健(sandwich)標準誤。 便宜、多數軟體按一個選項就有,但它把權重當成已知, 對穩定化權重通常偏保守。本頁也一併算了:MSM 的 是 0.045, bootstrap 是 0.045; 是 0.044 對 0.046。兩者在這份資料上很接近,但這是資料給的,不是通則。
IPCW:另一個加權,而且可以跟 IPTW 相乘
上面處理的是「誰接受了治療」。真實的追蹤資料還有第二個選擇性的問題:誰還留在追蹤裡。 病人失聯、退出、換醫院,而退出的人往往跟留下來的人不一樣——如果退出的機率跟共變項和治療有關, 那麼「還在追蹤裡」本身就是一個被條件化的變項,偏誤的結構跟干擾一模一樣。
處理方式也一模一樣:設限加權(inverse probability of censoring weighting, IPCW)。 形式跟 IPTW 完全平行,只是把「接受治療的機率」換成「在該時點仍未被設限的機率」:
實作上就是多配一個 logistic 模型(結果是「這一期有沒有被設限」), 把每個還在追蹤裡的人依他留下來的機率的倒數加權——留在追蹤裡的人要替那些跟他相似、 但已經退出的人發言。
本頁的模擬沒有設限,所以只示範了 IPTW 那一半。真實資料上少做這一半, 等於默默假設了「退出是完全隨機的」——那是設限與截切那一頁 講的非資訊性設限假設,而在會換藥、會停藥的族群裡,它通常是最站不住的一條。
怎麼讀報表
論文裡的 MSM 通常只留下三、四句話加一張表。要檢查的是五件事:
- 權重模型的變項清單有沒有列出來,分子與分母分開列。 只寫「用 IPTW 校正」等於沒寫。 分子裡混進共變項是常見的實作錯誤,而它不會在任何診斷圖上現形。
- 每個時點都算了權重嗎,還是只算了基線? 只有基線權重的分析不是 MSM, 它是基線 IPTW,處理不了 feedback。
- 權重的分布有沒有報。 至少要有平均值(穩定化權重應該接近 1)、最大值、 以及有效樣本數。只報平均值看不出極端權重;本頁的 ESS 是名目 n 的 92.5%, 實務上掉到五成以下就要回頭檢查重疊。
- 有沒有截尾(truncation),截在哪裡,有沒有做敏感度分析。 截尾是用一點偏誤換變異數, 是可接受的決定,但必須事先講明並附上不截尾的結果。
- 標準誤是怎麼來的。 要看到 bootstrap 或 robust/sandwich 字樣。 什麼都沒寫的加權迴歸,八成報的是那個不能用的標準誤。
還有一件常被跳過的:結果模型的右邊應該只有治療(與必要的基線變項),不應該有時間相依共變項。 看到 MSM 的結果模型裡放著 ,那不是 MSM,那是這一頁第三節在警告的那個分析。
常見誤用
| 誤用 | 為什麼錯 |
|---|---|
| 把時間相依共變項同時放進權重模型與結果模型 | 等於做完加權又條件化一次,過度校正與碰撞子偏誤原封不動回來 |
| 分子裡放共變項 | 那等於把要拿掉的干擾加回去,穩定化的意義消失 |
| 只算基線權重就叫 MSM | 基線加權處理不了「治療改變共變項、共變項再決定下一次治療」 |
| 只報平均權重 | 平均值接近 1 跟有沒有極端值是兩件事,要看最大值與 ESS |
| 直接讀加權迴歸的標準誤 | 權重是估出來的、加權的列不是獨立觀察,區間會太窄 |
| bootstrap 時不重配權重模型 | 把權重的不確定性排除在外,等於白拿一份精確度 |
把 non-integer #successes 當成模型配壞 | 那是加權的預期產物,點估計不受影響 |
| 沒有檢查正性(positivity) | 某個共變項型態下若沒有人接受(或都接受)治療,權重會爆炸或無法估 |
| 忘記 IPCW | 一旦因為偏離方案而設限,設限就是資訊性的,只加 IPTW 不夠 |
| 把 MSM 的係數當成「對一位病人的效果」 | 它是邊際參數,講的是整個世代都治療對整個世代都不治療 |
| 拿 MSM 的 OR 去跟條件模型的 OR 比大小 | 非塌縮性讓兩者本來就不相等,就算完全沒有干擾也不會相等 |
這一頁與其他頁的關係
- 權重這個想法的起點——見 propensity score 加權(IPTW)。 那一頁講的是只有一個時點的版本;權重診斷那一套(平衡表、權重分布、ESS、截尾) 在這裡完全適用,只是要對每一個時點各做一次。
- 箭頭要怎麼畫、要條件在哪些變項上——見 DAG 與干擾的結構。 本頁的碰撞子偏誤就是那一頁的 collider 規則在時間軸上的一個實例。
- Cox 模型裡的時間相依共變項——見 時間相依共變項與長表。
那一頁教的是長表怎麼組、
tmerge()怎麼用,那份資料整理工作是本頁階段一的前置; 兩頁的分歧點在於共變項受不受暴露影響——不受影響就用那一頁的標準 Cox, 受影響就要走這一頁的加權。 - per-protocol 分析的落點——見 目標試驗模擬。 那一頁定義了估計目標(ITT 還是 per-protocol)與時間零點,本頁提供 per-protocol 的估計手段。
- 設限的假設——見設限與截切。IPCW 那一節換掉的是 非資訊性設限這個假設的處理方式,不是取消它。
- 臨床落點——見資料庫研究那一章, 健保申報資料裡的用藥紀錄天生就是時間相依暴露。
重跑本頁的所有數字
/opt/homebrew/bin/Rscript figures/scripts/B6-08-msm.R讀讀看這張圖
答案取自產生本頁圖表的同一份統計輸出,不是另外打上去的。
三條路的合計 log odds ratio:把時間相依共變項放進迴歸 -1.427、完全不校正 -0.975、MSM -1.173,而真值是 -1.247。把那個變項放進迴歸錯在哪?
看答案與解析
正確答案: 它擋掉經由那個變項的真實路徑,又在同一個變項上打開一條假路徑,最後偏離真值 0.180
那個時間相依共變項同時是三種東西:第一期治療的後代、第二期治療的干擾因子,以及它與未測量變項的共同後果。條件在它上面因此同時犯兩個方向相反的錯——擋掉一條真實的因果路徑(過度校正),又打開一條原本被它擋住的假路徑(碰撞子偏誤)。淨結果是偏了 0.180,落在比真值更遠離虛無值的一側,也就是 -1.427 對 -1.247。完全不校正的 -0.975 不是解方,它把第二期的干擾原封不動留著,偏得更多而且方向相反。加權的 -1.173 落在兩者之間,也最接近真值,這正是這份模擬要示範的東西。
面板 A 裡,完全不校正的第一期估計是 -0.622,比 MSM 的 -0.593 更接近真值 -0.624。可以據此說不校正比較好嗎?
看答案與解析
正確答案: 不行。第一期是隨機分派,那一格本來就沒有干擾要處理;同一個分析在第二期估出 -0.353
一個方法在某一格碰巧接近真值,不代表它做對了。第一期治療是隨機分派的,所以「完全不校正」在那一格本來就沒有干擾要處理,-0.622 貼近 -0.624 是設計送的;同一個分析到了第二期就估出 -0.353,而那一期的真值同樣在 -0.624 附近,差距一眼可見。至於 -0.593,那是 MSM 在第一期的估計,它略遠一點,但 MSM 針對的是合計對比那個量,單看一格的排名沒有意義。看單一係數挑贏家,跟看 p 值挑分析是同一種病。
這份模擬的第一期穩定化權重恰好是 1。真實資料能不能照抄這件事?
看答案與解析
正確答案: 不能。這裡第一次治療是隨機分派又沒有基線共變項,分子與分母是同一個模型,比值才恆為 1.00
第一期權重是 1.00,是這份模擬的設定送的:第一次治療隨機分派、而且沒有基線共變項,於是那一期的分子與分母是同一個模型。真實資料沒有這個好運——年齡、共病、疾病嚴重度同時決定第一次治療與結局,那一期需要自己的權重,形式跟第二期一模一樣。把這件事讀成「第一期不用算權重」,就是把整個基線干擾漏掉。1.52 是全體權重的最大值、0.93 是有效樣本數占名目的比例,兩個都在描述權重乖不乖,而權重乖不乖跟第一期要不要建模是兩件事。
同一組分母模型,把分子從邊際治療機率換成 1,權重的範圍從 0.65 到 1.52 變成 2.76 到 7.43。這個變化的重點是什麼?
看答案與解析
正確答案: 有效樣本數掉到名目樣本數的 0.88 倍:權重散開之後少數人的份量變重,估計的變異跟著變大
重點不是權重變大,是變散。平均升到 4.00 只是記帳:分子換成一個常數之後每個人的權重都乘上同一個倍數,樣本數並沒有變多,精確度也沒有變好。真正的代價寫在有效樣本數上——未穩定化只剩名目的 0.88 倍,因為權重的離散程度直接決定了少數人能不能主宰整個分析。4.90 是最大權重的倍率,它是離散的症狀而不是可以忽略的細節;平均值對得上完全不能保證離散度沒問題,這正是要看有效樣本數、看最大值、看每一個型態各有幾個人的理由。這裡的範圍從 0.65 到 1.52 變成 2.76 到 7.43,散開的幅度是看得見的。
生成結果的那條式子裡,兩期治療的係數各是 -0.7。但這一頁一路在對的真值是 -1.247。哪一個說法對?
看答案與解析
正確答案: -1.247 是邊際參數——整個世代都治療對整個世代都不治療,全世代風險的 log odds 差
這兩個數字回答的是不同的問題。其中 -0.700 是條件參數:在未測量變項固定的前提下,治療讓 log odds 變多少。而 -1.247 是邊際參數:整個世代都治療對整個世代都不治療,全世代風險的 log odds 差多少。odds ratio 不可塌縮,把一個有預測力的變項從模型裡拿掉,係數就會往零縮,即使完全沒有干擾也一樣——那是尺度的性質,不是偏誤。MSM 估的是邊際參數,所以它要對的是 -1.247;拿 -1.173 去跟結構係數比,會得到「MSM 也偏了」的錯誤結論,而那個落差有一部分只是尺度造成的。同一個道理在真實論文裡的形式是:校正過的與沒校正的 odds ratio 本來就不會相等。
MSM 的合計估計是 -1.173,真值是 -1.247,差了 0.074;同一次分析重抽出來的合計 bootstrap 標準誤是 0.057。關於這個殘差,哪一個說法站得住?
看答案與解析
正確答案: 殘差只有這個估計自己標準誤 0.057 的一倍多,所以它的大小不能拿來當作「還剩多少偏誤」的度量——這一頁指出的工作模型設錯是從模型假設看出來的,不是從這個差看出來的
先把殘差跟它自己的不確定度放在一起看。合計估計的 bootstrap 標準誤是 0.057,而殘差是 0.074,只有一個標準誤多一點;單一次模擬跑出這個大小的差,雜訊本身就足以解釋,所以這個數字不能拿來度量還剩多少偏誤。這一頁確實指出工作模型設錯了——右邊只有兩個主效果,而真值裡兩個第一期對比 -0.635 與 -0.612 並不相等——但那是從模型假設推出來的,不是從 0.074 讀出來的;而且兩個對比之間的落差比殘差小了一個量級,撐不起「差多少殘差就是多少」。至於權重,標準差 0.284 描述的是權重散不散;加大 bootstrap 的重抽次數只會讓標準誤本身估得更穩,不會縮小估計的抽樣誤差,更不會把點估計拉近真值。
加權迴歸的 summary() 印出來的標準誤為什麼不能直接讀?
看答案與解析
正確答案: 因為它把加權後的每一列當成獨立的二項觀察,又把權重當成已知常數,兩個假設都不成立;表上的 0.0445 是重抽算出來的
加權 glm 的標準誤同時做了兩個不成立的假設:把加權後的列當成獨立的觀察(一個權重 1.5167 的人是一個人,不是一個半的人),以及把權重當成已知的常數(權重是從同一批人估出來的,它自己帶著抽樣誤差)。0.0445 是 bootstrap 的結果,重抽的是個體,而且每一次都把權重模型重配一次;只重跑結果模型、把權重當成算好的常數帶進去,等於把第二個錯誤原封不動搬進 bootstrap 裡。0.0455 是穩健標準誤,那是另一條可用的路,但它同樣把權重當成已知,兩者在這份資料上接近是資料給的,不是通則。至於把權重同除以一個常數,那不會改變任何東西,因為問題不在權重的大小而在它的來源。
用到這個方法的章節
素材來源與授權
本頁為原創內容