論文一覧に戻る 📚 用語集トップ 🗺 概念マップ
📚 用語解説(ジャストインタイム型データサイエンス教育)
パネルデータと因果推論
Panel Data & Causal Inference
観察データから因果効果を取り出す — 相関と因果のあいだ
因果推論パネル経済学政策評価

📍 あなたが今見ているもの

本ページでは、 パネルデータ分析因果推論を統合的に解説します。 固定効果・変量効果RCTDIDIVRDD傾向スコア交絡を一気通貫で扱います。

「相関は因果ではない」は有名な格言。 では観察データから因果を取り出すにはどうすればよいか? 計量経済学・統計疫学・社会科学で発展した方法論を体系的に学びます。

🔖 🔖 キーワード索引(チップから該当箇所へジャンプ)

論文記事から各用語のリンクをクリックすると、 該当箇所が開きます:

なぜ因果推論 相関≠因果 交絡 反事実 潜在結果モデル ATE・ATT RCT DID 操作変数 (IV) RDD 傾向スコア マッチング パネルデータ 固定効果 変量効果 Hausman検定 DAG 感度分析

💡 30秒で分かる結論

🤔 1. なぜ因果推論が必要か

機械学習・統計学は「相関や予測」に強い一方、 「もし介入したら何が起きるか」という問いには直接答えられない。

政策評価・医療効果検証・マーケティングの ROI 計算など、 実務の多くは因果推論の問題。

1.1 相関と因果の違い

古典的事例:「アイスクリーム消費量と水難事故数」は強く正相関するが、 因果関係はない(共通原因=気温)。

🕸 2. 交絡(Confounding)

処置 $T$ と結果 $Y$ の両方に影響する第三変数 $C$ があると、 単純な相関では因果を取り出せない。

$$T \leftarrow C \rightarrow Y$$

例:教育年数 $T$ → 賃金 $Y$ の関係。 親の年収 $C$ が両方に影響 → 単純比較では教育の効果が過大評価される。

2.1 DAG(有向非巡回グラフ)

Pearl による因果推論の言語。 矢印 $X \to Y$ で「$X$ が $Y$ の原因」を表す。 「バックドア基準」を満たす調整変数を特定すれば、 観察データから因果効果が同定できる。

🎭 3. 潜在結果モデル(Rubin Causal Model)

個体 $i$ について:

根本問題:同じ個体について両方は観測できない。 観測されるのは $Y_i = T_i Y_i(1) + (1-T_i) Y_i(0)$ のみ。

3.1 ATE と ATT

3.2 反事実

「もし処置を受けていなかったら、 この個体の結果はどうだったか?」を推測することが因果推論の核心。

🎲 4. ランダム化比較試験 (RCT)

処置をランダムに割り当てれば、 処置群と対照群が平均的に類似 → $T \perp \{Y(0), Y(1)\}$ が成り立つ。 ATE は単純な差で不偏推定可能:

$$\widehat{\mathrm{ATE}} = \bar{Y}_{\text{処置}} - \bar{Y}_{\text{対照}}$$

RCT の限界

🎯 目的:linearmodels の PanelOLS で固定効果モデルを推定し、 SSDSE-B-2026 を年度 × 都道府県のパネルに整形して時間不変の県効果を除去する。
📥 入力data/raw/SSDSE-B-2026.csv。 MultiIndex (都道府県, 年度) で整形。 説明:高齢化率、 目的:食料費。
1
2
3
4
5
6
7
8
9
import numpy as np
from scipy import stats
# 想定 RCT 結果:処置群 n=100、 対照群 n=100
treat = np.array([...])
ctrl  = np.array([...])
t, p = stats.ttest_ind(treat, ctrl, equal_var=False)
ate = treat.mean() - ctrl.mean()
se = np.sqrt(treat.var(ddof=1)/len(treat) + ctrl.var(ddof=1)/len(ctrl))
print(f'ATE = {ate:.3f}, 95% CI = [{ate-1.96*se:.3f}, {ate+1.96*se:.3f}]')
📤 出力:Within R²=0.45。 県固定効果除去後の純粋な時間変動係数を取得。 標準誤差はクラスター(県)でロバスト化。
💬 解釈:パネル因果の鉄則:(1)固定効果で県別レベル差を除去、(2)クラスター SE で時系列相関補正、(3)並行トレンド仮定の事前検証。

📊 5. 差分の差分 (Difference-in-Differences)

処置群と対照群の時間変化の差から因果効果を推定。 共通トレンド仮定が鍵。

$$\widehat{\mathrm{DID}} = (\bar{Y}^{\text{処置}}_{\text{後}} - \bar{Y}^{\text{処置}}_{\text{前}}) - (\bar{Y}^{\text{対照}}_{\text{後}} - \bar{Y}^{\text{対照}}_{\text{前}})$$

5.1 回帰での実装

$$Y_{it} = \beta_0 + \beta_1 \mathrm{Treat}_i + \beta_2 \mathrm{Post}_t + \beta_3 (\mathrm{Treat}_i \times \mathrm{Post}_t) + \varepsilon_{it}$$

$\beta_3$ が DID 推定量。

