この連載について
因果推論を「本を読んだ」で終わらせず、自分の言葉で説明でき、コードで再現できる状態まで落とす30日連載です。直前のDay 5では、RCT(無作為化比較試験)なら「単純な引き算がそのまま因果効果になる」ことを、Fisherの並べ替え検定とNeymanの反復サンプリングという2つの流派で確かめました。
でも現実に手元にあるデータは、たいてい割付がランダムではありません。誰が処置を受けるかは、本人の意欲や環境で決まっています。今日は、その割付を決める過程そのものに踏み込みます。どういう条件がそろえば、観察データでもRCTのように引き算してよいのか。そして、その条件が成り立っているかを、データから確かめられるのか。図はすべて合成データで、真の効果を私たちが握った状態で見せます。
TL;DR(3行)
- 割付メカニズム=「誰が処置を受けるか」を決める過程。 RCTは設計でランダムに、観察データは自己選択で決まります。だから観察データでは処置群と対照群で交絡因子$X$が偏り、素朴な引き算はバイアスを持ちます(今日の例で 4.92、真値は3.0)。
- 鍵は非交絡性(unconfoundedness)$W \perp (Y(0),Y(1)) \mid X$。 正しい$X$で条件付ければ、層の中では割付がランダムに見えます。$X$で調整すると素朴な4.92が 3.00 に戻り、真値と一致します。
- ただし非交絡性はデータから検証できません。 隠れた交絡があると、真の効果がゼロでも$X$調整後に +2.15、しかも観測$X$のバランスは合格に見えます。区別する道具は感度分析(Day 9)です。
今日の問い
Day 4と5で「無作為割付なら引き算が因果になる」と分かった。でも現実のデータは割付がランダムではない。
では、どういう条件がそろえば、観察データでもRCTのように引き算してよいのか。そして、その条件が成り立っているかを、データそのものから確かめられるのか?
結論を先に言うと、条件は3つあり、その芯は「非交絡性」です。そして最後の問いの答えは「原理的に確かめられない」です。今日はまず割付メカニズムの3条件を言葉にして、次に交絡を条件付けで消す様子をコードで見て、最後に「検証できない」を身をもって体感します。
概念① 割付メカニズム:誰が処置を受けるかを決める過程
Day 2で、因果推論の根本問題は「片方の潜在的結果しか観測できない」ことだと見ました。では、どちらが観測されるかは何が決めているのか。それが割付メカニズムです。形式的には、共変量$X$と潜在的結果$Y(0), Y(1)$を与えたときに、割付ベクトル$W$が起きる確率
$$
\Pr(W \mid X, Y(0), Y(1))
$$
として書けます。この関数が、どの潜在的結果を実現させ、どれを欠測にするかを決めています。RCTでは研究者がコイン投げでこれを設計し、完全に制御します。観察データでは、この過程は本人の選択や医師の判断で決まり、研究者は関数形すら知りません。
Imbens & Rubin は、この割付メカニズムに3つの条件を置きます。
- 個別性(individualistic):各人の割付確率は、自分の特性だけで決まる。他人の運命に直接引きずられない。
- 確率性(probabilistic):誰もが処置と対照の両方に回りうる。つまり $0 < e(x) < 1$。
- 非交絡性(unconfounded):割付が潜在的結果に依存しない。式で書くと $\Pr(W \mid X, Y(0), Y(1)) = \Pr(W \mid X)$。
RCTは、この3つを設計で満たし、かつ関数形が既知です。観察データでこの3つが(設計ではなく仮定として)成り立つ状態を、Rubinは正規な割付メカニズムと呼びます。これが観察研究の理想形で、Day 7以降の傾向スコアもマッチングも、すべてこの土台の上に立ちます。今日の主役は3つ目の非交絡性です。
概念② 非交絡性:正しいXで条件付ければ、層の中はRCT
非交絡性は、条件付き独立として書けます。
$$
W_i \perp!!!\perp \bigl(Y_i(0),, Y_i(1)\bigr) ,\bigm|, X_i
$$
言葉にすると、「同じ$X$を持つ人たちの中では、誰が処置に回るかは潜在的結果と無関係」です。全体では割付が偏っていても、$X$で条件付けた層の内側では、割付がコイン投げのように振る舞います。だから層の中では、Day 4と5で使った完全ランダム化実験の道具がそのまま効きます。観察データが、層ごとに小さなRCTの寄せ集めに化けるのです。これが「似たもの同士を比較する」という直感の正体です。
相棒になるのが確率性、いわゆる**オーバーラップ(共通サポート)**です。$X$のどの値でも処置群と対照群の両方がいる($0 < e(x) < 1$)ことを要求します。ある領域に片方の群しかいなければ、比較相手が存在せず、推定は外挿に頼るしかありません。
そして、高次元の$X$をまるごと条件付けるのは大変なので、Rubinは傾向スコア $e(x) = \Pr(W_i = 1 \mid X_i = x)$ という1次元の要約を用意します。傾向スコアで条件付けるだけで非交絡性が保たれる(バランシングスコア)ことが示されていて、これがDay 7の主役です。今日は、この傾向スコアがオーバーラップの図に顔を出します。
手を動かす①:交絡があると引き算はズレる、Xで条件付けると戻る
まず、割付がランダムでない観察データを合成します。事前リスクスコア$X$が高い人ほど処置を受けやすく(自己選択)、しかも$X$が高い人ほどアウトカム$Y$も高い、という素直な交絡構造です。真の効果は$\tau = 3.0$に固定します。
import numpy as np
import pandas as pd
import statsmodels.formula.api as smf
rng = np.random.default_rng(6)
N = 4000
x = rng.normal(0, 1, N) # 事前リスクスコア(観測される交絡因子)
e = 1 / (1 + np.exp(-1.2 * x)) # 傾向スコア:Xが高いほど処置されやすい
w = rng.binomial(1, e) # 割付(ランダムではない)
y0 = 5 + 2.0 * x + rng.normal(0, 1, N) # ベースライン:Xが高いほどYも高い=交絡
y1 = y0 + 3.0 # 真の効果 τ = 3.0
y = np.where(w == 1, y1, y0) # 観測できるのは片方だけ
# (1) 素朴な引き算
naive = y[w == 1].mean() - y[w == 0].mean()
# (2) Xを5分位で層に切り、層内で処置群と対照群を比べて加重平均する
edges = np.quantile(x, np.linspace(0, 1, 6))
edges[0], edges[-1] = -np.inf, np.inf
bins = np.digitize(x, edges[1:-1])
num = den = 0.0
for b in range(5):
m = bins == b
num += (y[m & (w == 1)].mean() - y[m & (w == 0)].mean()) * m.sum()
den += m.sum()
strat = num / den
print("素朴な差 = %.2f" % naive)
print("X層別調整後 = %.2f (真値 3.0)" % strat)
素朴な差 = 4.92
X層別調整後 = 3.21 (真値 3.0)
素朴な引き算は 4.92 で、真値3.0を大きく上回ります。処置群のほうが$X$の高い人に偏っていて、その人たちはもともと$Y$が高いからです。ところが$X$の層に切ってから比べると、層の中では処置群と対照群の$X$がほぼそろい、割付がランダムに見えます。層内の差を集計すると 3.21 まで戻ります。同じことを回帰(y ~ w + x)でやると、係数は 3.00 とさらに真値に一致します。下の図の左が交絡の正体($X$分布の偏り)、右が3つの推定の比較です。
なぜ層に切るだけで戻るのでしょうか。同じデータを散布図で見ると腑に落ちます。下の図の左では、処置群(オレンジ)が$X$の高い側に寄っていて、群平均をそのまま引くと4.92になります。ところが右のように$X$の薄い層をひとつ取り出すと、その中では処置群と対照群がぴったり重なり、縦の差は約 3.06 で真値3.0に一致します。これが非交絡性の言う「同じ$X$の層の中では、割付がランダムに見える」の正体です。
手を動かす②:オーバーラップが崩れると、その領域は推定できない
非交絡性が成り立っていても、確率性(オーバーラップ)が崩れると調整は効きません。片方の群しかいない領域には、比較相手がいないからです。選択の強さ$\beta$を変えて、傾向スコアの重なり具合を見ます。
import numpy as np
def make(beta, seed=6):
rng = np.random.default_rng(seed)
x = rng.normal(0, 1, 4000)
e = 1 / (1 + np.exp(-beta * x)) # 傾向スコア=処置を受ける確率
w = rng.binomial(1, e)
return e, w
for beta, name in [(1.2, "重なり良"), (4.0, "重なり悪")]:
e, w = make(beta)
hi = e > 0.85 # 処置されやすい(傾向スコアが高い)領域
print("%s (β=%.1f): 高スコア帯にいる対照群の割合 = %.1f%%"
% (name, beta, 100 * (w[hi] == 0).mean()))
重なり良 (β=1.2): 高スコア帯にいる対照群の割合 = 10.3%
重なり悪 (β=4.0): 高スコア帯にいる対照群の割合 = 3.9%
選択がゆるやか($\beta=1.2$)なら、傾向スコアが高い領域にも対照群が 10.3% 残っていて、比較相手がいます。ところが選択が急($\beta=4$)だと、その割合は 3.9% まで落ちます。処置されやすい人はほぼ全員が処置され、対照群がほとんどいません。この領域の効果を語るには外挿するしかなく、その外挿を支える根拠はデータの中にありません。下の図の右で、赤く網掛けした端が「片方の群しかいない外挿ゾーン」です。この傾向スコアの重なりを整える話が、次回Day 7の傾向スコアにつながります。
手を動かす③:非交絡性は「検証できない」
ここが今日いちばん大事で、いちばん不安になる話です。非交絡性は、観測データからは検証できません(Imbens & Rubin 12.2.3)。試しに、割付を観測されない交絡因子$U$が決めている世界を作ります。真の効果はゼロ、私たちが手元で測れるのは$U$とは無関係な共変量$X$だけ、という設定です。
import numpy as np
import pandas as pd
import statsmodels.formula.api as smf
rng = np.random.default_rng(6)
N = 4000
u = rng.normal(0, 1, N) # 観測されない交絡因子(例:本人の意欲)
x = rng.normal(0, 1, N) # たまたま測れた別の共変量(交絡ではない)
w = rng.binomial(1, 1 / (1 + np.exp(-1.5 * u))) # 割付は観測されないUで決まる
y = 4 + 2.0 * u + 0.3 * x + rng.normal(0, 1, N) # 真の効果 τ = 0(処置は Y を動かさない)
adj = smf.ols("y ~ w + x", pd.DataFrame({"y": y, "w": w, "x": x})).fit()
lo, hi = adj.params["w"] - 1.96 * adj.bse["w"], adj.params["w"] + 1.96 * adj.bse["w"]
print("X調整後の効果 = %.2f (95%%CI [%.2f, %.2f])" % (adj.params["w"], lo, hi))
print("観測Xのバランス = %.2f (0に近い=見かけは均衡)" % (x[w == 1].mean() - x[w == 0].mean()))
print("真の効果 = 0.00")
X調整後の効果 = 2.15 (95%CI [2.03, 2.28])
観測Xのバランス = -0.01 (0に近い=見かけは均衡)
真の効果 = 0.00
真の効果はゼロなのに、手元の$X$で調整した効果は +2.15、しかも95%信頼区間はゼロから遠く離れ、統計的には「明確な効果あり」に見えます。おまけに観測$X$のバランスはほぼゼロで、共変量のバランスチェックは合格します。にもかかわらず、この+2.15は丸ごと交絡です。割付を決めた$U$を測れていないので、いくら$X$で調整しても、いくら$X$のバランスが良くても、これを見抜く手がかりはデータの中にありません。同じ観測データが、本当に効果+2.15の世界からも、効果ゼロで交絡だけの世界からも生まれえます。データは2つの世界を区別できないのです。
下の図がその手詰まりです。データが決められるのは「調整後の差=2.15」という合計だけで、それが本物の効果なのか(説明A)、丸ごと交絡なのか(説明B)という内訳は、どうやっても決められません。この分解を仮定ではなく感度分析で扱うのがDay 9です。
つまづき・誤解しやすい点
- 「$X$のバランスが良い=非交絡」ではありません。 バランスチェックが確かめるのは、あくまで観測した共変量の分布がそろっているかだけです。手を動かす③のように、割付を決めた本当の要因を測れていなければ、観測$X$がいくらそろっていても交絡は残ります。バランスは必要条件の確認であって、非交絡性の証明ではありません。
- 調整すべきは処置より前の変数だけです。 非交絡性のために条件付ける$X$は、処置に先立つ変数に限ります。処置の後で動く中間結果(処置の効果が乗った変数)を調整に入れると、因果の通り道をふさいでバイアスを生みます。これは合流点バイアスと並ぶ定番の事故で、Day 10以降のDAGで「何を調整してよいか」を機械的に判定します。
- 共変量は多いほど良い、とは限りません。 交絡を消すために関連する変数はできるだけ入れたいのが原則ですが、入れた変数がオーバーラップを壊すこともあります。処置群と対照群を完全に切り分けてしまう変数を足すと、$e(x)$がゼロや1に貼りつき、比較相手が消えます。非交絡性とオーバーラップは、しばしば綱引きの関係になります。
実務での使いどころ(空間データ/天地人の文脈)
- 自治体の「導入済み」を素朴に比べない。 新しい施策を先に入れた自治体は、人口規模や財政や意欲の点で、まだ入れていない自治体と体系的に違います。導入済みと未導入をそのまま引き算すると、手を動かす①の4.92のように施策の効果を過大評価します。人口・財政・老朽化率など、処置より前の共変量で条件付けてから比べるのが出発点です。
- オーバーラップを地図で確認する。 「先進的な大都市はほぼ全部が導入済み、小規模自治体はほぼ全部が未導入」だと、傾向スコアが両端に貼りつき、比較できる自治体が中間にしかいません。効果を語れるのは共通サポートのある範囲だけで、そこを外れた外挿は結論に含めない、と最初に線を引きます。
- 測れていない交絡を正直に書く。 手を動かす③が示すのは、衛星データや統計で測れる共変量をどれだけ足しても、首長の判断や現場の温度感のような観測できない要因が割付を動かしていれば、効果はバイアスを持つということです。だから「非交絡性を仮定した」と明記し、その仮定がどれだけ崩れると結論がひっくり返るかを見るのが感度分析(Day 9)です。
- 空間の交絡は独立ではありません。 隣り合う自治体は似た特性を共有し、施策も波及します。Xで条件付けても近隣の影響が残る空間交絡は、この章の枠組みを素直には満たしません。ここはDay 28(空間×因果)でまとめて掘ります。
参考(本棚)
- Imbens & Rubin『Causal Inference for Statistics, Social, and Biomedical Sciences』第3章 割付メカニズムの分類 — 個別性・確率性・非交絡性の3条件と正規な割付メカニズムの定義
- 同 第12章 非交絡処置割付 — 非交絡性 $W \perp (Y(0),Y(1)) \mid X$、傾向スコア、そして「非交絡性は検証不可能」(12.2.3)の議論。今日の下敷き
- 安井翔太『効果検証入門』 — 観察データでのセレクションバイアスと共変量調整の実務視点
- 図はすべて合成データです。真の効果 $\tau$ と隠れた交絡$U$の有無を私たちが握ることで、素朴推定のズレと「検証できなさ」を再現しました。生成スクリプトは元ノート側に添付しています。
次回予告(Day 7)
今日は「正しい$X$で条件付ければ、観察データも層の中ではRCT」という土台を置きました。でも共変量が増えると、$X$の値ごとに層を切るのは現実には無理です。空いた層、片方の群しかいない層が続出します。そこで登場するのが、今日オーバーラップの図に顔を出した傾向スコア $e(x) = \Pr(W=1 \mid X)$ です。高次元の交絡を1次元に潰し、その重みで観察データをならすのが次回Day 7、傾向スコアと**IPW(逆確率重み付け)**です。手計算のIPWからsklearnまで、手を動かして組みます。



