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

中斷時間序列與差異中的差異(ITS 與 DiD)

某個政策在某個月上路,之後指標變了——那有多少該記在政策頭上?這一頁用同一份資料把兩種答法並排:ITS 把介入前的趨勢延伸出去當反事實,DiD 向一條政策打不到的對照序列借反事實。也講階躍與斜率的分別、季節性沒先扣掉會誤判成什麼樣、平行趨勢的事前檢查為什麼只能否定不能證明,以及一批真值已知為零的負對照怎麼把殘餘偏誤量出來。

一個政策上路了,然後呢

台灣讀者最容易撞到這個問題形狀的地方有三個:健保把某個藥或某項檢查從自費改成給付、醫院導入一套感染管制 bundle、以及 COVID 期間就醫行為整個位移。三件事都長成同一個樣子——一個時點、一個介入、一條逐月的指標,然後有人問「所以降了多少」。

最省事的答法是前後比:介入前平均這麼多,介入後平均那麼多,相除。這份資料上做出來是每月 873 對 571,比值 0.654(95% CI 0.642–0.665),看起來乾淨俐落。

它的問題不是精度不夠,是它把本來就在走的趨勢整段記在政策頭上。指標如果已經在下降,前後比會把那段下降算成政策的功勞;如果指標有季節性、而介入剛好落在旺季與淡季的交界,前後比連方向都可能講反。

這一頁講的兩個方法,差別只在一句話:反事實從哪裡來

  • 中斷時間序列(interrupted time series, ITS)把介入前的趨勢延伸出去,當作「如果政策沒上路,指標會走到哪裡」。反事實是外插來的。
  • 差異中的差異(difference-in-differences, DiD)找一條政策打不到的對照序列,用它在同一段時間裡的變化當反事實。反事實是來的。

兩者都不是憑空變出對照組。ITS 賭的是「趨勢會延續」,DiD 賭的是「兩條線本來會平行地走下去」。這一頁把兩個賭注放在同一份資料上,讓你看到多一條對照序列到底買到了什麼、又要付什麼。

這一頁用的資料

datasets::Seatbelts 是 base R 內建的,data(Seatbelts) 一行就有:英國 192 個月的道路傷亡月資料,從 1969-01 到 1984-12。它剛好同時提供介入序列與天然對照序列:

  • front——前座傷亡。1983 年的強制繫安全帶法規範的就是前座。
  • rear——後座傷亡。同一批車、同一條路、同一個經濟環境,但法律管不到後座

還有一件事必須先處理:介入前後的行駛里程從每月 14463 變成 18890,多了 30.6%。如果不把它放進模型,「開得比較多所以撞得比較多」會被折進「法律的效果」裡。所以本頁的每個模型都帶 offset(log(kms)),把結果從「每月幾個人」換成「每單位里程幾個人」——這就是 Poisson 迴歸那一頁講的 offset。

本頁最後一節的 empirical calibration 用的是模擬資料,不是 Seatbelts;那一節開頭會再說明一次。

中斷時間序列:把介入前的趨勢延伸出去當反事實

ITS 就是一個帶了兩個特別項的迴歸。用 Poisson 寫成:

logE[Yt]  =  β0  +  β1t  +  β2lawt  +  β3tslt  +  β4Xt  +  log(kmt)\log \mathbb{E}[Y_t] \;=\; \beta_0 \;+\; \beta_1 t \;+\; \beta_2 \,\mathrm{law}_t \;+\; \beta_3 \,\mathrm{tsl}_t \;+\; \beta_4 X_t \;+\; \log(\mathrm{km}_t)
  • tt 是月份序號,β1\beta_1介入前的長期趨勢。這一項就是前後比缺的東西。
  • lawt\mathrm{law}_t 是介入後為 1 的指示變項,β2\beta_2階躍(level change):介入當下那一跳。
  • tslt\mathrm{tsl}_t 是介入後累積的月數(介入前為 0),β3\beta_3斜率變化(slope change):介入之後每個月比介入前多走多少。本頁程式碼裡的 tsl 在介入當月已經是 1,所以 exp(β2)\exp(\beta_2) 是把不連續點外推到 tsl=0\mathrm{tsl} = 0 的那個值,介入後第一個月實際觀察到的比值是 exp(β2+β3)\exp(\beta_2 + \beta_3)。把 exp(β2)\exp(\beta_2) 報成階躍是慣例,但編碼方式會在階躍與斜率之間搬動一小塊,Methods 要寫清楚用的是哪一種。
  • XtX_t 是隨時間變動的共變項,本頁放的是油價。

