🎓 レベル:基礎 | 重要度:A(必須)
📎 前提:潜在結果モデル・バックドア基準と識別 | 次に読む:なぜRCTが黄金律か | 数理:条件付き確率・独立性・全確率の定理(統計)
要点(BLUF)
- 観察データの調整を因果効果と読むには、4つの仮定が要ります:交換可能性・正値性・一致性・SUTVA。
- 調整公式 の各ステップが、この4つのどれかに支えられています。1つ欠けると因果として読めません。
- 正値性が破れる(ある層に処置群が居ない)と調整は推定不能になり、SUTVA が破れる(干渉がある)と素朴比較が政策効果を取り逃すことを、擬似データで数値で示します。
1. 識別を支える4つの仮定
バックドア基準と識別 で、ATE が調整集合 で
と識別できることを見ました。この式の右辺が因果効果に等しくなるには、暗黙に次の4つが必要です。
| 仮定 | 中身 | 破れると |
|---|---|---|
| 交換可能性 (exchangeability / ignorability) | 未観測交絡が残り、層内比較が因果でない | |
| 正値性 (positivity / overlap) | (全層) | 比較相手のいない層が出て推定不能 |
| 一致性 (consistency) | (観測値=割り当て処置の潜在結果) | 観測 と潜在結果 が結びつかない |
| SUTVA | 相互干渉なし+処置は単一バージョン | 個体の潜在結果 が定義できない |
2. 調整公式の導出:どこで各仮定を使うか
(全員を処置した世界の平均)を、観測量だけで表してみます。各等号の上に使う仮定を書きます。
これは全期待値の法則(条件付き確率・独立性・全確率の定理)。ここで が個体ごとに定義できることが前提で、それを保証するのが SUTVA(他者の処置で自分の が変わらず、処置に複数版がない)です。次に
1つ目の等号は 交換可能性: を与えれば「誰が処置されたか」は潜在結果と無関係なので、 に絞っても の平均は変わりません。2つ目は 一致性: の人では観測値 がそのまま です。最後に、右辺 がすべての層 で計算できるためには、各層に処置群が存在する必要があります——これが 正値性。同様に も表せて、差を取れば前節の ATE 公式になります。4つすべてが揃って初めて「観測量=因果効果」と書けるのです。
交換可能性は バックドア基準と識別 のバックドア基準で(観測交絡を正しく調整すれば)確保できます。残る正値性と SUTVA を、破ってみて何が起きるか見ます。
3. 正値性が破れると調整は推定不能になる
正値性は「どの層 にも、処置群と対照群が両方いる」こと。ある層に片方しかいなければ、その層の効果 は比較相手がいないので計算できません。真の効果を と仕込み、正値性が成り立つ場合(A)と破れる場合(B)を比べます。
import numpy as np
import pandas as pd
# === 正値性(positivity):ある層に処置群(or 対照群)が居ないと調整が破綻する ===
rng = np.random.default_rng(3)
n = 50000
C = rng.integers(0, 5, size=n) # 交絡 C:5 水準 {0,1,2,3,4}
ATE_true = 2.0
def simulate(prop_by_level):
propensity = np.array(prop_by_level)[C] # 各個体の傾向スコア e(C)
X = rng.binomial(1, propensity)
Y = ATE_true * X + 1.0 * C + rng.normal(0, 1, n) # 真の効果 2.0
return X, Y
def stratified_table(X, Y):
rows = []
for c in range(5):
in_c = C == c
n_treat = int((X[in_c] == 1).sum())
n_ctrl = int((X[in_c] == 0).sum())
if n_treat > 0 and n_ctrl > 0:
effect = Y[in_c & (X == 1)].mean() - Y[in_c & (X == 0)].mean()
else:
effect = np.nan
rows.append([c, n_treat, n_ctrl, round(effect, 3)])
return pd.DataFrame(rows, columns=["C", "処置群n", "対照群n", "層内効果"])
# シナリオA:すべての層で 0<e<1(正値性 OK)
X_a, Y_a = simulate([0.2, 0.35, 0.5, 0.65, 0.8])
print("シナリオA(正値性 OK)")
tab_a = stratified_table(X_a, Y_a)
print(tab_a.to_string(index=False))
weights = [(C == c).mean() for c in range(5)]
print("層別調整 ATE =", round(np.average(tab_a["層内効果"], weights=weights), 3))
# シナリオB:両端で e=0 / e=1(正値性が破れる)
X_b, Y_b = simulate([0.0, 0.25, 0.5, 0.75, 1.0])
print("\nシナリオB(正値性が破れる)")
tab_b = stratified_table(X_b, Y_b)
print(tab_b.to_string(index=False))
print("→ C=0 は処置群0・C=4 は対照群0 で層内効果が NaN(推定不能)")
出力:
シナリオA(正値性 OK)
C 処置群n 対照群n 層内効果
0 2037 8065 2.011
1 3389 6532 2.015
2 5056 4929 2.021
3 6575 3405 2.002
4 7981 2031 1.978
層別調整 ATE = 2.005
シナリオB(正値性が破れる)
C 処置群n 対照群n 層内効果
0 0 10102 NaN
1 2506 7415 1.980
2 5013 4972 1.994
3 7466 2514 2.009
4 10012 0 NaN
→ C=0 は処置群0・C=4 は対照群0 で層内効果が NaN(推定不能)
出力の意味:シナリオ A は全層に両群がそろい、層別調整 ATE は で真値を当てます。シナリオ B は で処置群が 0 人、 で対照群が 0 人。これらの層では効果が NaN(推定不能) です。実務では正値性違反は「傾向スコアが や に張り付く」「ある属性の人は全員が処置を受けている」という形で現れ、IPW では重み が発散します。対処は ① 共通サポート(両群が重なる範囲)に推定対象を限定する、② モデルで外挿する(ただし外挿は仮定であってデータではない)、のいずれかで、どちらも estimand が変わる/仮定が増えることを意識する必要があります(逆確率重み付けIPW)。
4. SUTVA が破れると素朴比較は政策効果を取り逃す
SUTVA(Stable Unit Treatment Value Assumption) は2つの中身を持ちます:(i) 相互干渉なし(自分の結果は他人の処置に依存しない)、(ii) 処置は単一バージョン(“処置する” の中身が一定)。(i) が破れる典型がワクチンの集団免疫です。自分が打たなくても周りが打てば感染が減る——他者の処置が自分の結果を変えます。すると「処置の効果」が一意に定まりません。
ここでは個人の直接効果を 、集団の処置割合によるスピルオーバーを と仕込み、(i) 50%処置の RCT の素朴比較と、(ii) 全員処置 vs 誰も処置せずの政策効果を比べます。
import numpy as np
# === SUTVA(相互干渉なし)が破れると、RCTの素朴比較は政策効果を取り逃す ===
rng = np.random.default_rng(4)
n = 40000
# 真のモデル:個人の直接効果 tau=1.0、集団の処置割合によるスピルオーバー spill=2.0
tau, spill, base = 1.0, 2.0, 5.0
# (1) 50%処置のRCT(各人を独立にコイン投げ)
X = rng.binomial(1, 0.5, size=n)
frac_treated = X.mean() # 集団の処置割合(干渉=SUTVA違反の源)
Y = base + tau * X + spill * frac_treated + rng.normal(0, 1, n)
rct_naive = Y[X == 1].mean() - Y[X == 0].mean()
# (2) 政策効果:全員処置(frac=1) vs 誰も処置せず(frac=0) の真の対比
Y_all = base + tau * 1 + spill * 1.0
Y_none = base + tau * 0 + spill * 0.0
policy_effect = Y_all - Y_none
print(f"直接効果 tau (SUTVA下の個人効果) = {tau:.3f}")
print(f"RCTの素朴比較 = {rct_naive:.3f} ← 直接効果しか測れない")
print(f"政策効果 全員 vs 誰も (真値) = {policy_effect:.3f} ← 直接+スピルオーバー")
print(f"スピルオーバーによるギャップ = {policy_effect - rct_naive:.3f}")
出力:
直接効果 tau (SUTVA下の個人効果) = 1.000
RCTの素朴比較 = 0.991 ← 直接効果しか測れない
政策効果 全員 vs 誰も (真値) = 3.000 ← 直接+スピルオーバー
スピルオーバーによるギャップ = 2.009
出力の意味:RCT の素朴比較は 。処置群と対照群が同じ集団の処置割合を共有するので、スピルオーバー項が引き算で消え、直接効果 しか測れません。しかし「全員に配る」政策の真の効果は 。差の ()がスピルオーバーで、SUTVA を素朴に仮定するとこの を見落とします。干渉があるときは「処置の効果」が一意でなく、直接効果・スピルオーバー効果・全体効果を別々の estimand として定義し直す必要があります(部分干渉・クラスター設計など)。
5. 仮定の直観的意味と確認可能性
- 交換可能性は検証できない仮定です(反事実が見えないため)。観測交絡を調整して近似し、未観測交絡が残るリスクは感度分析(第7章)で評価します。RCT は割り当てをランダムにすることで、 なしの無条件交換可能性を設計で保証します(なぜRCTが黄金律か・フィッシャーの3原則)。
- 正値性はデータから確認できます:各層の処置・対照の人数や傾向スコアの分布を見ればよい(第3節)。違反は「推定できないものを推定しようとしている」サインです。
- 一致性・SUTVAは処置の定義の問題。「処置」が曖昧(用量がバラバラ、版が複数)だと一致性が崩れ、干渉があると SUTVA が崩れます。デザイン段階で処置を明確に定義し、干渉の構造を見極めることが対処になります。
⚠️ よくある誤解・落とし穴
- 「交絡を調整したから因果だ」と言い切れない。交換可能性は未観測交絡がないことまで要求します。観測した分を調整しても、見落とした交絡が残れば因果ではありません。「調整した」≠「交換可能」。
- 正値性違反を見落とす。回帰モデルは比較相手のいない層でも平気で数値を返します(外挿)。一見もっともらしい推定値が、実はデータの裏付けのない外挿、ということが起きます。重なり(overlap)の確認は必須です。
- SUTVA は暗黙に仮定されがち。ネットワーク・市場・感染症・教室など、単位が影響し合う場面では常に疑うべきです。「個人の効果」と「全員に展開したときの効果」は別物です。
- 一致性は自明でない。「禁煙の効果」のように処置が複数の実現方法を持つ(断つ/減らす/薬で)と、 がどの版を指すか曖昧になり、推定値の意味が定まりません。
関連ノート
- 第1章 因果推論の枠組み 目次
- 潜在結果モデル(前提・反事実と ATE/ATT)
- バックドア基準と識別(前提・交換可能性を満たす調整集合)
- 相関と因果の違い(交絡と素朴比較の失敗)
- なぜRCTが黄金律か(次のトピック・設計で交換可能性を保証)
- 逆確率重み付けIPW(正値性違反と重みの発散)
- 条件付き確率・独立性・全確率の定理(統計・条件付き独立と全期待値の法則)
- フィッシャーの3原則(統計・ランダム化の土台)
- 統計的因果推論・傾向スコア(統計・識別と傾向スコア)
- 因果推論テキスト 全体目次