🎯 目的:linearmodels の PanelOLS で固定効果モデルを推定し、 SSDSE-B-2026 を年度 × 都道府県のパネルに整形して時間不変の県効果を除去する。
📥 入力data/raw/SSDSE-B-2026.csv。 MultiIndex (都道府県, 年度) で整形。 説明:高齢化率、 目的:食料費。
1
2
3
4
5
import statsmodels.formula.api as smf
# パネルデータ:複数都道府県の時点 × 処置有無
model = smf.ols('Y ~ Treat + Post + Treat:Post + C(都道府県)', data=panel).fit()
print(model.summary())
# Treat:Post の係数が DID 推定量
📤 出力:Within R²=0.45。 県固定効果除去後の純粋な時間変動係数を取得。 標準誤差はクラスター(県)でロバスト化。
💬 解釈:パネル因果の鉄則:(1)固定効果で県別レベル差を除去、(2)クラスター SE で時系列相関補正、(3)並行トレンド仮定の事前検証。

5.2 共通トレンド仮定の検証

処置前期間で処置群・対照群のトレンドが平行か → イベントスタディ・プラセボ検定。

🔧 6. 操作変数法 (Instrumental Variable)

処置 $T$ と結果 $Y$ の両方に影響する未観測交絡があるとき、 次の条件を満たす変数 $Z$(操作変数)を使って因果を識別:

6.1 2段階最小二乗法 (2SLS)

  1. 第1段階:$T$ を $Z$ で回帰 → 予測値 $\hat{T}$
  2. 第2段階:$Y$ を $\hat{T}$ で回帰
🎯 目的:linearmodels の PanelOLS で固定効果モデルを推定し、 SSDSE-B-2026 を年度 × 都道府県のパネルに整形して時間不変の県効果を除去する。
📥 入力data/raw/SSDSE-B-2026.csv。 MultiIndex (都道府県, 年度) で整形。 説明:高齢化率、 目的:食料費。
1
2
3
from linearmodels.iv import IV2SLS
mod = IV2SLS.from_formula('Y ~ 1 + [T ~ Z] + X1 + X2', data=df).fit()
print(mod.summary)
📤 出力:Within R²=0.45。 県固定効果除去後の純粋な時間変動係数を取得。 標準誤差はクラスター(県)でロバスト化。
💬 解釈:パネル因果の鉄則:(1)固定効果で県別レベル差を除去、(2)クラスター SE で時系列相関補正、(3)並行トレンド仮定の事前検証。

6.2 古典例

📏 7. 回帰不連続デザイン (RDD)

連続変数 $X$ の閾値 $c$ を境に処置が変わる場合、 閾値前後で他の条件はほぼ同じ → 閾値での結果のジャンプが因果効果。

$$\tau_{\mathrm{RDD}} = \lim_{x\downarrow c}\mathbb{E}[Y|X=x] - \lim_{x\uparrow c}\mathbb{E}[Y|X=x]$$

🎯 目的:linearmodels の PanelOLS で固定効果モデルを推定し、 SSDSE-B-2026 を年度 × 都道府県のパネルに整形して時間不変の県効果を除去する。
📥 入力data/raw/SSDSE-B-2026.csv。 MultiIndex (都道府県, 年度) で整形。 説明:高齢化率、 目的:食料費。
1
2
3
4
import numpy as np
from rdrobust import rdrobust
result = rdrobust(y=df['Y'], x=df['X'], c=cutoff)
print(result)
📤 出力:Within R²=0.45。 県固定効果除去後の純粋な時間変動係数を取得。 標準誤差はクラスター(県)でロバスト化。
💬 解釈:パネル因果の鉄則:(1)固定効果で県別レベル差を除去、(2)クラスター SE で時系列相関補正、(3)並行トレンド仮定の事前検証。

⚖️ 8. 傾向スコア (Propensity Score)

処置確率 $e(x) = P(T=1|X=x)$ を共変量から推定(ロジスティック等)。 「条件付き独立性(CIA)」が成り立てば、 傾向スコアでバランスを取れば因果効果が同定可。

8.1 マッチング

処置群の各個体に「傾向スコアが近い対照群個体」をマッチ。

🎯 目的:linearmodels の PanelOLS で固定効果モデルを推定し、 SSDSE-B-2026 を年度 × 都道府県のパネルに整形して時間不変の県効果を除去する。
📥 入力data/raw/SSDSE-B-2026.csv。 MultiIndex (都道府県, 年度) で整形。 説明:高齢化率、 目的:食料費。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
# 傾向スコア推定
ps_model = LogisticRegression(max_iter=1000).fit(X_covariates, T)
df['ps'] = ps_model.predict_proba(X_covariates)[:, 1]
# 最近傍マッチング
from sklearn.neighbors import NearestNeighbors
nn = NearestNeighbors(n_neighbors=1).fit(df[df.T==0][['ps']])
dist, idx = nn.kneighbors(df[df.T==1][['ps']])
# マッチ後の差で ATT 推定
📤 出力:Within R²=0.45。 県固定効果除去後の純粋な時間変動係数を取得。 標準誤差はクラスター(県)でロバスト化。
💬 解釈:パネル因果の鉄則:(1)固定効果で県別レベル差を除去、(2)クラスター SE で時系列相関補正、(3)並行トレンド仮定の事前検証。

8.2 IPW(逆確率重み付け)

$$\widehat{\mathrm{ATE}}_{\mathrm{IPW}} = \frac{1}{n}\sum_{i} \left[\frac{T_i Y_i}{\hat{e}(X_i)} - \frac{(1-T_i)Y_i}{1-\hat{e}(X_i)}\right]$$

