Mímisbrunnr知恵の泉

← 因果推論 一覧

🎓 レベル:基礎 | 重要度:A(必須)

📎 前提:潜在結果モデル | 識別の仮定 | 数理:フィッシャーの3原則(統計)

要点(BLUF)


1. 問題:観察データの素朴な比較はなぜ危ういか

「治療を受けた人」と「受けなかった人」の結果を単純に引き算しても、それが治療の効果とは限らない。両群が治療以外の点でも違うからだ。

典型例:重症の患者ほど治療を受けやすい。重症の患者は(治療してもなお)予後が悪い。すると「治療群=重症で予後が悪い人の集まり」「対照群=軽症で予後が良い人の集まり」になり、治療そのものは有効でも、群間差は治療を有害に見せる。これが交絡(confounding)だ。

flowchart LR
    C["交絡C(重症度)"] --> T["処置T(治療する)"]
    C --> Y["結果Y(回復スコア)"]
    T --> Y

処置 TT と結果 YY の間には、TCYT \leftarrow C \rightarrow Y というバックドアパスが開いている(バックドア基準と識別)。素朴な比較はこの裏口の影響を効果と混同する。


2. ランダム化が断つもの:識別の仮定

ランダム化は、CC から TT へ伸びる矢印を設計で消す。割り当てをコイン投げで決めれば、TT は重症度 CC とも、その他あらゆる(既知・未知の)背景要因とも独立になる。

flowchart LR
    C["交絡C(重症度)"] --> Y["結果Y(回復スコア)"]
    R["ランダム化(コイン投げ)"] --> T["処置T"]
    T --> Y

CTC \rightarrow T の矢印が消え、バックドアパスが塞がれた。これが RCT の核心だ。

因果効果を識別する3つの仮定

平均処置効果 ATE=E[Y(1)Y(0)]\text{ATE} = E[Y(1) - Y(0)] を素朴な差で推定してよいための条件は次の3つ(識別の仮定)。

  1. 交換可能性(交換可能性 / ignorability){Y(0),Y(1)}T\{Y(0), Y(1)\} \perp T。処置の割り当てが潜在結果と独立。ランダム化はこれを設計で保証する(観察データでは仮定するしかない)。
  2. 正値性(positivity)0<P(T=1)<10 < P(T=1) < 1。どの個体も両方の処置を取りうる。割り当て確率を 0.50.5 など 0011 の間に固定すれば自動的に満たす
  3. SUTVA:他人の処置が自分の結果に影響しない(干渉なし)+ 一貫性 Y=TY(1)+(1T)Y(0)Y = T\,Y(1) + (1-T)\,Y(0)

なぜ素朴な差が ATE になるか(導出)

観測される群間差を、一貫性で潜在結果に書き換え、E[Y(0)T=1]E[Y(0)\mid T=1] を足して引く。

E[YT=1]E[YT=0]素朴な差=E[Y(1)T=1]E[Y(0)T=0]=(E[Y(1)T=1]E[Y(0)T=1])処置群での効果 ATT+(E[Y(0)T=1]E[Y(0)T=0])選択バイアス\begin{aligned} \underbrace{E[Y\mid T=1] - E[Y\mid T=0]}_{\text{素朴な差}} &= E[Y(1)\mid T=1] - E[Y(0)\mid T=0] \\ &= \underbrace{\big(E[Y(1)\mid T=1] - E[Y(0)\mid T=1]\big)}_{\text{処置群での効果 ATT}} + \underbrace{\big(E[Y(0)\mid T=1] - E[Y(0)\mid T=0]\big)}_{\text{選択バイアス}} \end{aligned}

第2項の選択バイアスは「もし両群とも未処置だったときの差」。観察データではここが 00 にならない(処置群の方が元々重症= Y(0)Y(0) が低い)。

ランダム化のもとでは {Y(0),Y(1)}T\{Y(0),Y(1)\} \perp T なので、条件づけが外れて

E[Y(0)T=1]=E[Y(0)T=0]=E[Y(0)],E[Y(1)T=1]=E[Y(1)]E[Y(0)\mid T=1] = E[Y(0)\mid T=0] = E[Y(0)], \qquad E[Y(1)\mid T=1] = E[Y(1)]

となり、選択バイアスは 00、ATT は ATE に一致する。よって

E[YT=1]E[YT=0]=E[Y(1)]E[Y(0)]=ATE.E[Y\mid T=1] - E[Y\mid T=0] = E[Y(1)] - E[Y(0)] = \text{ATE}.

素朴な差が、何の調整もなしに ATE の不偏推定になる。これが「RCT が黄金律」と呼ばれる数理的な理由だ。


3. コード:同じ母集団から観察データとRCTを作って比べる

