この連載について
因果推論を「本を読んだ」で終わらせず、自分の言葉で説明でき、コードで再現できる状態まで落とす30日連載です。直前のDay 17では、教育年数と賃金の間に残る観測できない交絡を、外生の「てこ」で外す操作変数法と2SLSを組みました。そこで出会ったのが、除外制約というデータでは検証できない仮定です。今日から2日は、時間方向の比較で交絡に立ち向かう定番デザインに入ります。実データはカリフォルニア州の臓器提供制度変更(organ_donations)です。
TL;DR(3行)
- DID=前後差の引き算を2回。 カリフォルニアの前後差 −0.85ポイントから、他26州の前後差 +1.39ポイント(時代の波)を引くと、制度変更の効果は −2.2ポイント。登録を増やすはずの変更が、逆に登録率を下げていました。
- 鍵は平行トレンド仮定。 水準の一致は要りません(カリフォルニア27% vs 他州44%のままでよい)。要求するのは「処置がなければ同じだけ動いた」という動きの一致で、これは反事実についての仮定なので検証できません。
- TWFE回帰は同じ −0.0225 をクラスタSE付きで返します。 イベントスタディでは処置前の係数がゼロ近傍、処置後は3四半期そろって約 −2ポイント。平行トレンドを示唆しますが、証明はしません。
今日の問い
政策を入れた州の前後比較には、政策の効果と「時代の波」が混ざる。かといって導入州と未導入州をある時点で比べると、今度はもともとの水準の違いが混ざる。
前後差を2回引くだけの素朴な計算が因果効果になるのは、どんな条件が成り立つときか?
答えの中心が平行トレンド仮定です。今日はまず「なぜ2回引くと交絡が消えるのか」を言葉と式にして、次に実データで手計算の2×2 DID、同じ答えを返すTWFE回帰、そして仮定の検分に使うイベントスタディまでをコード化します。
概念① 差分の差分:時代の波を対照群に測らせる
2011年7月、カリフォルニア州は運転免許の手続きでの臓器提供の意思確認を変えました。それまでは登録したい人だけがチェックするopt-in方式でしたが、変更後は「登録する」「今回は登録しない」を全員に明示的に選ばせる方式(active choice)になりました。選択を迫れば登録は増えるだろう、という期待の変更です。効果を知りたいとき、素朴な前後比較には罠があります。変更の前後で登録率が動いたとして、その動きには制度の効果だけでなく、全国的な啓発キャンペーンや季節性のような時代の波が混ざるからです。
そこで、制度を変えていない州に時代の波を測ってもらいます。処置群(カリフォルニア)の前後差は「効果+波」、対照群(他の州)の前後差は「波だけ」。引き算すれば波が消え、効果が残ります。これが**差分の差分法(DID, difference-in-differences)**です。
$$
\hat{\tau}{\text{DID}} = \underbrace{\bigl(\bar{Y}{\text{CA,後}} - \bar{Y}{\text{CA,前}}\bigr)}{\text{効果+時代の波}} - \underbrace{\bigl(\bar{Y}{\text{他州,後}} - \bar{Y}{\text{他州,前}}\bigr)}_{\text{時代の波}}
$$
この引き算が成り立つ条件が平行トレンド仮定です。潜在的結果の言葉で書くと、
$$
E\bigl[Y_{\text{後}}(0) - Y_{\text{前}}(0) ,\bigm|, \text{処置群}\bigr] = E\bigl[Y_{\text{後}}(0) - Y_{\text{前}}(0) ,\bigm|, \text{対照群}\bigr]
$$
「処置がなかった世界では、処置群も対照群と同じだけ動いた」という主張です。注目してほしいのは、この式が要求していないものです。水準の一致は要りません。 カリフォルニアの登録率が27%で他州平均が44%でも構いません。時点をまたがない水準の差は、1回目の引き算(各群の前後差)で消えるからです。Day 6 の非交絡性が「処置群と対照群は $X$ でそろえれば同質」を求めたのに対し、DIDは「異質でよい。ただし動きだけは同じ」まで要求を下げています。そのかわり、左辺の $Y_{\text{後}}(0)$ は処置群では観測されない反事実なので、この仮定はデータからは検証できません。Day 6 の非交絡性、Day 17 の除外制約に続き、これで3回目の「検証できない仮定」との付き合いです。
概念② 2元固定効果(TWFE):引き算を回帰で書き直す
手計算の引き算は、回帰の形にも書けます。州 $i$、四半期 $t$ のパネルデータで、
$$
Y_{it} = \alpha_i + \gamma_t + \tau, D_{it} + \varepsilon_{it}
$$
を推定します。$\alpha_i$ は州の固定効果で、州ごとの水準の違い(カリフォルニアはもともと低い、など)をまるごと吸収します。$\gamma_t$ は時点の固定効果で、全州に共通の時代の波を吸収します。残った $D_{it}$(処置群×処置後だけ1になるダミー)の係数 $\tau$ が、DIDの推定値です。州と時点という2方向の固定効果を入れるので、**2元固定効果(TWFE, two-way fixed effects)**と呼びます。2群2期の設定では、$\tau$ は手計算の2×2 DIDと代数的に一致します。回帰で書く利点は、標準誤差が出ること、共変量を足せること、そして多期間への一般化がそのまま書けることです。
標準誤差にはひとつ作法があります。同じ州の観測は四半期をまたいで似ています(州ごとの体質や制度は簡単には変わりません)。162行を独立なデータとして扱うと、実際より情報が多いふりをした過信の標準誤差が出ます。そこで誤差の相関を州の中では自由に許すクラスタロバスト標準誤差を使い、実質的な独立の単位を「行」ではなく「州」に落とします。パネルデータの推測の定番です。
手を動かす①:2×2の手計算、4つの平均を2回引く
データは organ_donations です。Kessler & Roth がカリフォルニアの制度変更を検証した研究のデータで、Huntington-Klein『The Effect』の教材として公開されています。27州の臓器提供登録率を、2010年Q4から2012年Q1まで四半期ごとに記録したパネルです。制度変更は2011年7月なので、2011Q3以降が処置後になります。まず4つの平均を出して2回引きます。
import pandas as pd
from causaldata import organ_donations
d = organ_donations.load_pandas().data
d["ca"] = (d.State == "California").astype(int) # 処置群=カリフォルニア
d["post"] = (d.Quarter_Num >= 4).astype(int) # 2011年Q3以降=制度変更後
d["treat"] = d["ca"] * d["post"]
print("パネル: %d州 × %d四半期 = %d行" % (d.State.nunique(), d.Quarter_Num.nunique(), len(d)))
m = d.groupby(["ca", "post"])["Rate"].mean()
print("処置前 カリフォルニア %.4f / 他26州 %.4f" % (m[1, 0], m[0, 0]))
print("処置後 カリフォルニア %.4f / 他26州 %.4f" % (m[1, 1], m[0, 1]))
print("前後差 カリフォルニア %+.4f / 他26州 %+.4f"
% (m[1, 1] - m[1, 0], m[0, 1] - m[0, 0]))
print("DID = (%+.4f) - (%+.4f) = %+.4f"
% (m[1, 1] - m[1, 0], m[0, 1] - m[0, 0],
(m[1, 1] - m[1, 0]) - (m[0, 1] - m[0, 0])))
パネル: 27州 × 6四半期 = 162行
処置前 カリフォルニア 0.2713 / 他26州 0.4449
処置後 カリフォルニア 0.2628 / 他26州 0.4589
前後差 カリフォルニア -0.0085 / 他26州 +0.0139
DID = (-0.0085) - (+0.0139) = -0.0225
カリフォルニアの前後差は −0.85ポイント。これだけ見ると「少し下がった」です。でも同じ期間、制度を変えていない26州は +1.39ポイント上がっていました。時代の波は上向きだったのです。波に乗れていれば28.5%になるはずだったカリフォルニアが実際は26.3%。差し引き **−2.2ポイント(−0.0225)**が制度変更の効果です。登録率27%の州で2.2ポイントは、相対で約8%の減少です。登録を増やすはずのactive choiceが逆に減らした、という行動経済学的にも面白い結果で、「今回は登録しない」という明示的な断り方が用意されたことが効いたと解釈されています。登録率が下がったという結果を、私はしばらく信じられませんでした。選択を迫れば増えるという直感が強すぎて、減る側の説明を自分で用意できなかったからです。断りの選択肢を明示的に置くことは、断る許可を配ることでもあります。そう思えるまでに少し時間がかかりました。下の図の左が時系列、右がいま計算した4点の幾何です。
手を動かす②:TWFE回帰で同じ答え、クラスタSEを添えて
同じ推定を概念②の回帰で書きます。州ダミーと四半期ダミーを入れ、処置群×処置後のダミー treat の係数を読みます。標準誤差は州でクラスタリングします。
import statsmodels.formula.api as smf
twfe = smf.ols("Rate ~ treat + C(State) + C(Quarter_Num)", data=d).fit(
cov_type="cluster", cov_kwds={"groups": d["State"]})
ci = twfe.conf_int().loc["treat"]
print("TWFEの処置効果 = %+.4f(手計算のDIDと一致)" % twfe.params["treat"])
print("クラスタSE = %.4f, 95%%CI [%+.4f, %+.4f]" % (twfe.bse["treat"], ci[0], ci[1]))
TWFEの処置効果 = -0.0225(手計算のDIDと一致)
クラスタSE = 0.0067, 95%CI [-0.0356, -0.0093]
係数は −0.0225 で、手を動かす①の引き算と一致します。これは偶然ではなく、2群2期のDIDとTWFEは同じ計算の別表記だからです。回帰にした見返りが区間で、95%信頼区間はゼロを含みません。固定効果が何を吸収したかも確認しておきます。州固定効果が「カリフォルニア27% vs 他州44%」という水準差を、四半期固定効果が「+1.39ポイントの時代の波」を引き受け、treat の係数には前後差の差だけが残りました。回帰は引き算の自動化です。
手を動かす③:イベントスタディでプレトレンドを見る
平行トレンドは検証できません。それでも黙って信じる必要はなく、処置前のデータで検分はできます。処置ダミーを1本にまとめず、カリフォルニア×四半期のダミーに分解して、各四半期の「対照州との差の動き」を推定します。基準には制度変更の直前の四半期(2011Q2)を取り、係数はすべて「2011Q2との比較」で読みます。これをイベントスタディと呼びます。もし処置前から2群の動きがズレていたなら、処置前の係数がゼロから離れて現れるはずです。
for q in [1, 2, 4, 5, 6]: # 基準は 2011Q2(Quarter_Num=3、制度変更の直前)
d["ca_q%d" % q] = d["ca"] * (d.Quarter_Num == q).astype(int)
es = smf.ols("Rate ~ ca_q1 + ca_q2 + ca_q4 + ca_q5 + ca_q6"
" + C(State) + C(Quarter_Num)", data=d).fit(
cov_type="cluster", cov_kwds={"groups": d["State"]})
labels = {1: "2010Q4", 2: "2011Q1", 4: "2011Q3", 5: "2011Q4", 6: "2012Q1"}
for q, name in labels.items():
b, se = es.params["ca_q%d" % q], es.bse["ca_q%d" % q]
print("%s: %+.4f (95%%CI [%+.4f, %+.4f])" % (name, b, b - 1.96 * se, b + 1.96 * se))
2010Q4: -0.0029 (95%CI [-0.0139, +0.0080])
2011Q1: +0.0063 (95%CI [+0.0014, +0.0112])
2011Q3: -0.0216 (95%CI [-0.0324, -0.0107])
2011Q4: -0.0203 (95%CI [-0.0299, -0.0107])
2012Q1: -0.0222 (95%CI [-0.0437, -0.0006])
処置前の2本は −0.3ポイントと+0.6ポイントでゼロ近傍、処置後は3四半期そろって約 −2ポイントです。効果が制度変更の瞬間に現れ、その後も持続している形で、DIDの結論を支えます。正直に見ておきたい点がひとつあります。2011Q1の係数+0.0063は、区間がゼロをわずかに外れています。大きさは処置後の3分の1以下なので結論は揺らぎませんが、プレトレンド検査を「ゼロを含むか」の合否スタンプで読むと、こういう小さなズレの扱いに困ります。見るべきは係数の大きさとパターンです。処置前が小さく、処置時点で段差ができ、処置後が安定して大きい。この形が読めたら合格、が実務の読み方です。
つまづき・誤解しやすい点
- 「処置前が平行だった」は平行トレンドの証明ではありません。 仮定が語っているのは処置後の反事実(処置がなかったらどう動いたか)で、検査できるのは処置前だけです。たとえばカリフォルニアだけに効く景気ショックが2011年Q3にたまたま来ていたら、プレトレンドが完璧でもDIDはその影響を効果に混ぜます。しかも「水準で平行」と「対数で平行」は水準が違う2群では同時に成り立たないので、平行トレンドは目盛りの取り方にも依存します。対照群の説得力そのものを設計で上げる道は、次回Day 19で扱います。
- 多期間・段階導入ではTWFEが壊れます。 今日の設定は処置群1つ・切替時点1つのきれいな2×2でした。ところが州ごとに導入時期がずれる段階導入(staggered adoption)でTWFEを回すと、係数は「すでに処置済みの群を対照扱いする比較」まで重みに混ぜた奇妙な加重平均になり、効果が時間とともに変わる場合には符号が裏返ることさえあります。近年はCallaway & Sant'Annaなどの改良推定量が定番になりつつありますが、本連載は体系の幹を優先してSeason 2に送ります。段階導入のDIDを見たら「TWFE一本で大丈夫か」と疑う、を今日の持ち帰りにしてください。
- 処置クラスタが1つのときの標準誤差は割り引いて読みます。 クラスタSEはクラスタ数が十分多いときに頼れる近似で、処置されたのがカリフォルニア1州だけの今日の設定は、その条件のいちばん苦しい端です。区間 [−0.0356, −0.0093] は参考値と考え、対照州を順番に「偽の処置群」に見立てて効果の順位で語り直すプラセボ置換検定(Day 5のFisherの発想の再会)で補強するのが誠実です。その道具は次回Day 19の主役になります。
GISデータ実務での使い方
- 自治体の施策評価はDIDの主戦場です。 ある自治体が漏水対策事業や料金改定を始めたとき、前後比較には気象や景気の共通の波が混ざります。未導入の自治体を対照に前後差の差を取れば、波を消せます。効くのは「水準の一致は要らない」という性質で、老朽化率も人口規模も違う自治体同士でも、動きが平行なら比較の土俵に乗ります。
- 最初に描くのはFig 1の左です。 導入前の数期間について、処置自治体と対照候補の時系列を重ねます。プレトレンドが割れていたら、対照の選び直しか設計の変更が先で、回帰を回すのはその後です。イベントスタディまで描けば、効果の立ち上がりの形(即効か遅効か)も同じ図から読めます。
- 自治体×年のパネルではクラスタSEを忘れないでください。 同じ自治体の漏水率や管路事故率は年をまたいで強く相関します。行数ではなく自治体数が実質的なサンプルサイズだ、という感覚はDIDに限らずパネル分析全般で効きます。
- 隣の自治体に効果が漏れると対照が汚染されます。 広域連携で対策のノウハウが隣に伝われば、対照群の「波だけ」のはずの前後差に効果の一部が乗り、DIDは効果を見誤ります。SUTVA違反が時空間パネルでどう顔を出すかは、Day 28でまとめて掘ります。
参考(本棚)
- Matheus Facure『Causal Inference in Python』第8章。DID・平行トレンド・TWFEの実装視点で、今日の下敷き
- Nick Huntington-Klein『The Effect』第18章。organ_donationsデータの出どころで、イベントスタディの設計もこの章に沿った
- Cameron & Trivedi『Microeconometrics Using Stata』。パネルの固定効果とクラスタロバスト標準誤差の土台
- Kessler & Roth (2014)「Don't Take 'No' for an Answer: An Experiment with Actual Organ Donor Registrations」。カリフォルニアの制度変更を検証した原論文
- データ: organ_donations(
causaldataパッケージ経由、MITライセンス、取得日2026-08-15)。27州×6四半期の臓器提供登録率パネル
次回予告(Day 19)
今日の対照群は「他の26州の平均」で、幸いプレトレンドはきれいに平行でした。でも処置されたのが1つの州で、平行に動いてくれる対照が1つも見つからなかったら? そのときは、複数の州を重み付きで混ぜ合わせ、処置前の軌跡にぴったり寄り添う「存在しない対照州」を合成します。次回Day 19は合成コントロール法。テキサス州の刑務所拡張データで、重みの最適化を手組みし、今日のつまづきで予告したプラセボ置換検定まで進みます。