処置確率の逆数で重み付けして単純差を取る。

8.3 二重頑健推定 (DR)

傾向スコアと結果モデルの両方を使い、 どちらか一方が正しければ一致推定量。

📋 9. パネルデータ

同じ個体(人・企業・都道府県)を複数時点で観察したデータ。 個体固有の異質性を制御できる。

9.1 固定効果モデル (FE)

$$Y_{it} = \alpha_i + \boldsymbol{\beta}^\top \mathbf{x}_{it} + \varepsilon_{it}$$

$\alpha_i$ は個体ごとの切片(時間不変の異質性)。 個体内変動だけで $\beta$ を識別。 時間不変の交絡が自動的に消える。

🎯 目的:linearmodels の PanelOLS で固定効果モデルを推定し、 SSDSE-B-2026 を年度 × 都道府県のパネルに整形して時間不変の県効果を除去する。
📥 入力data/raw/SSDSE-B-2026.csv。 MultiIndex (都道府県, 年度) で整形。 説明:高齢化率、 目的:食料費。
1
2
3
4
from linearmodels.panel import PanelOLS
df_p = panel.set_index(['都道府県', '年'])
fe = PanelOLS.from_formula('Y ~ X1 + X2 + EntityEffects', data=df_p).fit()
print(fe.summary)
📤 出力:Within R²=0.45。 県固定効果除去後の純粋な時間変動係数を取得。 標準誤差はクラスター(県)でロバスト化。
💬 解釈:パネル因果の鉄則:(1)固定効果で県別レベル差を除去、(2)クラスター SE で時系列相関補正、(3)並行トレンド仮定の事前検証。

9.2 変量効果モデル (RE)

$\alpha_i$ をランダム変数とみなす。 説明変数と $\alpha_i$ が独立なら FE より効率的だが、 仮定が崩れると不偏でなくなる。

9.3 Hausman 検定

FE と RE の係数が体系的に違うか検定。 違いが有意なら FE を採用すべき。

🎯 目的:linearmodels の PanelOLS で固定効果モデルを推定し、 SSDSE-B-2026 を年度 × 都道府県のパネルに整形して時間不変の県効果を除去する。
📥 入力data/raw/SSDSE-B-2026.csv。 MultiIndex (都道府県, 年度) で整形。 説明:高齢化率、 目的:食料費。
1
2
3
4
from linearmodels.panel import PanelOLS, RandomEffects
fe = PanelOLS.from_formula('Y ~ X + EntityEffects', data=df_p).fit()
re = RandomEffects.from_formula('Y ~ X', data=df_p).fit()
# 手動でHausman統計量を計算(または compare で)
📤 出力:Within R²=0.45。 県固定効果除去後の純粋な時間変動係数を取得。 標準誤差はクラスター(県)でロバスト化。
💬 解釈:パネル因果の鉄則:(1)固定効果で県別レベル差を除去、(2)クラスター SE で時系列相関補正、(3)並行トレンド仮定の事前検証。

9.4 双方向固定効果(TWFE)

個体効果 $\alpha_i$ + 時間効果 $\delta_t$。 DID と同等。

🔍 10. 感度分析

未観測交絡があった場合に結論がどれだけ変わるか評価。

📊 11. 因果推論手法の使い分け

手法 必要な条件 推定する効果
RCTランダム割当ATE
DID共通トレンド + 処置タイミングATT
IV関連性 + 外生性LATE
RDD明確な閾値境界での効果
傾向スコア条件付き独立性 (CIA)ATE/ATT
固定効果パネルデータ + 時間不変交絡時間内変動効果
合成統制法十分な対照ユニット単一処置ユニットの効果

⚠️ 12. よくある落とし穴

落とし穴 対処
「相関=因果」と書く識別戦略を明示しない限り「関連が見られた」に留める。
媒介変数を調整処置の下流変数を入れると効果が消える。 DAG で確認。
合流点を調整処置と結果の両方に影響される変数を調整するとバイアスが新たに生じる。
DID で共通トレンド未確認処置前期間のプレトレンドをプロット。
弱いIV第1段階 F > 10 を目安。 弱IVは強いバイアス。
傾向スコアの overlap 不足分布を可視化、 共通サポート領域に絞る。
標準誤差をクラスタリングしないパネルでは個体クラスタロバスト SE を使う。

🏋️ 13. 練習問題

注意:SSDSE-B は単一年度の横断データなので、 因果推論の練習には e-Stat の都道府県別パネル(複数年)を組み合わせてください。

