Mímisbrunnr知恵の泉

← 因果推論 一覧

🎓 レベル:標準 | 重要度:B(標準)

📎 前提:なぜRCTが黄金律か | 次:操作変数法と2SLS(IVの本格展開)

要点(BLUF)


1. 非遵守とは何か

割り当て ZZ(ランダム)と、実際に受けた処置 DD を区別する。

問題は、誰が遵守し誰がしないかが、その人の性質(健康度・意欲)と結びついていること。「自分の判断で飲んだ/飲まなかった」という選択は、結果に効く隠れた要因 UU と相関する。だから「実際に飲んだ人 vs 飲まなかった人」を比べると、ランダム化で断ったはずの交絡が裏口から戻ってくる

flowchart LR
    Z["割り当てZ(ランダム)"] --> D["実際の服用D"]
    U["潜在的な型U(遵守傾向・健康度)"] --> D
    U --> Y["結果Y"]
    D --> Y

ZZ はランダムなので UU と独立(ZUZ \perp U)。だが DDUU の影響を受ける。DD で群分けすると DUYD \leftarrow U \rightarrow Y のバックドアが開く。一方 ZZ で群分けすれば、ZZUU と独立なのでこの裏口は開かない。「ランダムなのは ZZ であって DD ではない」——これが全ての出発点だ。


2. 4つのタイプ(principal strata)

割り当てに対する反応で母集団を4つに分ける。

Z=0Z=0 のとき DDZ=1Z=1 のとき DD説明
遵守者(complier)0011割り当て通りに行動
常用者(always-taker)1111割り当てに関係なく必ず処置を受ける
不服用者(never-taker)0000割り当てに関係なく決して受けない
天邪鬼(defier)1100割り当てと逆。単調性で除外

各個体の型は観測できない(ZZ は片方しか見られない)。だが割り当てがランダムなので、4タイプの構成比は Z=1Z=1 群と Z=0Z=0 群で同じ。この対称性が後の識別を可能にする。


3. 3つの推定量:ITT・as-treated・LATE

**ITT(intention-to-treat)**は「割り当てられたこと」の効果。実際に飲んだかは問わない。

ITTY=E[YZ=1]E[YZ=0]\text{ITT}_Y = E[Y \mid Z=1] - E[Y \mid Z=0]

ZZ はランダムなので ITT は不偏。ただし測っているのは「処置を提供することの効果」で、非遵守者が混ざるぶん「処置を受けることの効果」より薄まる(希釈)。

第1段階は、割り当てが実際の服用をどれだけ動かしたか。単調性のもとでこれは遵守者の割合に等しい。

ITTD=E[DZ=1]E[DZ=0]=P(complier)\text{ITT}_D = E[D \mid Z=1] - E[D \mid Z=0] = P(\text{complier})

**LATE(local average treatment effect)= CACE(complier average causal effect)**は、遵守者に限った処置効果。Wald 推定量で識別される。

LATE=E[Y(1)Y(0)complier]=ITTYITTD\text{LATE} = E[\,Y(1) - Y(0) \mid \text{complier}\,] = \frac{\text{ITT}_Y}{\text{ITT}_D}

直観:ITT は「遵守者だけが処置の有無を切り替わる」効果を、全体(遵守者+非遵守者)で薄めたもの。だから遵守者割合 ITTD\text{ITT}_D で割り戻せば、遵守者あたりの効果に復元できる。

**as-treated(per-protocol の素朴版)**は、実際に飲んだか DD で比較する。

τ^as-treated=E[YD=1]E[YD=0]\hat\tau_{\text{as-treated}} = E[Y \mid D=1] - E[Y \mid D=0]

これは DUYD \leftarrow U \rightarrow Y の交絡を含むので偏る。常用者(元々健康で予後が良い)が D=1D=1 側に、不服用者が D=0D=0 側に固まるためだ。

識別の仮定(Angrist–Imbens–Rubin)

LATE が Wald 推定量で識別されるための条件:

  1. SUTVA(干渉なし・一貫性)。
  2. ランダム割り当てZ(,Y(0),Y(1))Z \perp (\text{型},\, Y(0), Y(1))。RCTで保証。
  3. 除外制約(exclusion restriction)ZZDD を通してのみ YY に影響(ZZ から YY への直接の矢印がない)。割り当てそれ自体が結果を動かさない。
  4. 単調性(monotonicity):天邪鬼がいない(割り当てが処置を減らす人はいない)。
  5. 関連性(relevance)ITTD0\text{ITT}_D \neq 0(遵守者が存在する=第1段階が効いている)。

4. コード:ITT・as-treated・Wald を比べる

遵守者 0.50.5/常用者 0.20.2/不服用者 0.30.3 の擬似データを作る。遵守者への真の効果(=LATE)を 44 と仕込み、ITT・as-treated・Wald がそれぞれ何を当てるかを見る。タイプによってベースラインと効果が異なる(異質)点に注意。

