🎓 レベル:標準 | 重要度:A(必須)
📎 前提:バックドア基準と識別 | 識別の仮定 | 回帰による調整とその限界 | 数理:ロジスティック回帰(機械学習)
要点(BLUF)
- 傾向スコア は「共変量 の人が処置を受ける確率」。
- これは バランシングスコア: で条件づけると共変量 の分布が処置群と対照群で揃う()。よって高次元の の代わりに1次元の で調整できる(次元削減)。
- 強く無視できる割り当ての下、層別(subclassification)やマッチングで素朴比較のバイアスを大きく消し、 を回収できる。前節回帰による調整とその限界と違い、結果の関数形を仮定せず処置の確率だけをモデル化する。
1. 概念:処置の確率をモデル化する
回帰による調整とその限界では結果 の関数形を当てる必要があった。傾向スコア法は発想を変え、「処置を受ける確率」だけをモデル化する。
共変量 がバックドア基準と識別のバックドア基準を満たすとき、 を使えば調整ができる。直観は「傾向スコアが同じ人どうしを比べれば、処置・対照の振り分けはコイン投げと同じ」――つまり局所的にRCTに近づく。
2. 識別:バランシングスコアの定理
バランシング性
傾向スコアの核心は次の性質である。
つまり を固定すると、処置 はそれ以上 に依存しない。証明は一行:
左辺が に依らず だけで決まるので、 を与えれば と は独立。結果として、 が等しいグループの中では、共変量 の分布が処置群と対照群で同じになる。
強く無視できる割り当ての縮約
識別の仮定の条件付き交換可能性 が成り立つなら、Rosenbaum–Rubin(1983)の定理により
が言える。高次元ベクトル で条件づける代わりに、スカラー で条件づければ十分になる。これが傾向スコアの最大の御利益(次元削減)。さらに正値性 が要る点は回帰による調整とその限界と同じ。
❓ 確認:傾向スコアで「予測がよく当たる」ことは目的だろうか? 違う。目的はバランスを取ること。極端に言えば の予測精度が高すぎる(処置が完全に予測できる)と、それは正値性の崩壊を意味し、むしろ調整できなくなる。
3. 推定:層別とマッチングで効果を回収する
真の効果を仕込んだ擬似データで確かめる。2つの共変量 が処置にも結果にも線形に効く(交絡する)状況で、真の効果は均一 とする。傾向スコアをロジスティック回帰で推定し、(1) 5分位での層別、(2) 最近傍マッチング、で効果を回収する。共変量バランスは 標準化平均差 (SMD) で測る(目安は )。
import warnings
import numpy as np
import pandas as pd
from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import NearestNeighbors
warnings.filterwarnings("ignore")
# === 傾向スコアの層別・マッチングで共変量バランスを改善し ATE を回収する ===
rng = np.random.default_rng(7)
n = 6000
X1 = rng.normal(0, 1, size=n)
X2 = rng.normal(0, 1, size=n)
# 処置割り当て: 両共変量に依存(交絡)
prop_true = 1.0 / (1.0 + np.exp(-(0.7 * X1 + 0.7 * X2)))
T = rng.binomial(1, prop_true)
# 結果: 共変量が線形に交絡。真の効果は均一
ATE_true = 3.0
Y = 1.0 + ATE_true * T + 2.0 * X1 + 2.0 * X2 + rng.normal(0, 1, size=n)
naive = Y[T == 1].mean() - Y[T == 0].mean()
# 傾向スコア推定(ロジスティック回帰)
covariates = np.column_stack([X1, X2])
e_hat = LogisticRegression().fit(covariates, T).predict_proba(covariates)[:, 1]
# 標準化平均差(SMD): 共変量バランスの指標
def smd(values, treat):
m1, m0 = values[treat == 1].mean(), values[treat == 0].mean()
s1, s0 = values[treat == 1].std(), values[treat == 0].std()
return (m1 - m0) / np.sqrt((s1**2 + s0**2) / 2)
smd_X1_before = abs(smd(X1, T))
smd_X2_before = abs(smd(X2, T))
# (1) 傾向スコア層別(5分位)で ATE と層内バランス
df = pd.DataFrame({"Y": Y, "T": T, "X1": X1, "X2": X2, "e": e_hat})
df["stratum"] = pd.qcut(df["e"], 5, labels=False)
ate_list, size_list, smd1_list, smd2_list = [], [], [], []
for s in range(5):
sub = df[df["stratum"] == s]
if (sub["T"] == 1).sum() > 0 and (sub["T"] == 0).sum() > 0:
ate_s = sub.loc[sub["T"] == 1, "Y"].mean() - sub.loc[sub["T"] == 0, "Y"].mean()
ate_list.append(ate_s)
size_list.append(len(sub))
smd1_list.append(abs(smd(sub["X1"].values, sub["T"].values)))
smd2_list.append(abs(smd(sub["X2"].values, sub["T"].values)))
ate_stratified = np.average(ate_list, weights=size_list)
smd_X1_after = np.average(smd1_list, weights=size_list)
smd_X2_after = np.average(smd2_list, weights=size_list)
# (2) 最近傍マッチング(傾向スコアが最も近い対照を処置に対応づけ -> ATT)
treated = df[df["T"] == 1]
control = df[df["T"] == 0]
nn = NearestNeighbors(n_neighbors=1).fit(control[["e"]].values)
_, idx = nn.kneighbors(treated[["e"]].values)
att_matching = (treated["Y"].values - control["Y"].values[idx.flatten()]).mean()
print(f"真の ATE : {ATE_true:.3f}")
print(f"素朴比較 : {naive:.3f}")
print(f"傾向スコア層別 ATE : {ate_stratified:.3f}")
print(f"最近傍マッチング ATT : {att_matching:.3f}")
print(f"X1: 調整前 {smd_X1_before:.3f} -> 層別後 {smd_X1_after:.3f}")
print(f"X2: 調整前 {smd_X2_before:.3f} -> 層別後 {smd_X2_after:.3f}")
実行結果は次の通り。
真の ATE : 3.000
素朴比較 : 5.247
傾向スコア層別 ATE : 3.249
最近傍マッチング ATT : 2.968
X1: 調整前 0.575 -> 層別後 0.077
X2: 調整前 0.611 -> 層別後 0.104
出力の意味:素朴比較は 5.247 と真値 3.0 を大きく上回る(処置群は が大きい個体に偏り、それが を押し上げる)。傾向スコアで調整すると、層別で 3.249、マッチングで 2.968 と真値近くまで戻る。重要なのは下2行の 共変量バランス:調整前は SMD が 0.58, 0.61 と大きく偏っていたのが、層別後は 0.08, 0.10 とほぼ揃う。バランスが取れたからこそ効果推定が正しくなった、という因果が数値で見える。層別の 3.249 がまだ真値からわずかにずれるのは、5分位という粗い層分けで層内に残差交絡が残るため(後述)。
4. なぜバランスが鍵なのか(直観)
傾向スコア法のゴールは予測精度ではなく 疑似的なランダム化 だ。 が同じ人を集めれば、その小集団では処置・対照の振り分けが(観測共変量に関する限り)コイン投げと同等になる。だから層別やマッチングで小集団ごとに差を取れば、交絡が相殺される。
- 層別: を分位点で区切り、層内で差を取って層サイズで加重平均。Rosenbaum–Rubin は5分位で観測交絡バイアスの約90%が除去できると示した。残り約10%は層内の不均一さ(本コードの 3.249 のずれに対応)。層を増やすか、後続のIPW・回帰併用で削れる。
- マッチング:処置個体ごとに が最も近い対照を当てる。推定対象は 処置群での平均効果 (ATT)。本コードの 2.968 がそれで、真値 3.0 に近い。
どちらも結果 の関数形を仮定していない点が回帰による調整とその限界との決定的な違い。傾向スコアの設計(共変量の選択・モデル)が正しければ、結果がどんな非線形でも調整できる。
⚠️ よくある誤解・落とし穴
- バランスを確認せずに ATE を報告しない。傾向スコア法の品質は p値ではなく 調整後の SMD で判断する。SMD が大きいままなら、モデルに交互作用・非線形項を足すか、別手法へ。
- 傾向スコアの予測精度を上げるのは目的ではない。 が 0 や 1 に張り付く(処置がほぼ確実に予測できる)のは正値性の崩壊で、その層はマッチング相手が見つからない。逆確率重み付けIPWではこれが分散爆発として顕在化する。
- 共通サポート外を捨てる代償:マッチングで相手のいない個体を落とすと、推定対象の母集団が変わり外的妥当性が損なわれる。
- 「傾向スコアを共変量として回帰に入れれば万能」ではない。それも関数形依存を持ち込む。次節以降の重み付け(IPW)・二重頑健(AIPW)が、関数形への依存をさらに減らす方向の発展となる。
関連ノート
- 因果:回帰による調整とその限界(結果の関数形に依存する素朴な調整)/逆確率重み付けIPW(傾向スコアの逆数で重み付け)/二重頑健推定AIPW(傾向スコアと回帰の併用)/バックドア基準と識別(調整集合の選び方)/共変量調整と層別とブロック化(実験計画での層別)
- 機械学習:ロジスティック回帰(傾向スコアモデルの定番)
- 統計:一般化線形モデル(ロジスティック・ポアソン回帰)(ロジットリンクの数理)