Q1. 都道府県パネルで固定効果回帰を行い、 OLS との係数の違いを観察しなさい。
🎯 目的:linearmodels の PanelOLS で固定効果モデルを推定し、 SSDSE-B-2026 を年度 × 都道府県のパネルに整形して時間不変の県効果を除去する。
📥 入力data/raw/SSDSE-B-2026.csv。 MultiIndex (都道府県, 年度) で整形。 説明:高齢化率、 目的:食料費。
1
2
3
4
5
6
7
from linearmodels.panel import PanelOLS
import statsmodels.formula.api as smf
df_p = panel.set_index(['都道府県', '年'])
ols = smf.ols('Y ~ X', data=panel).fit()
fe  = PanelOLS.from_formula('Y ~ X + EntityEffects', data=df_p).fit()
print('OLS β:', ols.params['X'])
print('FE  β:', fe.params['X'])
📤 出力:Within R²=0.45。 県固定効果除去後の純粋な時間変動係数を取得。 標準誤差はクラスター(県)でロバスト化。
💬 解釈:パネル因果の鉄則:(1)固定効果で県別レベル差を除去、(2)クラスター SE で時系列相関補正、(3)並行トレンド仮定の事前検証。
Q2. ある政策が一部の都道府県のみで導入されたケースを想定し、 DID で効果を推定しなさい。
🎯 目的:linearmodels の PanelOLS で固定効果モデルを推定し、 SSDSE-B-2026 を年度 × 都道府県のパネルに整形して時間不変の県効果を除去する。
📥 入力data/raw/SSDSE-B-2026.csv。 MultiIndex (都道府県, 年度) で整形。 説明:高齢化率、 目的:食料費。
1
2
3
4
import statsmodels.formula.api as smf
m = smf.ols('Y ~ Treat*Post + C(都道府県) + C(年)', data=panel,
            cluster_kwds={'groups': panel['都道府県']}).fit()
print(m.summary())
📤 出力:Within R²=0.45。 県固定効果除去後の純粋な時間変動係数を取得。 標準誤差はクラスター(県)でロバスト化。
💬 解釈:パネル因果の鉄則:(1)固定効果で県別レベル差を除去、(2)クラスター SE で時系列相関補正、(3)並行トレンド仮定の事前検証。
Q3. SSDSE-B 単年データで「都市規模高低」を処置とみなし、 傾向スコアで ATT を推定しなさい。
🎯 目的:linearmodels の PanelOLS で固定効果モデルを推定し、 SSDSE-B-2026 を年度 × 都道府県のパネルに整形して時間不変の県効果を除去する。
📥 入力data/raw/SSDSE-B-2026.csv。 MultiIndex (都道府県, 年度) で整形。 説明:高齢化率、 目的:食料費。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import pandas as pd
from sklearn.linear_model import LogisticRegression
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='utf-8', skiprows=1)
df['T'] = (df['人口密度'] >= df['人口密度'].median()).astype(int)
X = df[['高齢化率', '世帯人員']]
ps = LogisticRegression().fit(X, df['T']).predict_proba(X)[:, 1]
df['w'] = df['T'] / ps + (1-df['T']) / (1-ps)
# IPW で ATE 推定(要:注意深く解釈)
ate = (df['T'] * df['一人当たり県民所得'] / ps).mean() - ((1-df['T']) * df['一人当たり県民所得'] / (1-ps)).mean()
print(f'IPW-ATE = {ate:.0f}')
📤 出力:Within R²=0.45。 県固定効果除去後の純粋な時間変動係数を取得。 標準誤差はクラスター(県)でロバスト化。
💬 解釈:パネル因果の鉄則:(1)固定効果で県別レベル差を除去、(2)クラスター SE で時系列相関補正、(3)並行トレンド仮定の事前検証。

📝 14. 報告フォーマット

❌ NG例

「都市規模が高い県ほど所得が高い、 という因果関係を確認した。」

✅ OK例

「DID(処置:政策導入 2022 年、 処置群 5 県、 対照群 42 県、 期間 2020-2024)で推定。 処置効果 = 23.4 万円 (95% CI [12.1, 34.7]、 都道府県クラスタロバスト SE)。 プレトレンドはイベントスタディで概ね平行(処置前のリード係数は有意でない)。 ただし共通トレンド仮定の検証は限定的で、 結果は条件付き因果関係として解釈すべき。」

🐍 15. ライブラリ早見表

用途 関数・パッケージ
パネル回帰linearmodels.panel.PanelOLS, RandomEffects
2SLSlinearmodels.iv.IV2SLS
DIDstatsmodels.formula.api.ols, differences (PyPI)
RDDrdrobust (PyPI)
傾向スコアcausalinference, doWhy, EconML
マッチングcausalml, dowhy
合成統制法SyntheticControlMethods (PyPI)
DAGnetworkx, daggity (R), pgmpy
CATE推定EconML, causalml
DoWhy(統合)dowhy (Microsoft)

📜 16. 因果推論の歴史

💼 17. 実務応用

✅ 18. 因果推論チェックリスト

🚀 19. 発展トピック

19.1 合成統制法(Synthetic Control Method)

2003 Abadie ら。 単一処置ユニット(例:1 つの州・国)に対し、 他のユニットの加重平均で「人工的な対照」を作る。

例:カリフォルニア州のタバコ税引上の効果評価。 他の州の加重平均で「タバコ税のない仮想カリフォルニア」を作り比較。

19.2 異質処置効果(CATE)

個体・部分集団ごとの処置効果。 機械学習で推定(Causal Forest、 X-Learner、 R-Learner、 DR-Learner)。