import numpy as np
import pandas as pd

# === 非遵守のある擬似データ:ITT・as-treated・Wald(LATE) を比べる ===
rng = np.random.default_rng(42)
n = 40000

# 真のLATE(遵守者[complier]に対する治療の効果)を 4 と仕込む
LATE_true = 4.0

# 3つのタイプ:遵守者 0.5 / 常用者(always-taker) 0.2 / 不服用者(never-taker) 0.3
types = rng.choice(["complier", "always", "never"], size=n, p=[0.5, 0.2, 0.3])

# Z:ランダムな割り当て(操作変数)。コイン投げ
Z = rng.binomial(1, 0.5, size=n)

# D:実際の服用。タイプと割り当てから決まる
D = np.zeros(n, dtype=int)
D[types == "always"] = 1                 # 常用者は割り当てに関係なく服用
D[types == "never"] = 0                  # 不服用者は割り当てに関係なく服用しない
D[types == "complier"] = Z[types == "complier"]   # 遵守者は割り当て通り

# 潜在結果:タイプ別ベースライン + 服用したときの効果(遵守者の効果= LATE_true)
baseline = np.where(types == "complier", 10.0,
            np.where(types == "always", 14.0, 8.0))   # 常用者は元々高い、不服用者は低い
effect = np.where(types == "complier", LATE_true,
           np.where(types == "always", 1.0, 10.0))    # タイプで効果が異なる(異質)
Y = baseline + effect * D + rng.normal(0.0, 2.0, size=n)

df = pd.DataFrame({"types": types, "Z": Z, "D": D, "Y": Y})

# ITT:割り当て Z による効果(実際に服用したかは問わない)
ITT_Y = df.loc[df.Z == 1, "Y"].mean() - df.loc[df.Z == 0, "Y"].mean()
# 第1段階(コンプライアンス率)= 割り当てが服用をどれだけ動かしたか
ITT_D = df.loc[df.Z == 1, "D"].mean() - df.loc[df.Z == 0, "D"].mean()
# Wald推定量 = ITT_Y / ITT_D(=LATE)
wald = ITT_Y / ITT_D
# as-treated(実際に服用したか D で素朴に比較)
as_treated = df.loc[df.D == 1, "Y"].mean() - df.loc[df.D == 0, "Y"].mean()

print(f"真のLATE(遵守者への効果)          = {LATE_true:+.3f}")
print(f"ITT  (割り当て効果, 服用問わず)    = {ITT_Y:+.3f}   ← 非遵守で希釈され過小")
print(f"第1段階 ITT_D(コンプライアンス率)  = {ITT_D:+.3f}   ← 遵守者の割合 0.5")
print(f"as-treated(実服用で素朴比較)       = {as_treated:+.3f}   ← 自己選択でバイアス")
print(f"Wald = ITT_Y / ITT_D                 = {wald:+.3f}   ← LATEを回収")

# IV(操作変数)としての等価性:cov(Y,Z)/cov(D,Z) も同じ値
iv = np.cov(Y, Z, ddof=1)[0, 1] / np.cov(D, Z, ddof=1)[0, 1]
print(f"IV  cov(Y,Z)/cov(D,Z)                = {iv:+.3f}   ← Waldと一致(2SLSの単純版)")

出力:

真のLATE(遵守者への効果)          = +4.000
ITT  (割り当て効果, 服用問わず)    = +1.982   ← 非遵守で希釈され過小
第1段階 ITT_D(コンプライアンス率)  = +0.496   ← 遵守者の割合 0.5
as-treated(実服用で素朴比較)       = +5.566   ← 自己選択でバイアス
Wald = ITT_Y / ITT_D                 = +4.000   ← LATEを回収
IV  cov(Y,Z)/cov(D,Z)                = +4.000   ← Waldと一致(2SLSの単純版)

出力の意味:3つの推定量がきれいに分かれた。ITT =1.98=1.98 は不偏だが「割り当ての効果」であって、非遵守者が混ざるぶん真の処置効果 44 より小さい(希釈)。as-treated =5.6=5.6 は真値を大きく超える——常用者(ベースライン 1414 で高い)が D=1D=1 側に集まる自己選択バイアスだ。Wald =4.0=4.0 だけが真の LATE を回収した。ITTY/ITTD=1.98/0.496=4.0\text{ITT}_Y/\text{ITT}_D = 1.98/0.496 = 4.0 という割り算が効いている。最後の行は、Wald が共分散比 Cov(Y,Z)/Cov(D,Z)\operatorname{Cov}(Y,Z)/\operatorname{Cov}(D,Z) と同一=操作変数法の単純な姿であることを示す。