階躍與斜率回答的是兩個不同的臨床問題。階躍問的是「這件事一發生就改變了什麼」——法規、給付、開關型的介入通常長這樣。斜率問的是「這件事改變了後續的走向」——衛教、訓練、逐步擴散的介入通常長這樣。一個介入可以只有其中一種,也可以兩種都有。

英國前座傷亡的中斷時間序列圖。橫軸是年份,每三年標一格,從 1969 標到 1984;縱軸是每月前座傷亡人數,已標準化到平均行駛里程。淡紅色圓點是每個月的觀察值,介入前多半散布在七百到一千二之間、冬季偏高夏季偏低,介入後掉到五百上下。一條深色垂直虛線畫在法律生效的月份,旁邊標著 law in force 1983-02。深紅色實線是分段迴歸的配適值:虛線左邊那一段隨時間緩緩下滑,在虛線處往下一跳,右邊那一段從比較低的位置起步、而且轉為緩緩往上。灰褐色虛線從左段的末端往右延伸到序列結尾,那是反事實——假如法律沒有上路、介入前的趨勢原樣走下去會落在哪裡;介入後的觀察點與配適線整段都落在它下方。左下角圖例分三列,說明圓點是標準化到平均里程的觀察值、實線是配適的分段迴歸、虛線是延伸出去的介入前趨勢。圖頂兩行說明文字寫出這是跑在 datasets::Seatbelts 上的 Poisson ITS、offset 是 log(kms)、油價固定在平均值,以及階躍 RR 0.680(0.654–0.707)與每月斜率 RR 1.0123。
ITS 的反事實是那條灰褐色虛線:介入前的趨勢原樣延伸出去。階躍 RR 0.680(95% CI 0.654–0.707)量的是實線與虛線在介入當下那一刻的落差。產圖腳本 figures/scripts/B6-10-its-did.R

配適出來的階躍是 RR(rate ratio,發生率比;本頁每一個取過指數的係數都是這個量)0.680(95% CI 0.654–0.707,p < 0.0001),也就是法律上路當下,前座傷亡相對於反事實下降了約 32.0%。斜率變化是每月 RR 1.0123(95% CI 1.010–1.015)——大於 1,代表法律之後的月趨勢比法律之前往上翹

前後比不是中斷時間序列

把上面的模型跟本頁開頭那個前後比並排:

做法估計95% CI它分得開什麼
純前後比(沒有時間項)0.6540.642–0.665什麼都分不開,只有一個數字
ITS(時間 + 階躍 + 斜率)0.6800.654–0.707長期趨勢、階躍、斜率變化各一個係數

兩個數字沒有差很多,這其實是這份資料的運氣——介入前的趨勢本來就相對平緩。真正的差別在最右邊那一欄:前後比連「這是一跳還是一個轉向」都答不出來,而這正是臨床上最想知道的那件事。政策如果只是把指標往下推一格、之後照舊往上爬,跟政策把整條走勢扳彎,後續的決策完全不同。

還有一個看不見的代價:前後比沒有辦法被自己的圖抓錯。ITS 的圖上有一條反事實虛線,讀者可以自己判斷那條線延伸得合不合理;前後比只給兩個平均數,沒有任何東西可以被質疑。

沒先扣掉的東西,會被記在政策頭上

這一節是這一頁最實際的一段。同一個階躍,把校正項一項一項拿掉:

模型階躍 RR95% CI每月斜率 RRAIC
完整校正:油價 + 里程 offset0.6800.654–0.7071.01235204
拿掉油價,保留里程 offset0.6650.640–0.6911.01295330
拿掉里程 offset,保留油價0.6820.657–0.7091.01364719
兩個都不放,只有計數0.6600.635–0.6851.01444988
完整校正 + 日曆月0.7390.711–0.7681.00622941
(對照)純前後比,沒有時間項0.6540.642–0.665

第一件值得注意的事跟直覺不合:把油價與里程 offset 拿掉,估計只在 0.660 到 0.682 之間動了 3.4%,信賴區間大幅重疊。 也就是說,在這份資料上,「你有沒有校正油價」根本改不了結論。這件事本來被寫成本頁的主要教學點,是實際跑過之後被自己的數字推翻的。

真正會改變結論的是季節性。加進日曆月之後,階躍從 0.680 變成 0.739(95% CI 0.711–0.768)——與完整校正模型的區間 0.654–0.707 不重疊。AIC 同時從 5204 掉到 2941,跌了 2263,代表季節性是這份資料裡被忽略掉的最大一塊結構。

原因不神秘:道路傷亡有明顯的年內週期(冬季日照短、天候差,傷亡高),而法律在 1983-02 上路,介入後那 23 個月覆蓋的月份組合跟介入前 169 個月的不一樣。沒有把日曆月扣掉,那塊季節差就被記在法律頭上。