真の効果を ATEtrue=3\text{ATE}_{\text{true}}=3 と仕込んだ潜在結果を作り、(a) 重症者ほど治療されやすい観察データと (b) コイン投げのRCT で、それぞれ素朴な差を計算する。あわせて交絡 CC の群間バランスも見る。

import numpy as np
import pandas as pd

# === 同じ母集団から「観察データ」と「RCTデータ」を作り、素朴な差を比べる ===
rng = np.random.default_rng(42)
n = 20000

# 真の処置効果(治療で回復スコアが +3 される。全員一定)
ATE_true = 3.0

# 交絡 C:ベースラインの重症度(高いほど重症=放っておくと回復スコアが低い)
C = rng.normal(0.0, 1.0, size=n)

# 潜在結果:Y(0) は重症度が高いほど低い。Y(1) = Y(0) + 効果
Y0 = 50.0 - 8.0 * C + rng.normal(0.0, 3.0, size=n)
Y1 = Y0 + ATE_true

# --- (a) 観察データ:重症な人ほど治療を受けやすい(医師の判断=交絡)---
p_treat_obs = 1.0 / (1.0 + np.exp(-2.0 * C))   # シグモイド:C が高いほど治療されやすい
T_obs = rng.binomial(1, p_treat_obs)
Y_obs = np.where(T_obs == 1, Y1, Y0)            # 実際に観測される結果(一貫性)

# --- (b) RCT:コイン投げで治療を割り当て(C と独立)---
T_rct = rng.binomial(1, 0.5, size=n)
Y_rct = np.where(T_rct == 1, Y1, Y0)

# 素朴な群間差(処置群の平均 - 対照群の平均)
naive_obs = Y_obs[T_obs == 1].mean() - Y_obs[T_obs == 0].mean()
naive_rct = Y_rct[T_rct == 1].mean() - Y_rct[T_rct == 0].mean()

print(f"真の効果 ATE_true            = {ATE_true:+.3f}")
print(f"観察データの素朴な差         = {naive_obs:+.3f}  (バイアスあり)")
print(f"RCT の素朴な差               = {naive_rct:+.3f}  (ほぼ真値)")

# 共変量バランス:交絡 C の群間平均
print("\n--- 共変量 C のバランス(治療群 vs 対照群の平均)---")
print(f"観察データ: 治療群 C={C[T_obs==1].mean():+.3f}  対照群 C={C[T_obs==0].mean():+.3f}  差={C[T_obs==1].mean()-C[T_obs==0].mean():+.3f}")
print(f"RCT       : 治療群 C={C[T_rct==1].mean():+.3f}  対照群 C={C[T_rct==0].mean():+.3f}  差={C[T_rct==1].mean()-C[T_rct==0].mean():+.3f}")

出力:

真の効果 ATE_true            = +3.000
観察データの素朴な差         = -6.684  (バイアスあり)
RCT の素朴な差               = +3.008  (ほぼ真値)

--- 共変量 C のバランス(治療群 vs 対照群の平均)---
観察データ: 治療群 C=+0.608  対照群 C=-0.605  差=+1.213
RCT       : 治療群 C=+0.003  対照群 C=+0.007  差=-0.003

出力の意味:真の効果は +3+3(治療は有益)なのに、観察データの素朴な差は 6.7-6.7治療が有害に見えるという深刻な逆転だ。原因は共変量バランスを見れば一目瞭然で、観察データでは治療群の重症度 CC が対照群より 1.21.2 も高い(重症者に治療が集中している)。一方 RCT では素朴な差が +3.0+3.0 とほぼ真値で、CC の群間差は 0.000.00 = ランダム化が交絡 CC自動でバランスさせている。コードでは CC を使って調整など一切していない点に注目してほしい。ランダム化は「未知の交絡すら」バランスさせるので、調整する変数を知らなくてよいのが強みだ。


4. コード:素朴な差の「標本分布」を見る

1回の実験では偶然で当たることもある。多数回くり返したときに推定値の中心(平均)が真値に一致するか=不偏性をモンテカルロで確かめる。

import numpy as np
import matplotlib.pyplot as plt
import japanize_matplotlib

# === モンテカルロ:素朴な差の「分布」が真値を中心にするか確かめる ===
rng = np.random.default_rng(7)
ATE_true = 3.0
n = 500          # 1回の実験のサンプルサイズ
n_rep = 3000     # 実験を何回繰り返すか

naive_obs_list = []
naive_rct_list = []