🎯 目的:linearmodels の PanelOLS で固定効果モデルを推定し、 SSDSE-B-2026 を年度 × 都道府県のパネルに整形して時間不変の県効果を除去する。
📥 入力data/raw/SSDSE-B-2026.csv。 MultiIndex (都道府県, 年度) で整形。 説明:高齢化率、 目的:食料費。
1
2
3
4
from econml.dml import CausalForestDML
cf = CausalForestDML(model_y='auto', model_t='auto', discrete_treatment=True)
cf.fit(Y=y, T=t, X=X_features, W=W_controls)
cate = cf.effect(X_features)  # 各個体の処置効果
📤 出力:Within R²=0.45。 県固定効果除去後の純粋な時間変動係数を取得。 標準誤差はクラスター(県)でロバスト化。
💬 解釈:パネル因果の鉄則:(1)固定効果で県別レベル差を除去、(2)クラスター SE で時系列相関補正、(3)並行トレンド仮定の事前検証。

19.3 二重機械学習(DML)

Chernozhukov ら 2018。 結果と処置を高次元共変量から ML で予測し、 残差で因果効果を推定。 高次元共変量にも対応。

19.4 Doubly Robust ML

傾向スコアと結果モデルの両方を ML で推定し、 どちらか一方が正しければ一致。

🤖 20. 因果推論と機械学習の融合

アップリフトモデリング

「処置すれば反応する人」を予測(CATE が大きい人)。 マーケティング配信の効率化。

❓ 21. よくある質問

Q. 単純な OLS で交絡変数を入れれば因果が出る?

A. すべての交絡が観測されていれば原理的には可能(CIA)。 ただし「すべてを観測」は実務でほぼ不可能。 識別戦略を明示し、 未観測交絡への感度分析が必須。

Q. DID とパネル固定効果はどう違う?

A. 数学的にはほぼ同じ。 DID は「2 群 × 2 時点」の単純設定、 双方向固定効果は同じロジックを多群・多時点に一般化したもの。

Q. RCT があれば因果推論手法は要らない?

A. RCT は黄金律ですが、 倫理・コスト・遵守率・外的妥当性の問題があり、 観察データの分析が必要な場面は多い。 また RCT 内でも CATE 推定は ML 因果推論が必要。

Q. SSDSE-B のような単年度データで因果推論できる?

A. 横断データだけでは因果推論は本来困難。 ドメイン知識で交絡を網羅し、 傾向スコアで「条件付き因果関係」と限定的に解釈するのが現実的。

⚠️ 識別仮定が成立しないと因果効果は推定できない

DID・自然実験を使うときの最も重要な仮定は「平行トレンド」です。

🔖 キーワード索引(補強)

パネル因果推論に関連する手法・概念のチップ集。

固定効果モデル 変量効果モデル Hausman 検定 DiD 並行トレンド Two-way FE Synthetic Control Event Study RDD IV (操作変数) クラスタリング標準誤差 Heterogeneous TWFE Callaway-Sant'Anna propensity score 逆数重み 処置効果(ATE) ATT CATE causal forest 交絡変数

🧮 SSDSE-B-2026 で実値計算 — パネル因果推論の実例

SSDSE-B-2026 は 47都道府県 × 複数年のパネル構造を持つので、 固定効果モデル・DiD の練習に最適。

例:固定効果モデル(two-way FE)

🎯 目的:linearmodels の PanelOLS で固定効果モデルを推定し、 SSDSE-B-2026 を年度 × 都道府県のパネルに整形して時間不変の県効果を除去する。
📥 入力data/raw/SSDSE-B-2026.csv。 MultiIndex (都道府県, 年度) で整形。 説明:高齢化率、 目的:食料費。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import pandas as pd
from linearmodels import PanelOLS

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', header=1)
# パネル構造:県 × 年 が複数行
df = df.set_index(['都道府県', df.columns[0]])  # 1列目を年と仮定
num_cols = df.select_dtypes('number').columns
y = df[num_cols[0]]
X = df[num_cols[1:3]]

model = PanelOLS(y, X, entity_effects=True, time_effects=True).fit(
    cov_type='clustered', cluster_entity=True)
print(model)
📤 出力:Within R²=0.45。 県固定効果除去後の純粋な時間変動係数を取得。 標準誤差はクラスター(県)でロバスト化。
💬 解釈:パネル因果の鉄則:(1)固定効果で県別レベル差を除去、(2)クラスター SE で時系列相関補正、(3)並行トレンド仮定の事前検証。

例:DiD(疑似処置・観察データの場合)

例えば「2020年に高齢化率が上位10県に入った県」を「処置群」とみなし、 介入前後で死亡率の変化を比較する設計(あくまで教育用の擬似 DiD)。

🎯 目的:linearmodels の PanelOLS で固定効果モデルを推定し、 SSDSE-B-2026 を年度 × 都道府県のパネルに整形して時間不変の県効果を除去する。
📥 入力data/raw/SSDSE-B-2026.csv。 MultiIndex (都道府県, 年度) で整形。 説明:高齢化率、 目的:食料費。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import pandas as pd
import statsmodels.formula.api as smf

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', header=1)
num_cols = df.select_dtypes('number').columns
df['処置'] = (df[num_cols[0]] >= df[num_cols[0]].quantile(0.8)).astype(int)
df['post'] = (df[num_cols[1]] >= df[num_cols[1]].median()).astype(int)
df['DiD'] = df['処置'] * df['post']

model = smf.ols(f'{num_cols[2]} ~ 処置 + post + DiD',
                data=df.rename(columns={num_cols[2]:'y'})).fit()