安慰劑序列:法律管不到的那條線

ITS 最大的弱點是它沒有對照組——它的對照是同一群人的過去。所以只要有另一個原因剛好在同一個月改變了指標(另一項政策、一次油價衝擊、統計口徑換了),ITS 分不出來。

Seatbelts 給了一個很乾淨的檢查:把完全一樣的模型跑在 rear 上。後座傷亡受同樣的季節、同樣的油價、同樣的里程影響,但法律不管後座。所以它是一個負對照結果(negative control outcome):如果模型是對的,這裡不應該看到階躍。

序列模型階躍 RR95% CIp
前座(法律適用)完整校正0.6800.654–0.707< 0.0001
後座(安慰劑)完整校正0.9630.919–1.0080.1072
後座(安慰劑)不校正,只有計數0.9530.910–0.9980.0400

完整校正的安慰劑序列給出階躍 RR 0.963,95% CI 0.919–1.008 跨過 1,未達統計顯著。這是這一頁最有價值的一列:法律真的只打到前座,而這件事現在是讀者自己在表上看得到的證據,不是作者的斷言。

措辭要準確:這一列說的是「在這份資料上未偵測到後座的階躍」,不是「後座完全沒有變化」。CI 的下界到 0.919,也就是一個約 8.1% 的真實下降仍然與資料相容。要主張「後座沒有受影響」,需要的是事先設定的等效界線,不是一個跨過 1 的區間。

第三列是附帶的教訓:同一個安慰劑序列,不校正就「顯著」了(RR 0.953,95% CI 0.910–0.998,p = 0.0400)。安慰劑檢定本身也吃模型設定——它不是一個免費的保險,一個設定錯誤的安慰劑檢定會給你假的安心感,也會給你假的警報。

負對照的完整分類(負對照暴露與負對照結果各能反駁什麼、一個負對照不為零時怎麼解讀)在 工具變數那一頁;本頁最後一節談的是同一個想法擴大到幾十個結果之後能做什麼。

差異中的差異:反事實用借的

有了 rear 這條線,可以做的就不只是安慰劑檢定。把兩條序列疊成 long format,加一個組別指示變項,反事實就從「延伸出去」換成「借過來」:

logE[Ygt]  =  α0  +  α1grpg  +  α2lawt  +  α3(grpg×lawt)  +  α4t  +  α5Xt  +  log(kmt)\log \mathbb{E}[Y_{gt}] \;=\; \alpha_0 \;+\; \alpha_1 \,\mathrm{grp}_g \;+\; \alpha_2 \,\mathrm{law}_t \;+\; \alpha_3 \,(\mathrm{grp}_g \times \mathrm{law}_t) \;+\; \alpha_4 t \;+\; \alpha_5 X_t \;+\; \log(\mathrm{km}_t)

α3\alpha_3——那個交互作用項——就是 DiD 估計。它的意思是:治療組在介入前後的變化,減掉對照組在同一段時間裡的變化。任何同時打到兩組的東西(油價、天氣、經濟、統計口徑)在相減時被消掉,這就是多一條對照序列買到的東西。

兩條月傷亡序列疊在同一張圖上。橫軸是年份,每三年標一格,從 1969 到 1984;縱軸是每月傷亡人數,兩條序列都已標準化到平均行駛里程。細的紅色線是前座傷亡的逐月值,位置明顯較高、鋸齒狀的季節起伏很大;細的藍灰色線是後座傷亡的逐月值,位置較低、起伏較小。兩條粗線是各自的十二個月移動平均:粗紅線在整段期間緩緩下滑,粗藍灰線大致持平。一條深色垂直虛線畫在法律生效的月份,旁邊標著 law in force 1983-02。虛線右邊,紅線明顯往下掉了一段而藍灰線沒有跟著掉,兩條線之間原本很寬的落差在那裡收窄。右上角圖例兩列,分別是前座(法律適用)與後座(法律不適用)。圖頂兩行說明文字寫出兩條序列都標準化到平均里程、粗線是十二個月移動平均,以及 DiD 交互作用 RR 0.642(0.624–0.660),前後座之間的落差只在法律之後收窄。
DiD 讀的是兩條線之間那段落差怎麼變。交互作用 RR 0.642(95% CI 0.624–0.660)就是落差在法律前後的變化倍數。產圖腳本 figures/scripts/B6-10-its-did.R

三個係數各自的意思值得逐一拆開,因為論文表格常常只印交互作用那一列,讀者不知道另外兩列被拿去做了什麼:

係數RR95% CI它在講什麼
組別主效果2.1822.162–2.202介入前,前座傷亡是後座的幾倍——兩組本來就不同,DiD 不要求它們相同
時期主效果1.1901.162–1.218對照序列自己在介入後的變化,也就是借來的反事實
交互作用(DiD 估計)0.6420.624–0.660前座相對於後座那個變化,額外多走的部分

組別主效果 2.182 說的是介入前前座傷亡本來就是後座的兩倍多。DiD 不要求兩組的水準相同,它只要求兩組的變化趨勢平行——這是 DiD 與配對、加權那一類方法最不一樣的地方,也是它常被誤解的地方。

平行趨勢:DiD 唯一的識別假設

DiD 用對照序列換掉了外插,代價是換來一個新的假設:平行趨勢(parallel trends)——如果政策沒有上路,兩條序列的差距會維持不變。

這個假設沒有辦法被驗證,因為它講的是一個沒有發生的世界。能做的是事前檢查:把介入前那段切成若干期,各期估一個「治療組相對於對照組」的值,看它們是不是都貼在 1 附近。這就是 event-study 圖。

介入前逐年的前座對後座比值圖。橫軸是介入前的年份,從 1969 逐年標到 1982;縱軸是前座對後座的比率比,以對數尺度呈現。每一年一個點加一條垂直的信賴區間線段;基準年 1982 畫成空心圓、固定在 1 上、沒有區間。一條水平虛線畫在 1。1969 到 1972 這四年的點明顯高於 1,落在 1.143 到 1.174 之間,區間整段在 1 上方;1973 與 1974 逐步下降但區間仍不含 1;1975 之後多數年份的點靠近 1,其中 1975、1976、1981 的區間蓋住 1,而 1977 到 1980 又稍微翹起來、區間再度不含 1。整體形狀不是一條貼在 1 上的平線,而是前段偏高、後段才趨平。圖頂三行說明文字分別寫出這是介入前的組別乘年份交互作用、全部為零的聯合檢定卡方值 129.6、自由度 13、p < 0.0001,10 個非基準年的區間不含 1 而非基準年共 13 個、空心圓是基準年,以及一句提醒:平坦的圖不能證明平行趨勢,只能是沒有反駁它。
介入前逐年的前座對後座比值。13 個非基準年裡有 10 個的區間不含 1,聯合檢定卡方 129.6(13 自由度,p < 0.0001)。這張圖否定了全期的平行趨勢。產圖腳本 figures/scripts/B6-10-its-did.R
前座對後座 RR95% CI區間
19691.1521.098–1.209不含 1
19701.1741.120–1.232不含 1
19711.1431.090–1.199不含 1
19721.1681.114–1.225不含 1
19731.0791.029–1.131不含 1
19741.0571.007–1.110不含 1
19751.0030.954–1.054含 1
19761.0380.988–1.092含 1
19771.0971.044–1.154不含 1
19781.0891.036–1.144不含 1
19791.0891.036–1.144不含 1
19801.0561.005–1.110不含 1
19811.0180.968–1.070含 1
1982基準年(固定為 1)

結果很明確,而且對這一頁是好消息:13 個非基準年裡有 10 個的區間不含 1,全部為零的聯合檢定是卡方 129.6(13 自由度,p < 0.0001)。1969 到 1972 那四年,前座對後座的比值是基準年的 1.143 到 1.174 倍;要到 1975 之後才大致趨平。

所以本頁的 DiD 估計 0.642 不能被寫成比 ITS 的 0.680 更可信的數字。 誠實的寫法是:事前檢查否定了全期的平行趨勢,只有介入前的後段勉強站得住;如果要用 DiD,應該把樣本限縮到後段、或改用允許趨勢差異的設定,並且把這個決定寫進 Methods。

怎麼算

四個模型全部用 base R 的 glm(),沒有任何額外套件。

data(Seatbelts)
d <- as.data.frame(Seatbelts)
d$time <- seq_len(nrow(d))
d$mon  <- (d$time - 1) %% 12 + 1
d$month <- factor(d$mon)

# 介入 index 從資料推導,不要寫死月份。公文上的生效日與資料庫裡
# 真正開始變化的月份常常差一到兩個月。
law_idx <- which(d$law == 1)[1]

# 介入後累積的月數,介入前為 0。這個編碼讓 law 的係數是「介入當下
# 那一跳」、tsl 的係數是「之後每個月的斜率變化」。原點挪一個月會
# 在兩個係數之間搬動數值,所以要固定寫法。
d$tsl <- pmax(0, d$time - law_idx + 1)

