Mímisbrunnr知恵の泉

← 因果推論 一覧

🎓 レベル:標準 | 重要度:A(必須)

📎 前提:バックドア基準と識別 | 識別の仮定 | 数理:重回帰分析(統計)

要点(BLUF)


なぜ調整では足りないのか:内生性

回帰による調整とその限界 で見た調整法は、交絡が観測できていることが大前提だった。だが現実には、能力・意欲・健康度のように観測できない交絡 UU が処置と結果の両方を動かすことが多い。

結果モデルを

Y=β0+βT+ε,εUY = \beta_0 + \beta\,T + \varepsilon, \qquad \varepsilon \ni U

と書くと、UUTT にも影響するため誤差 ε\varepsilon と処置 TT が相関する。これを内生性(endogeneity) と呼ぶ。このとき OLS は

plimβ^OLS=β+Cov(T,ε)Var(T)\operatorname{plim}\hat\beta_{\text{OLS}} = \beta + \frac{\mathrm{Cov}(T,\varepsilon)}{\mathrm{Var}(T)}

となり、第2項のぶんだけ系統的に偏る。UU が観測できない以上、共変量に加えることもできない。手詰まりに見える。

flowchart LR
    Z["操作変数 Z"] --> T["処置 T"]
    U["未観測交絡 U(観測不能)"] -.-> T
    U -.-> Y["結果 Y"]
    T --> Y

突破口は、TT を外から揺さぶる変数 ZZ を見つけることだ。図で ZZTT にだけ矢印を引き、YY へは TT経由してのみ到達し、UU とは無関係。この ZZ操作変数である。

識別の仮定:操作変数が満たすべき3条件

ZZ が因果効果を識別するには次が必要。

  1. 関連性(relevance)Cov(Z,T)0\mathrm{Cov}(Z,T)\neq 0ZZ は処置を実際に動かす(第1段が有意)。
  2. 除外制約(exclusion restriction)ZZTT通じてのみ YY に影響する。ZYZ\to Y の直接経路はない(Cov(Z,ε)=0\mathrm{Cov}(Z,\varepsilon)=0)。
  3. 独立性/外生性(independence)ZZ は交絡 UU と独立。割当が「あたかもランダム」。

この3つのうち 2と3はデータだけでは検証できない(誤差 ε\varepsilon は未観測)。理屈と制度知識で正当化するしかない。ここが IV の弱点であり強みでもある。

これらが成り立つと、単一操作変数では

βIV=Cov(Z,Y)Cov(Z,T)=βCov(Z,T)+Cov(Z,ε)Cov(Z,T)=β\beta_{\text{IV}} = \frac{\mathrm{Cov}(Z,Y)}{\mathrm{Cov}(Z,T)} = \frac{\beta\,\mathrm{Cov}(Z,T) + \mathrm{Cov}(Z,\varepsilon)}{\mathrm{Cov}(Z,T)} = \beta

と、除外制約 Cov(Z,ε)=0\mathrm{Cov}(Z,\varepsilon)=0 のおかげで真の効果 β\beta が取り出せる。これを推定で実装したのが2SLSだ。

2段階最小二乗法(2SLS)

T=π0+π1Z+v,T^=π^0+π^1ZT = \pi_0 + \pi_1 Z + v, \qquad \hat T = \hat\pi_0 + \hat\pi_1 Z Y=β0+βT^+誤差Y = \beta_0 + \beta\,\hat T + \text{誤差}

T^\hat TZZ の関数なので UU と無相関=外生。だから第2段の係数が交絡を逃れて β\beta を当てる。次のコードでは未観測交絡で内生な処置有効な操作変数を仕込み、OLSが偏り2SLSが真値を回収することを数値で確かめる。

import numpy as np
import statsmodels.api as sm

# === 未観測交絡で処置が内生な擬似データ+有効な操作変数を作る ===
rng = np.random.default_rng(42)
n = 10000

U = rng.normal(0, 1, n)              # 未観測交絡(観測できない)
Z = rng.normal(0, 1, n)             # 操作変数(U と独立に生成)
T = 0.8 * Z + 1.0 * U + rng.normal(0, 1, n)   # 処置:Z にも U にも依存=内生
beta_true = 2.0                      # 仕込んだ真の因果効果
Y = 1.0 + beta_true * T + 1.5 * U + rng.normal(0, 1, n)