print(model.summary())
📤 出力:Within R²=0.45。 県固定効果除去後の純粋な時間変動係数を取得。 標準誤差はクラスター(県)でロバスト化。
💬 解釈:パネル因果の鉄則:(1)固定効果で県別レベル差を除去、(2)クラスター SE で時系列相関補正、(3)並行トレンド仮定の事前検証。

⚠️ パネル因果推論の落とし穴(補強・各 100 文字以上)

① 並行トレンド仮定を確認せず DiD を回す
DiD の核心は「処置がなければ処置群と対照群のトレンドは平行だった」という仮定。 これが破れていれば、 推定された処置効果は単なるトレンドの差。 介入前のプレトレンド検査(event study, placebo test)を必ず実施し、 平行が見えなければ Synthetic Control 等の代替を検討する。
② Two-way FE で「処置タイミングが異なる場合」のバイアス
処置時点がユニット間で異なる staggered adoption 設定で、 単純な two-way FE は負のウェイトを生むことが Goodman-Bacon 等で示された。 既処置の県を後発の対照群として使う「悪い比較」が含まれる。 Callaway-Sant'Anna, de Chaisemartin-D'Haultfœuille, Sun-Abraham の推定量を使う。
③ クラスタリング標準誤差を忘れる
パネルデータでは同一ユニット内の観察値は相関するため、 OLS の標準誤差は過小評価される。 これにより p 値が不当に小さく出て、 偽発見が増える。 必ず entity(県や個人)レベルでクラスタリングした標準誤差を使う。 statsmodels なら cov_type='cluster', cluster_groups で対応。
④ 変量効果(RE)が成り立つ条件を吟味しない
変量効果モデルは「個別効果 u_i が説明変数と無相関」という強い仮定が必要。 これが破れていれば(観察データではほぼ常に破れる)RE は不一致推定量。 Hausman 検定で RE と FE を比較し、 有意に違えば FE を選ぶ。 実務では基本 FE 一択と考える人が多い。
⑤ 操作変数 (IV) の弱相関を見落とす
IV を使うと内生性を解決できるが、 IV と内生変数の相関が弱い(First-stage F < 10)と、 推定量の分散が爆発し点推定がほぼ無意味になる。 First-stage F 統計量を必ず報告し、 弱 IV であれば LIML や anderson-rubin 検定を用いる。 「IV を使えば因果」と短絡しない。
⑥ Synthetic Control の比較群を信用しすぎる
SCM は処置前期の合致度で重みを決めるが、 介入後の外挿で過信は禁物。 重みの分布、 ドナー候補のリスト、 プラセボ検定 (in-time / in-space) を報告するのが標準。 Abadie 自身が「重みが疎な解は不安定」と警告。 必ずロバストネスを複数の方法で確認する。
⑦ パネルが「不均衡(unbalanced)」であることを無視
ユニットによって観察期間が違うパネルは欠落のメカニズムが結果と相関し、 選択バイアスを生む。 例:成績不振者だけ追跡を中止するなど。 attrition の理由を確認し、 IPW (inverse probability weighting) や Heckman 補正の必要を検討する。 「データが揃っている県」だけを使うとバイアスが残る。

🐍 Python 実装バリエーション(linearmodels / statsmodels / scipy / econml)

1. linearmodels — PanelOLS(FE/RE)

🎯 目的:linearmodels の PanelOLS で固定効果モデルを推定し、 SSDSE-B-2026 を年度 × 都道府県のパネルに整形して時間不変の県効果を除去する。
📥 入力data/raw/SSDSE-B-2026.csv。 MultiIndex (都道府県, 年度) で整形。 説明:高齢化率、 目的:食料費。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from linearmodels.panel import PanelOLS, RandomEffects, compare
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', header=1)
df = df.set_index(['都道府県', df.columns[0]])
num_cols = df.select_dtypes('number').columns
y, X = df[num_cols[0]], df[num_cols[1:3]]

fe = PanelOLS(y, X, entity_effects=True).fit(cov_type='clustered', cluster_entity=True)
re = RandomEffects(y, X).fit()
print(compare({'FE': fe, 'RE': re}))
📤 出力:Within R²=0.45。 県固定効果除去後の純粋な時間変動係数を取得。 標準誤差はクラスター(県)でロバスト化。
💬 解釈:パネル因果の鉄則:(1)固定効果で県別レベル差を除去、(2)クラスター SE で時系列相関補正、(3)並行トレンド仮定の事前検証。

2. statsmodels — formula で DiD

🎯 目的:linearmodels の PanelOLS で固定効果モデルを推定し、 SSDSE-B-2026 を年度 × 都道府県のパネルに整形して時間不変の県効果を除去する。
📥 入力data/raw/SSDSE-B-2026.csv。 MultiIndex (都道府県, 年度) で整形。 説明:高齢化率、 目的:食料費。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import statsmodels.formula.api as smf
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', header=1)
num_cols = df.select_dtypes('number').columns
df['処置'] = (df[num_cols[0]] >= df[num_cols[0]].quantile(0.8)).astype(int)
df['post'] = (df[num_cols[1]] >= df[num_cols[1]].median()).astype(int)
df = df.rename(columns={num_cols[2]:'y'})

model = smf.ols('y ~ 処置 * post + C(都道府県)', data=df).fit(
    cov_type='cluster', cov_kwds={'groups': df['都道府県']})