# --- 1. ITS:前座 ---------------------------------------------------
its <- glm(front ~ time + law + tsl + PetrolPrice + offset(log(kms)),
           data = d, family = poisson())
exp(cbind(RR = coef(its), confint.default(its)))["law", ]

# --- 2. 季節性:加日曆月。本頁真正會改變結論的那一項 -----------------
its_season <- glm(front ~ time + law + tsl + PetrolPrice + month +
                    offset(log(kms)), data = d, family = poisson())
exp(coef(its_season)["law"])

# --- 3. 安慰劑序列:一模一樣的模型跑在 rear 上 -----------------------
# 法律不管後座。這裡出現階躍就代表模型抓到的不只是法律。
placebo <- glm(rear ~ time + law + tsl + PetrolPrice + offset(log(kms)),
               data = d, family = poisson())
exp(cbind(RR = coef(placebo), confint.default(placebo)))["law", ]

# --- 4. DiD:兩條序列疊成 long format -------------------------------
long <- rbind(
  data.frame(y = d$front, grp = 1L, d[, c("time", "law", "kms", "PetrolPrice")]),
  data.frame(y = d$rear,  grp = 0L, d[, c("time", "law", "kms", "PetrolPrice")])
)
did <- glm(y ~ grp * law + time + PetrolPrice + offset(log(kms)),
           data = long, family = poisson())
exp(cbind(RR = coef(did), confint.default(did)))["grp:law", ]

# --- 5. 平行趨勢的事前檢查(event study)----------------------------
# 只用介入前的資料,逐年估一個 grp x year 交互作用。
pre <- subset(long, law == 0 & time < law_idx)
pre$year <- 1969 + (pre$time - 1) %/% 12
pre$yf <- relevel(factor(pre$year), ref = "1982")   # 最後一個完整的介入前年
es <- glm(y ~ grp + yf + grp:yf + PetrolPrice + offset(log(kms)),
          data = pre, family = poisson())

# 聯合檢定:所有介入前交互作用同時為零。拒絕就是否定平行趨勢;
# 不拒絕「不」等於平行趨勢成立。
es_null <- glm(y ~ grp + yf + PetrolPrice + offset(log(kms)),
               data = pre, family = poisson())
anova(es_null, es, test = "LRT")

驗證環境:R 4.6.0 + jsonlite 2.0.0。Seatbelts 是 base R 內建資料集,data(Seatbelts) 一行即得。

ITS 與 DiD 並排:多一條對照序列買到什麼

ITSDiD
反事實從哪來介入前的趨勢外插出去向對照序列借
核心假設沒有介入的話,介入前的趨勢會延續沒有介入的話,兩組的差距會維持不變
本頁的估計0.680(0.654–0.707)0.642(0.624–0.660)
假設可以事前檢查嗎不能直接檢查;靠安慰劑序列與多個介入時點旁證可以做 event study,但只能否定不能證明
本頁的檢查結果安慰劑序列未偵測到階躍,與「只打到前座」相容事前檢查否定了全期的平行趨勢
什麼情況下會壞掉同時有另一個介入、指標定義改版、趨勢本來就要轉折對照組被政策間接影響(溢出)、兩組面對的衝擊不同,或各單位在不同時點上路(見上面的 staggered adoption 警告)

兩個估計差了 0.038,DiD 那個離 1 更遠,也就是降幅更大(0.642 對 0.680)。差距的來源看得到:DiD 的時期主效果 1.190 大於 1,也就是借來的反事實說「介入後這段期間本來應該上升」,於是前座相對於它就要多降一點。

這不代表 DiD 比較好。 它代表兩個方法對「本來會怎樣」給了不同的答案,而在這份資料上,DiD 那個答案背後的假設沒有通過事前檢查。多一條對照序列買到的是「共同衝擊被消掉」,付出的是「兩條線的相對走勢必須穩定」——而後者在這裡站不住。

實務上常見的做法是兩個都報:ITS 當主分析、DiD 當敏感度分析(或反過來),並且說明兩者的假設不同,一致才是支持,不一致就要說明差在哪。只報其中一個而不提另一個,讀者沒有辦法判斷結論對方法的選擇有多敏感。

負對照與 empirical calibration

單一個負對照結果(像上面的 rear)能回答「有沒有偏誤」。一批負對照可以回答更進一步的問題:偏誤有多大、往哪個方向

