🎓 レベル:標準 | 重要度:A(必須)
📎 前提:バックドア基準と識別 | 識別の仮定 | 数理:重回帰分析(統計)
要点(BLUF)
- 未観測交絡で処置 が内生()だと、いくら共変量を入れても回帰は偏る。バックドアを閉じられない。
- そこで 操作変数 (処置だけを動かし結果には直接効かない外生的な「揺さぶり」)を使い、 のうち で説明される外生部分だけで効果を測る=2段階最小二乗法(2SLS)。
- 識別に要る3条件は 関連性・除外制約・独立性(+単調性)。効果が異質なら IV が当てるのは ATE ではなく コンプライアの効果=LATE。
なぜ調整では足りないのか:内生性
回帰による調整とその限界 で見た調整法は、交絡が観測できていることが大前提だった。だが現実には、能力・意欲・健康度のように観測できない交絡 が処置と結果の両方を動かすことが多い。
結果モデルを
と書くと、 が にも影響するため誤差 と処置 が相関する。これを内生性(endogeneity) と呼ぶ。このとき OLS は
となり、第2項のぶんだけ系統的に偏る。 が観測できない以上、共変量に加えることもできない。手詰まりに見える。
flowchart LR
Z["操作変数 Z"] --> T["処置 T"]
U["未観測交絡 U(観測不能)"] -.-> T
U -.-> Y["結果 Y"]
T --> Y
突破口は、 を外から揺さぶる変数 を見つけることだ。図で は にだけ矢印を引き、 へは を経由してのみ到達し、 とは無関係。この が操作変数である。
識別の仮定:操作変数が満たすべき3条件
が因果効果を識別するには次が必要。
- 関連性(relevance):。 は処置を実際に動かす(第1段が有意)。
- 除外制約(exclusion restriction): は を通じてのみ に影響する。 の直接経路はない()。
- 独立性/外生性(independence): は交絡 と独立。割当が「あたかもランダム」。
この3つのうち 2と3はデータだけでは検証できない(誤差 は未観測)。理屈と制度知識で正当化するしかない。ここが IV の弱点であり強みでもある。
これらが成り立つと、単一操作変数では
と、除外制約 のおかげで真の効果 が取り出せる。これを推定で実装したのが2SLSだ。
2段階最小二乗法(2SLS)
- 第1段:処置を操作変数に回帰し、外生部分を取り出す。
- 第2段:結果を予測処置 に回帰する。
は の関数なので と無相関=外生。だから第2段の係数が交絡を逃れて を当てる。次のコードでは未観測交絡で内生な処置と有効な操作変数を仕込み、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 と上振れしている。 が と を同方向に押し上げる正の交絡なので、偏りも正で理論値 と一致する。一方 2SLSは 1.977 と真値 2.0 をほぼ回収した。第1段の 係数 0.775 が0から十分離れており、操作変数が効いている証拠だ。
実務では
linearmodelsのIV2SLSを使うと正しい標準誤差が出る(第2段を素朴にOLSしたSEは過小になる)。ここでは依存を増やさず、点推定が回収される事実を手実装で確認した。
弱い操作変数の害
第1段の関連が弱い()と、 が小さく の分母が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 ではない。 に反応して処置を変える人=コンプライアの効果だけ、すなわち局所平均処置効果(LATE) だ(非遵守とITT の遵守タイプ分類と同じ構図)。
単調性(へそ曲がり=defier がいない)を仮定すると、Wald 推定量
が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の推定値は「 で動く人たち(コンプライア)」に限った効果であり、誰についての効果かを必ず言語化する必要がある。最低賃金を割引券 で揺さぶった研究の結論を、 に反応しない層へ外挿してはいけない。
仮定の直観:なぜ除外制約が決定的か
操作変数の核心は「 は を通じてしか に効かない」という除外制約だ。もし が に直接の裏口を持つと、第1段で取り出した にその裏口経路が混ざり、第2段の係数は再び汚染される。たとえば「コレラ井戸の場所」を 、「井戸水の摂取」を 、「発症」を とするスノウの分析が説得的なのは、井戸の場所が摂取を通じてしか発症に効かない(場所そのものは健康に無関係)と信じられるからだ。除外制約はデータでなく因果物語で支える。
⚠️ よくある誤解・落とし穴
- 「操作変数があれば交絡を気にせず因果が出る」は誤り。除外制約と独立性は検証不能の強い仮定。一つでも破れれば2SLSは堂々と偏った値を出す。
- 弱い操作変数を見逃す。第1段が弱いと推定は高分散かつOLS方向へ偏る。F値・第1段の有意性を必ず報告する。
- LATEをATEと取り違える。IVは「コンプライアの効果」。母集団平均でも、処置を受けた人全体の効果(ATT)でもない。
- 過剰識別の濫用。操作変数を増やすと過剰識別検定(Sargan/Hansen)はできるが、すべての が除外制約を満たす保証にはならない。
- テストできるのは関連性だけ。除外制約・独立性は「検定で通った」と言えない。
関連ノート
- 非遵守とITT — 遵守タイプ(コンプライア/always/never)とITT、IVの土台
- バックドア基準と識別 — 観測交絡なら調整、未観測ならIVという分岐
- 識別の仮定 — 交換可能性・正値性・SUTVA との対応
- デザインの選び方 — どんなデータ状況でIVを選ぶか
- 重回帰分析(統計)— 第1段・第2段のOLSの基礎