5. コード:標本分布で「何を中心にするか」を確認

1回の推定では偶然当たることもある。多数回くり返して、各推定量が何を中心にするかを見る。

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

# === モンテカルロ:ITT・as-treated・Wald の標本分布を比べる ===
rng = np.random.default_rng(99)
LATE_true = 4.0
n = 3000
n_rep = 3000

itt_list, attr_list, wald_list = [], [], []

for _ in range(n_rep):
    types = rng.choice(["complier", "always", "never"], size=n, p=[0.5, 0.2, 0.3])
    Z = rng.binomial(1, 0.5, size=n)
    D = np.zeros(n, dtype=int)
    D[types == "always"] = 1
    D[types == "complier"] = Z[types == "complier"]
    baseline = np.where(types == "complier", 10.0,
                np.where(types == "always", 14.0, 8.0))
    effect = np.where(types == "complier", LATE_true,
               np.where(types == "always", 1.0, 10.0))
    Y = baseline + effect * D + rng.normal(0.0, 2.0, size=n)

    itt_y = Y[Z == 1].mean() - Y[Z == 0].mean()
    itt_d = D[Z == 1].mean() - D[Z == 0].mean()
    itt_list.append(itt_y)
    wald_list.append(itt_y / itt_d)
    attr_list.append(Y[D == 1].mean() - Y[D == 0].mean())

itt_arr = np.array(itt_list)
attr_arr = np.array(attr_list)
wald_arr = np.array(wald_list)

print(f"真のLATE                = {LATE_true:+.3f}")
print(f"ITT       平均={itt_arr.mean():+.3f}  (希釈され過小)")
print(f"as-treated平均={attr_arr.mean():+.3f}  (自己選択でバイアス)")
print(f"Wald      平均={wald_arr.mean():+.3f}  SD={wald_arr.std():.3f}  (LATEを回収)")

plt.figure(figsize=(8, 4.5))
plt.hist(itt_arr, bins=40, alpha=0.55, label="ITT")
plt.hist(attr_arr, bins=40, alpha=0.55, label="as-treated")
plt.hist(wald_arr, bins=40, alpha=0.55, label="Wald (LATE)")
plt.axvline(LATE_true, color="red", linestyle="--", linewidth=2, label="真のLATE=4")
plt.xlabel("推定値")
plt.ylabel("頻度")
plt.title("ITT・as-treated・Wald の標本分布:Waldだけが真のLATEを中心にする")
plt.legend()
plt.tight_layout()
plt.show()

出力:

真のLATE                = +4.000
ITT       平均=+1.997  (希釈され過小)
as-treated平均=+5.535  (自己選択でバイアス)
Wald      平均=+3.992  SD=0.169  (LATEを回収)

出力の意味:3000回の反復で、ITT は 2.02.0、as-treated は 5.55.5 を中心に固まる——どちらも nn を増やしても真の LATE 44 には寄らない(系統的なズレ)。Wald だけが 3.993.99 = 真値を中心にする。ヒストグラムでも Wald の山だけが赤い破線(=4=4)に乗る。「不偏な ITT」と「真の処置効果」は別物で、両者を橋渡しするのが第1段階による割り戻し(Wald)だと視覚的に分かる。


6. ITT は無意味か:使い分け

希釈されるからといって ITT が劣るわけではない。問いが違うだけだ。

LATE の弱点は 「遵守者」という観測できない部分集団の効果である点(外的妥当性)。遵守者割合が小さい(弱い操作変数)と Wald の分母が小さく、推定が不安定になる。


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

(1)「飲んだ人だけ/プロトコルを守った人だけ」で分析する誘惑 最もやりがちな誤り。DD(実際の服用)や遵守状況で対象を絞った瞬間、ランダム化の保護が消えて交絡が戻る。ランダムなのは ZZ だけ、を忘れない。

(2)除外制約は検証できない仮定 Wald が LATE になる鍵は「ZZDD を通してのみ YY に効く」こと。割り当てを知ること自体が行動を変える(プラセボ効果、ホーソン効果)と崩れる。盲検化はこの仮定を守るための装置。

(3)弱い操作変数(遵守率が低い) ITTD\text{ITT}_D が小さいと ITTY/ITTD\text{ITT}_Y/\text{ITT}_D00 に近い数での割り算になり、ばらつき・偏りが拡大する。第1段階の強さ(遵守率)を必ず確認する。詳細は 操作変数法と2SLS

(4)LATE を ATE と混同する LATE は「遵守者」限定の効果。常用者・不服用者には外挿できない。本コードでも全体平均効果は 0.5×4+0.2×1+0.3×10=5.20.5\times4+0.2\times1+0.3\times10=5.2 で LATE =4=4 と一致しない。データから識別できるのは遵守者ぶんだけ、という限界を明示する。


関連ノート