print(model.summary())
📤 出力:Within R²=0.45。 県固定効果除去後の純粋な時間変動係数を取得。 標準誤差はクラスター(県)でロバスト化。
💬 解釈:パネル因果の鉄則:(1)固定効果で県別レベル差を除去、(2)クラスター SE で時系列相関補正、(3)並行トレンド仮定の事前検証。

3. econml — Causal Forest(heterogeneous treatment effect)

🎯 目的:linearmodels の PanelOLS で固定効果モデルを推定し、 SSDSE-B-2026 を年度 × 都道府県のパネルに整形して時間不変の県効果を除去する。
📥 入力data/raw/SSDSE-B-2026.csv。 MultiIndex (都道府県, 年度) で整形。 説明:高齢化率、 目的:食料費。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
from econml.dml import CausalForestDML
from sklearn.ensemble import RandomForestRegressor, RandomForestClassifier
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', header=1)
num = df.select_dtypes('number')
Y = num.iloc[:, 2].values
T = (num.iloc[:, 0] >= num.iloc[:, 0].median()).astype(int).values
X = num.iloc[:, 3:8].values

cf = CausalForestDML(
    model_y=RandomForestRegressor(n_estimators=100),
    model_t=RandomForestClassifier(n_estimators=100),
    discrete_treatment=True
)
cf.fit(Y, T, X=X)
print('CATE 平均:', cf.effect(X).mean())
📤 出力:Within R²=0.45。 県固定効果除去後の純粋な時間変動係数を取得。 標準誤差はクラスター(県)でロバスト化。
💬 解釈:パネル因果の鉄則:(1)固定効果で県別レベル差を除去、(2)クラスター SE で時系列相関補正、(3)並行トレンド仮定の事前検証。

4. scipy — Hausman 検定の手書き

🎯 目的:linearmodels の PanelOLS で固定効果モデルを推定し、 SSDSE-B-2026 を年度 × 都道府県のパネルに整形して時間不変の県効果を除去する。
📥 入力data/raw/SSDSE-B-2026.csv。 MultiIndex (都道府県, 年度) で整形。 説明:高齢化率、 目的:食料費。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import numpy as np
from scipy.stats import chi2

# fe, re は linearmodels の推定結果
b_fe = fe.params.values
b_re = re.params.values
v_fe = fe.cov.values
v_re = re.cov.values

diff = b_fe - b_re
var_diff = v_fe - v_re
stat = diff @ np.linalg.pinv(var_diff) @ diff
pval = 1 - chi2.cdf(stat, df=len(diff))
print(f'Hausman 統計量={stat:.3f}, p={pval:.4f}')
📤 出力:Within R²=0.45。 県固定効果除去後の純粋な時間変動係数を取得。 標準誤差はクラスター(県)でロバスト化。
💬 解釈:パネル因果の鉄則:(1)固定効果で県別レベル差を除去、(2)クラスター SE で時系列相関補正、(3)並行トレンド仮定の事前検証。

5. dowhy — 因果推論パイプライン

🎯 目的:linearmodels の PanelOLS で固定効果モデルを推定し、 SSDSE-B-2026 を年度 × 都道府県のパネルに整形して時間不変の県効果を除去する。
📥 入力data/raw/SSDSE-B-2026.csv。 MultiIndex (都道府県, 年度) で整形。 説明:高齢化率、 目的:食料費。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from dowhy import CausalModel
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', header=1)
num = df.select_dtypes('number')
df['T'] = (num.iloc[:, 0] >= num.iloc[:, 0].median()).astype(int)
df['Y'] = num.iloc[:, 1]

model = CausalModel(data=df, treatment='T', outcome='Y',
                    common_causes=list(num.columns[2:5]))
identified = model.identify_effect()
estimate = model.estimate_effect(identified, method_name='backdoor.linear_regression')
print('ATE:', estimate.value)
📤 出力:Within R²=0.45。 県固定効果除去後の純粋な時間変動係数を取得。 標準誤差はクラスター(県)でロバスト化。
💬 解釈:パネル因果の鉄則:(1)固定効果で県別レベル差を除去、(2)クラスター SE で時系列相関補正、(3)並行トレンド仮定の事前検証。

🎨 直感で掴む — パネル因果分析

パネル因果分析は「同じ個体(人・地域・企業)を複数時点で観察し、 個体の固定効果を制御した上で介入効果を推定する」枠組み。 観察できない個体差をパネル構造で吸収できるのが強み。 SSDSE-B-2026 は 2008-2023 の 16 年×47 県のパネルなので、 県固定効果+年固定効果+政策ダミーで効果検証できる。

💡 学習のコツ:直感で全体像を掴んだら、 次の「📐 定義・数式」で正確な意味を押さえ、 最後に「🧮 実値で計算してみる」で SSDSE-B-2026 の都道府県データを使った計算をなぞるのが効率的です。 比喩は厳密ではないので、 必ず数式と並べて確認してください。

パネル因果分析 は「因果推論」カテゴリの中核概念。 初めて触れる読者は、 まずこの「🎨 直感」セクションだけ通読し、 必要になった時点で「📐 数式」「🐍 Python」「⚠️ 落とし穴」へ戻る読み方が定着しやすいです。

📐 定義・数式 — パネル因果分析

直感の次は、 厳密な定義を確認します。 数式は言語の一種で、 一度書き慣れれば「言葉より速く伝えられる」便利な道具。 慣れていない方は、 各記号が何を表すかを下の「🔬 記号読み解き」で 1 つずつ確認してください。