兩欄的模擬結果圖。左欄標題寫著這是模擬的負對照面板,附註列出隨機種子與樣本數。橫軸是估計的比率比、對數尺度,縱軸是標準誤且方向反轉,所以精確的估計落在上方——這是負對照面板慣用的畫法。39 個藍灰色實心圓是負對照的估計,它們的真值全部是 1,但整團明顯偏在 1 的右邊。一條深色實線畫在 1 的位置,另一條紅色虛線畫在配適出來的虛無分布中心,旁邊標著 null centre RR 1.316;兩條線之間的距離就是殘餘偏誤。一個紅色實心三角形是目標結果的估計,位置比所有負對照更靠右。左下角圖例四列,分別是負對照(真值 1)、目標結果(真值 2.0)、RR 等於 1 這條「負對照本來該落在哪裡」的線,以及配適出來的虛無中心即殘餘偏誤。右欄標題是目標結果,畫成兩列的森林圖:上面一列是未校正的估計 2.99,區間 2.72 到 3.29,整段落在真值右側;下面一列是校正後的估計 2.27,區間 1.77 到 2.91,明顯較寬而且蓋住了真值。一條垂直虛線畫在真值 2.0 上並標著 true RR。右欄的附註兩行寫出校正把區間拉寬了 2.6 倍、負對照的涵蓋率從 12.8% 變成 94.9%。
模擬。左:39 個真值為 1 的負對照,整團偏在 1 的右邊,配適出來的中心是 RR 1.316。右:把那個偏移量扣回去之後,目標結果的區間變寬 2.6 倍,並且蓋住真值 2.0。產圖腳本 figures/scripts/B6-10-its-did.R

左圖那團點是這一節的全部論點。39 個結果的真值都是 1,如果分析是無偏的,它們應該散布在 1 的兩側;實際上整團偏到右邊,配適出來的虛無分布中心是 RR 1.316,偏離 1 達 31.6%,離散程度(log 尺度的 tau)是 0.117。

更刺眼的是這一個數字:39 個負對照裡有 34 個的 95% CI 不含 1——換句話說,如果照常規讀 p 值,這 34 個「顯著關聯」你會全部相信,而它們每一個的真值都是 1。名目上 95% 的區間,實際涵蓋率只有 12.8%。

未校正校正後名目值 / 真值
負對照的 95% CI 涵蓋率12.8%94.9%95.0%
目標結果的 RR2.992.272.0
目標結果的 95% CI2.72–3.291.77–2.91要蓋住 2.0 才算對

校正的做法本身很簡單:把每個估計在 log 尺度上減掉那個偏移量 mu,把標準誤改成原本的 se 與虛無分布離散度 tau 的平方和開根號。結果是三件事同時發生:

  1. 負對照的涵蓋率從 12.8% 回到 94.9%,也就是名目值該有的樣子。
  2. 目標結果從 2.99(95% CI 2.72–3.29,不含真值 2.0)變成 2.27(95% CI 1.77–2.91,蓋住真值)。
  3. 代價是區間寬了 2.6 倍。校正不會讓你更有把握,它讓你的把握變誠實。

讀一篇政策評估論文要問的問題

看到「某政策上路後 X 下降了多少」這種標題時,四個問題可以問掉大部分的疑慮:

  1. 對照組是誰? ITS 的對照是同一群人的過去,DiD 的對照是另一群人的同期。沒有講清楚是哪一種,那個數字就沒有辦法評價。用了 DiD 的話,對照組憑什麼被政策打不到,要寫得出理由。
  2. 平行趨勢查了沒? 有沒有 event-study 圖,還是只有一句「我們檢查過平行趨勢且未達顯著」。後者是把檢定力不足讀成證據。
  3. 季節性與長期趨勢扣掉了沒? 本頁的例子裡,只差一個日曆月項就讓階躍從 0.680 走到 0.739,兩個區間不重疊。
  4. 有沒有負對照或安慰劑序列? 一個「法律管不到」的結果或族群,是最便宜也最有說服力的檢查。

另外兩個常被跳過的細節:介入時點是怎麼定的(公文日期與資料庫裡的轉折常常差一到兩個月),以及有沒有處理自相關(沒有的話標準誤偏窄,區間比實際窄)。

常見誤用