# 素朴OLS:Y を T に回帰(U を無視)
ols = sm.OLS(Y, sm.add_constant(T)).fit()
beta_ols = ols.params[1]

# 2SLS 手実装:第1段 T~Z で予測、第2段 Y~予測T
stage1 = sm.OLS(T, sm.add_constant(Z)).fit()
T_hat = stage1.fittedvalues
stage2 = sm.OLS(Y, sm.add_constant(T_hat)).fit()
beta_2sls = stage2.params[1]

print(f"真の効果 beta_true = {beta_true:.3f}")
print(f"素朴OLS推定      = {beta_ols:.3f}")
print(f"第1段 Z係数      = {stage1.params[1]:.3f}")
print(f"2SLS推定         = {beta_2sls:.3f}")

出力は次の通り。

真の効果 beta_true = 2.000
素朴OLS推定      = 2.572
第1段 Z係数      = 0.775
2SLS推定         = 1.977

OLSは 2.572 と上振れしている。UUTTYY を同方向に押し上げる正の交絡なので、偏りも正で理論値 1.5×Cov(T,U)/Var(T)0.571.5\times\mathrm{Cov}(T,U)/\mathrm{Var}(T)\approx0.57 と一致する。一方 2SLSは 1.977 と真値 2.0 をほぼ回収した。第1段の ZZ 係数 0.775 が0から十分離れており、操作変数が効いている証拠だ。

実務では linearmodelsIV2SLS を使うと正しい標準誤差が出る(第2段を素朴にOLSしたSEは過小になる)。ここでは依存を増やさず、点推定が回収される事実を手実装で確認した。

弱い操作変数の害

第1段の関連が弱い(π10\pi_1\approx 0)と、Cov(Z,T)\mathrm{Cov}(Z,T) が小さく βIV\beta_{\text{IV}} の分母が0近くで暴れる。推定は高分散になり、有限標本ではOLSの偏りの方へ引き戻される。第1段の F統計量 > 10 が経験則の目安だ。強い/弱い操作変数で500回ずつ繰り返し、2SLSのばらつきを比べる。

import numpy as np
import statsmodels.api as sm

# === 弱い操作変数の害:第1段が弱いと2SLSは暴れOLSの偏りへ引き寄せられる ===
rng = np.random.default_rng(7)
n = 1000
beta_true = 2.0
n_rep = 500

def one_draw(gamma_strength):
    U = rng.normal(0, 1, n)
    Z = rng.normal(0, 1, n)
    T = gamma_strength * Z + 1.0 * U + rng.normal(0, 1, n)
    Y = 1.0 + beta_true * T + 1.5 * U + rng.normal(0, 1, n)
    F = sm.OLS(T, sm.add_constant(Z)).fit().fvalue       # 第1段F(>10が目安)
    b_ols = sm.OLS(Y, sm.add_constant(T)).fit().params[1]
    T_hat = sm.OLS(T, sm.add_constant(Z)).fit().fittedvalues
    b_iv = sm.OLS(Y, sm.add_constant(T_hat)).fit().params[1]
    return F, b_ols, b_iv

for label, gamma in [("強い操作変数", 0.8), ("弱い操作変数", 0.05)]:
    Fs, ols_list, iv_list = [], [], []
    for _ in range(n_rep):
        F, b_ols, b_iv = one_draw(gamma)
        Fs.append(F); ols_list.append(b_ols); iv_list.append(b_iv)
    print(f"{label}: 第1段F平均={np.mean(Fs):7.1f}  "
          f"OLS平均={np.mean(ols_list):.3f}  "
          f"2SLS平均={np.mean(iv_list):.3f}  2SLS標準偏差={np.std(iv_list):.3f}")
print(f"(真の効果={beta_true:.3f})")

出力は次の通り。

強い操作変数: 第1段F平均=  321.0  OLS平均=2.567  2SLS平均=2.000  2SLS標準偏差=0.069
弱い操作変数: 第1段F平均=    2.3  OLS平均=2.751  2SLS平均=2.834  2SLS標準偏差=9.082