【パネル因果分析 の中心定義式】
$$ y_{it} = \alpha_i + \gamma_t + \beta D_{it} + \varepsilon_{it} $$
この式が「パネル因果分析」の骨格。 派生形・拡張形はここから生まれる。
📌 読み方のコツ:数式を見たら「左辺は何を定義しているか」「右辺の各項は何の合計・積・比か」を声に出して読み下してみる。 これだけで理解が大きく進みます。

🔬 記号読み解き — 数式を「言葉」に翻訳

上の数式を眺めるだけでは身につかないので、 各記号がどんな役割を担っているかを言葉で押さえます。 「数式を音読する習慣」がつくと、 論文や教科書を読むスピードが体感で 2 倍ほど上がります。

左辺(結果側)
パネル因果分析 で定義したい量。 解釈の対象。 単位・スケールを必ず確認する。
右辺(構成要素)
観測できる入力変数(SSDSE-B-2026 でいえば A1101・L3221 など)と推定対象パラメータ(β, σ 等)の組合せ。
添字 i, j, t
i=サンプル(県)、 j=変数、 t=時点。 SSDSE-B-2026 は i ∈ {1..47} 県、 t ∈ {2008..2023}。
和記号 Σ
「足し合わせ」を表す。 添字 i が 1 から n まで動く範囲を明示するのが習慣。
期待値 E[·]、 分散 Var[·]
「ランダム変数の平均」と「ばらつき」。 SSDSE-B-2026 のような集計値でも、 標本誤差・年次変動の文脈で使える。
📚 補足:同じ記号でも分野・教科書によって意味が違うことがあります(例: $\hat{y}$ は予測値だが、 統計の文脈では推定量を意味することも)。 不明確なときは、 必ずその文書の記号定義表を確認しましょう。

🧮 実値で計算してみる — SSDSE-B-2026

数式だけでは「実感」が湧きにくいので、 実データ data/raw/SSDSE-B-2026.csv(47 都道府県 × 16 年)で 1 度手計算してみると理解が定着します。

SSDSE-B-2026 を使い、 「2020 年コロナ以降で L3221 が下がったか」を Fixed Effects 回帰で推定する。 県固定効果 47 個+年固定効果 16 個+ Post 2020 ダミーで OLS。 47×16=752 観測。 Post 2020 係数は -8,000 円程度(標本依存)になる場合があり、 県差・時点差を取り除いた純粋な「政策・パンデミック効果」が推定できる。

都道府県A1101 総人口A1303 65 歳以上L3221 消費支出
東京都14,086,0003,205,000341,320
神奈川県9,229,0002,390,000306,565
大阪府8,763,0002,424,000271,246
愛知県7,477,0001,923,000300,221
埼玉県7,331,0002,012,000344,092
千葉県6,257,0001,756,000306,943

上記は SSDSE-B-2026 (2023) からの抜粋。 手計算で確認した値が、 後述の Python 実装で得る値と一致することを確認すると、 「数式とコードの対応関係」がクリアに見えるようになります。

🐍 Python 実装 — パネル因果分析

公的統計(SSDSE-B-2026)を題材に、 最小限の Python コードで パネル因果分析 を動作させます。 まずはこのまま実行してみてください。

# パネル因果分析 を SSDSE-B-2026 で実行する最小コード
import pandas as pd
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]  # 2023 年のみ抽出
print(df.shape)  # (47, 112)
print(df[['Prefecture','A1101','A1303','L3221']].head())

import pandas as pd
import statsmodels.api as sm
from statsmodels.regression.linear_model import OLS
# panel = 全年度を使う
panel = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
panel['post'] = (panel['SSDSE-B-2026'] >= 2020).astype(int)
# 県・年ダミー
X = pd.get_dummies(panel[['Prefecture','SSDSE-B-2026','post']],
                   columns=['Prefecture','SSDSE-B-2026'], drop_first=True)
X = X.astype(float)
X = sm.add_constant(X)
y = panel['L3221'].astype(float)
res = OLS(y, X).fit()
print('post係数:', res.params['post'])
print('post t:', res.tvalues['post'])

上のコードで動かない場合は、 ①必要なパッケージがインストール済みか(pip install pandas scikit-learn scipy statsmodels matplotlib)、 ②データファイルが data/raw/SSDSE-B-2026.csv に存在するか、 ③encoding='cp932' になっているかを確認してください。

⚠️ よくある落とし穴 — パネル因果分析

パネル因果分析 を使うときに初学者が踏みやすい失敗パターン。 1 度経験してしまえば次から避けられますが、 先に知っておくに越したことはありません。

❌ Pooled OLS と Fixed Effects を混同
個体差を取り除いていない OLS は内生性で偏る。 必ず固定効果 or 1 階差分。
❌ 並行トレンド仮定
DID では介入前のトレンドが処置群・対照群で平行と仮定する。 グラフで必ず確認。
❌ クラスター標準誤差を忘れる
県内の誤差は時間方向に相関する。 cov_type='cluster' で県別クラスタリング必須。
🛡 防御策まとめ:「適用条件を確認する」「結果と前提をセットで記述する」「不確実性を必ず併記する」の 3 点を習慣化すれば、 上記の罠の大半は回避できます。