誤用為什麼錯
用前後平均值相比就宣稱政策效果把介入前本來就在走的趨勢整段記在政策頭上
只報階躍不報斜率讀者會以為那個降幅一直維持,實際上可能在觀察窗內就回升
介入後只有幾個月就報斜率變化斜率是這個模型裡最脆弱的估計,短觀察窗撐不住
不校正季節性介入後的觀察窗只要不是整數個年,季節差就會被記在政策頭上
介入月份照公文日期寫死資料庫裡真正的轉折常差一到兩個月,過渡月會被塞到錯的一邊
把 DiD 的「兩組水準不同」當成問題DiD 要求的是趨勢平行,不是水準相同
event-study 圖平坦就宣稱平行趨勢成立那張圖能否定假設,不能證明假設;不顯著也可能只是檢定力不足
事前檢查失敗仍照原樣報 DiD至少要限縮期間或改設定,並把決定寫進 Methods
對照序列其實會被政策間接影響溢出效應會把真實效果稀釋掉,DiD 的估計會偏向虛無
各單位上路時間不同時仍套本頁的 2×2 DiD錯開處理時點下,two-way fixed effects 會拿「已經被處理過的單位」當對照;效果隨時間變化時可能產生負權重、甚至方向相反的估計
沒有檢查殘差自相關標準誤偏樂觀,區間比它該有的窄
把兩條序列疊成長表就直接讀 GLM 的標準誤同一個月的那兩列不是獨立觀察,區間會偏窄
只報 ITS 或只報 DiD,不提另一個兩者假設不同,讀者無法判斷結論對方法選擇有多敏感
負對照序列未達顯著就說「完全沒有影響」未偵測到不等於不存在;要主張相當需要事先設定的等效界線
資料庫研究不放任何負對照殘餘偏誤在這類資料裡是常態,沒有負對照就沒有辦法量它

這一頁與其他頁的關係

  • 臨床上會用到它的地方——見資料庫與健保申報資料研究。跨年度的趨勢分析必須查給付政策沿革,而政策變更本身就是一個可以用本頁方法估計的介入。
  • 底層的模型——見 Poisson 迴歸與發生率。本頁的每一個模型都是帶 offset 的 Poisson GLM,係數的指數就是 RR。
  • 另一種「自己當自己對照」的設計——見自我對照設計。它把對照放在同一個人的其他時間窗內,與 ITS 用整條序列的過去當對照是同一個家族的兩種切法。
  • 反事實與識別假設的語言——見因果圖與後門準則目標試驗模擬。平行趨勢是一個關於反事實的假設,跟可交換性一樣不可驗證。
  • 負對照的完整分類——見工具變數
  • 時間相關的陷阱——見時間相依共變項。政策評估的資料常常同時有時間相依的暴露與時間相依的干擾。

重跑本頁的所有數字

/opt/homebrew/bin/Rscript figures/scripts/B6-10-its-did.R

讀讀看這張圖

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

同一個階躍,把油價與里程 offset 拿掉,估計只在 0.660 到 0.682 之間動;把日曆月加進去,階躍卻從 0.680 變成 0.739。這說了什麼?

看答案與解析

正確答案: 季節性才是被忽略掉的最大一塊:加進日曆月之後的 0.739,區間與完整校正模型不重疊

道路傷亡有明顯的年內週期,而法律在二月上路,介入後那二十三個月覆蓋的月份組合跟介入前不一樣——沒有把日曆月扣掉,那塊季節差就被記在法律頭上。所以階躍從 0.680 移到 0.739,而且兩個區間不重疊。反過來,0.660 到 0.682 這一整段是油價與里程三個變體的全部影響,區間大幅重疊,在這份資料上「有沒有校正油價」根本改不了結論。這一段的教訓不是「多校正比較安全」,而是「要校正對的東西」——哪一項重要由資料的性質決定,而任何以月為單位的健康指標幾乎都有季節性。

把完全一樣的模型跑在後座傷亡(法律管不到的那條序列)上,階躍是 0.963,95% CI 從 0.919 到 1.008。這一列該怎麼寫?

看答案與解析

正確答案: 「在這份資料上未偵測到後座的階躍」,並且注意下界到 0.919,一個約 8% 的真實下降仍然與資料相容

這一列是這一頁最有價值的一列,但它說的是「未偵測到」而不是「沒有」。0.963 的區間跨過虛無值,下界到 0.919,也就是一個接近一成的真實下降仍然與資料相容;要主張後座沒有受影響,需要的是事先設定的等效界線。0.953 是同一條安慰劑序列在不校正的模型下的結果,它的區間不含虛無值,也就是「顯著」了——安慰劑檢定本身也吃模型設定,一個設定錯誤的安慰劑檢定會給你假的安心感,也會給你假的警報,而不是法律溢出的證據。

介入前逐年的前座對後座比值,13 個非基準年裡有 10 個的區間不含虛無值。這對 DiD 的估計說了什麼?

看答案與解析

正確答案: 事前檢查否定了全期的平行趨勢——最早那一年已經是基準年的 1.152 倍——DiD 不能寫成比 ITS 可信