for _ in range(n_rep):
    C = rng.normal(0.0, 1.0, size=n)
    Y0 = 50.0 - 8.0 * C + rng.normal(0.0, 3.0, size=n)
    Y1 = Y0 + ATE_true

    # 観察データ:重症者ほど治療されやすい
    p_obs = 1.0 / (1.0 + np.exp(-2.0 * C))
    T_obs = rng.binomial(1, p_obs)
    Y_obs = np.where(T_obs == 1, Y1, Y0)
    naive_obs_list.append(Y_obs[T_obs == 1].mean() - Y_obs[T_obs == 0].mean())

    # RCT:ランダム割り当て
    T_rct = rng.binomial(1, 0.5, size=n)
    Y_rct = np.where(T_rct == 1, Y1, Y0)
    naive_rct_list.append(Y_rct[T_rct == 1].mean() - Y_rct[T_rct == 0].mean())

naive_obs_arr = np.array(naive_obs_list)
naive_rct_arr = np.array(naive_rct_list)

print(f"真の効果 ATE_true        = {ATE_true:+.3f}")
print(f"観察データ 素朴差の平均  = {naive_obs_arr.mean():+.3f}  (真値から大きくずれる=バイアス)")
print(f"RCT       素朴差の平均   = {naive_rct_arr.mean():+.3f}  (真値とほぼ一致=不偏)")
print(f"RCT 素朴差の標準偏差     = {naive_rct_arr.std():.3f}")

plt.figure(figsize=(8, 4.5))
plt.hist(naive_obs_arr, bins=40, alpha=0.6, label="観察データの素朴な差")
plt.hist(naive_rct_arr, bins=40, alpha=0.6, label="RCT の素朴な差")
plt.axvline(ATE_true, color="red", linestyle="--", linewidth=2, label="真の効果 ATE=3")
plt.xlabel("推定された処置効果")
plt.ylabel("頻度")
plt.title("素朴な群間差の標本分布:観察データは偏り、RCTは真値を中心にする")
plt.legend()
plt.tight_layout()
plt.show()

出力:

真の効果 ATE_true        = +3.000
観察データ 素朴差の平均  = -6.703  (真値から大きくずれる=バイアス)
RCT       素朴差の平均   = +3.016  (真値とほぼ一致=不偏)
RCT 素朴差の標準偏差     = 0.773

出力の意味:3000回くり返しても観察データの分布は 6.7-6.7 を中心に固まっている。サンプルを増やしてもこのズレは消えない(バイアスは系統的で、nn を増やすと「間違った値」に正確に収束する)。RCT の分布は真値 33 を中心に対称に散らばり、平均は 3.023.02。RCT の素朴差は不偏で、ばらつき(標準偏差 0.770.77)は nn を増やせば縮む。「偏り(bias)」と「ばらつき(variance)」は別物で、RCT が解決するのは偏りの方だ。ばらつきを縮める工夫は次の 共変量調整と層別とブロック化 で扱う。


5. 直観:なぜランダム化で「独立」になるのか

潜在結果 Y(0),Y(1)Y(0), Y(1) は、実験前から各個体にすでに張りついている固定値だ(重症者は Y(0)Y(0) が低い、など)。コイン投げは、この潜在結果の中身を一切見ずに TT を決める。見ずに決めた以上、TT がどちらになるかは潜在結果と確率的に無関係=独立になる。

これは フィッシャーの3原則無作為化そのものだ。無作為化は「未知の系統的な偏りを偶然のばらつきに変換する」。因果の言葉でいえば「交絡バイアスを、推定量のばらつき(標準誤差)に付け替える」操作にあたる。だから RCT でも標準誤差は残る(消えるのは偏りだけ)。

観察データRCT
交換可能性仮定するしかない(成り立つ保証なし)設計で保証される
未知の交絡調整できない(測っていない)自動でバランス
素朴な差偏るATE の不偏推定
残る課題偏り+ばらつきばらつきのみ

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

(1)小標本では1回のランダム化でも偶然の不均衡が起きる ランダム化が保証するのは「平均的に(期待値で)バランスする」こと。nn が小さい1回の実験では、たまたま重症者が片群に偏ることがある。これに備えるのがブロック化・層別共変量調整と層別とブロック化)で、既知の予後因子は設計で釣り合わせておくとよい。

(2)「RCTなら何もしなくてよい」ではない 不偏なのは「割り当てが完全に守られ、脱落がランダム」な理想状態の話。実際には非遵守(割り当てと違う行動)や脱落が起きると、素朴な差は崩れる。これは 非遵守とITT で扱う。

(3)SUTVAの干渉 ワクチン・SNS・教室介入などでは、他人の処置が自分の結果に波及する(集団免疫・ネットワーク効果)。このとき個体レベルの Y(t)Y(t) が定義できず、RCT でも素朴な解釈が崩れる。クラスター無作為化などの設計で対処する。

(4)外的妥当性(一般化可能性) RCT が保証するのは「その実験集団における」内的妥当性(バイアスのなさ)。被験者が母集団と違えば(例:健康な志願者ばかり)、得られた ATE を現実の対象に外挿してよいかは別問題。ランダム化はサンプルの交絡を断つが、サンプル選択の偏りは断たない。


関連ノート