強い操作変数では2SLSは平均2.000・標準偏差0.069と真値にぴたりと集中。弱い操作変数では第1段F平均が 2.3(10未満) に落ち、2SLSの標準偏差が9.082へ爆発し、平均も2.834と当てにならない。「操作変数がある=安心」ではない。第1段の強さ(F値)を必ず確認すること。

IVが当てるのはLATE:効果が異質なとき

処置効果が個人ごとに違うとき、IVが識別するのは母集団全体の ATE ではないZZ に反応して処置を変える人=コンプライアの効果だけ、すなわち局所平均処置効果(LATE) だ(非遵守とITT の遵守タイプ分類と同じ構図)。

単調性(へそ曲がり=defier がいない)を仮定すると、Wald 推定量

β^IV=E[YZ=1]E[YZ=0]E[TZ=1]E[TZ=0]pE ⁣[Y(1)Y(0)complier]\hat\beta_{\text{IV}} = \frac{E[Y\mid Z=1]-E[Y\mid Z=0]}{E[T\mid Z=1]-E[T\mid Z=0]} \xrightarrow{p} E\!\left[Y(1)-Y(0)\mid \text{complier}\right]

がLATEに一致する。次のコードは、常に処置を受ける always-taker が高いベースラインを持つ(自己選択)状況で、素朴比較・Wald・真のLATE・真のATEを比べる。

import numpy as np

# === IV が当てるのは LATE(コンプライアの効果)であって ATE ではない ===
rng = np.random.default_rng(1)
n = 200000

# タイプ:complier 40% / always-taker 30% / never-taker 30%
u = rng.random(n)
is_complier = u < 0.40
is_always = (u >= 0.40) & (u < 0.70)
is_never = u >= 0.70

Z = rng.integers(0, 2, n)                    # 操作変数(ランダム割当)

# 処置の決まり方:complier は Z に従う/always は常に1/never は常に0
T = np.where(is_complier, Z, np.where(is_always, 1, 0))

# タイプ別の真の個別効果(異質)とベースライン(always は高い=自己選択)
effect = np.where(is_complier, 2.0, np.where(is_always, 6.0, 0.0))
baseline = np.where(is_complier, 5.0, np.where(is_always, 8.0, 4.0))
Y = baseline + effect * T + rng.normal(0, 0.5, n)

naive = Y[T == 1].mean() - Y[T == 0].mean()
wald = (Y[Z == 1].mean() - Y[Z == 0].mean()) / (T[Z == 1].mean() - T[Z == 0].mean())
ate_true = effect.mean()
late_true = effect[is_complier].mean()

print(f"素朴比較 E[Y|T=1]-E[Y|T=0] = {naive:.3f}")
print(f"Wald(=2SLS)推定           = {wald:.3f}")
print(f"真の LATE(コンプライア) = {late_true:.3f}")
print(f"真の ATE(母集団全体)    = {ate_true:.3f}")

出力は次の通り。

素朴比較 E[Y|T=1]-E[Y|T=0] = 6.793
Wald(=2SLS)推定           = 2.017
真の LATE(コンプライア) = 2.000
真の ATE(母集団全体)    = 2.595

素朴比較は6.793と大暴投(高ベースラインの always-taker が処置群に偏るため)。Wald は2.017で真のLATE 2.0 を当てた。だが真のATE 2.595 とは違う。IVの推定値は「ZZ で動く人たち(コンプライア)」に限った効果であり、誰についての効果かを必ず言語化する必要がある。最低賃金を割引券 ZZ で揺さぶった研究の結論を、ZZ に反応しない層へ外挿してはいけない。

仮定の直観:なぜ除外制約が決定的か

操作変数の核心は「ZZTT を通じてしか YY に効かない」という除外制約だ。もし ZZYY に直接の裏口を持つと、第1段で取り出した T^\hat T にその裏口経路が混ざり、第2段の係数は再び汚染される。たとえば「コレラ井戸の場所」を ZZ、「井戸水の摂取」を TT、「発症」を YY とするスノウの分析が説得的なのは、井戸の場所が摂取を通じてしか発症に効かない(場所そのものは健康に無関係)と信じられるからだ。除外制約はデータでなく因果物語で支える。

⚠️ よくある誤解・落とし穴

関連ノート