event study 檢查的是「介入前兩條線的相對走勢穩不穩定」,而 13 個非基準年裡有 10 個的區間不含虛無值,最早那一年的比值高到 1.152 倍,要到後段才大致趨平。這否定了全期的平行趨勢,所以 0.642 不能被寫成比 ITS 的 0.680 更可信的數字。但「否定」不等於「完全失效」:誠實的作法是把樣本限縮到趨勢站得住的後段、或改用允許趨勢差異的設定,並且把這個決定寫進 Methods。反過來,介入前的形狀也不是與 DiD 無關的背景——DiD 的反事實整個是向對照序列借來的,借得成不成立就靠這張圖。

ITS 估出階躍 0.680,DiD 估出 0.642。這個差距是從哪裡來的?

看答案與解析

正確答案: 從借來的反事實:DiD 的時期主效果是 1.190,也就是對照序列說「介入後這段期間本來應該上升」,於是前座相對於它就要多降一點

兩個方法對「本來會怎樣」給了不同的答案。ITS 把介入前的趨勢外插出去,DiD 向對照序列借,而對照序列說介入後這段期間本來應該上升——時期主效果 1.190 大於虛無值——於是前座相對於它就要多降一點,0.642 比 0.680 離虛無值更遠。2.182 是組別主效果,也就是前座與後座那個固定的差距,那正是 DiD 在相減時消掉的東西,不會進到政策效果裡。1.012 是 ITS 的每月斜率變化——介入後的月趨勢相對於介入前多走多少——而 DiD 的模型裡也有時間項,兩者不是「有沒有建模」的差別。差距本身不代表哪一個比較好,它代表兩個方法的假設不同,而在這份資料上 DiD 那個假設沒有通過事前檢查。

39 個真值都等於虛無值的負對照結果,配適出來的虛無分布中心是 1.316。這說了什麼?

看答案與解析

正確答案: 名目 95% 的區間實際只涵蓋了 0.128 的負對照——照常規讀 p 值會把大批虛無結果當成顯著

如果分析是無偏的,39 個真值等於虛無值的估計應該散布在虛無值的兩側;實際上整團偏到同一邊,配適出來的中心是 1.316。這正是系統性偏誤的樣子——0.117 是那團點的離散程度,它比偏移量小,反而說明這團點是一起偏的,不是各自亂跳。後果寫在涵蓋率上:名目 95% 的區間實際只涵蓋 0.128 的負對照,也就是絕大多數真值等於虛無值的結果會被讀成顯著關聯。這正是資料庫研究的處境:一個未測量的干擾因子同時影響暴露與每一個結果,而分析者看不到它。

把偏移量扣回去之後,目標結果從 2.99 變成 2.27,而它的真值是 2。校正買到了什麼、付出了什麼?

看答案與解析

正確答案: 買到的是誠實的把握:校正後的下界降到 1.77,區間終於蓋住真值,代價是區間寬了兩倍多

校正做的是兩件事:把每個估計在 log 尺度上減掉那個偏移量,並把標準誤改成原本的標準誤與虛無分布離散度的平方和開根號。結果是未校正的 2.99(下界 2.72,整段落在真值的右側)變成 2.27(下界 1.77),區間蓋住真值,而且負對照的涵蓋率回到名目值。代價是區間寬了 2.64 倍——這不是把訊號稀釋掉,而是把原本被低估的不確定性還原。校正不會讓你更有把握,它讓你的把握變誠實;一個看起來很窄卻蓋不住真值的區間,窄是假的。

純前後比給出 0.654,區間 0.642 到 0.665;ITS 的階躍是 0.680,區間 0.654 到 0.707。兩個數字很接近,可以說前後比夠用了嗎?

看答案與解析

正確答案: 不行。前後比只給一個數字,答不出這是一跳還是一個轉向;ITS 另外估了每月 1.012 的斜率變化

兩個數字接近是這份資料的運氣——介入前的趨勢本來就相對平緩。真正的差別不在數值而在「分得開什麼」:前後比只有兩個平均數,它答不出政策是把指標往下推一格、還是把整條走勢扳彎,而這兩件事對後續決策的意義完全不同。ITS 除了階躍 0.680 之外,另外估了 1.012 這個斜率變化——介入後的月趨勢相對於介入前多走多少;介入後本身的月斜率要把時間項與這一項合起來看——那是前後比在結構上就看不到的東西。0.009 是前後比的標準誤,它小既不是優點也不是可以事後修的東西——把區間放寬並不會生出一個斜率係數。還有一個看不見的代價:前後比沒有辦法被自己的圖抓錯,而 ITS 的圖上有一條反事實虛線,讀者可以自己判斷它延伸得合不合理。

素材來源與授權

本頁為原創內容

回報內容問題

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

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

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

一併送出的資訊

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