🔖 キーワード索引(補強)
パネル因果推論に関連する手法・概念のチップ集。
固定効果モデル
変量効果モデル
Hausman 検定
DiD
並行トレンド
Two-way FE
Synthetic Control
Event Study
RDD
IV (操作変数)
クラスタリング標準誤差
Heterogeneous TWFE
Callaway-Sant'Anna
propensity score
逆数重み
処置効果(ATE)
ATT
CATE
causal forest
交絡変数
🔖 キーワード索引(チップから該当箇所へジャンプ)
論文記事から各用語のリンクをクリックすると、 該当箇所が開きます:
💡 30秒で分かる結論
🍰 まずはやさしく
原因と結果の関係を調べる道具です。
ある行動が本当に効果あったか知るために使います。
勉強法を変えて点数が上がったか調べるようなものです。
ここでは効果を正しく測る方法を学びます。
因果効果=処置あり時 - 処置なし時の差 。 両方を観測できないのが本質的問題。
RCT :ランダム割り当てで交絡を断ち切る黄金律。
DID :処置群・対照群の差分の差分 で時間効果を除く。
IV :処置に影響するが結果には直接影響しない外生変数で内生性を回避。
RDD :閾値前後の比較で因果を推定。
傾向スコア :処置確率で重み付け・マッチング。
固定効果 :個体内変動だけで効果を推定(時間不変の交絡を除去)。
DAG :因果関係を有向グラフで可視化し、 必要な調整変数を特定。
🤔 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$ の原因」を表す。 「バックドア基準 」を満たす調整変数を特定すれば、 観察データから因果効果が同定できる。
共通原因 (交絡因子)→ 必ず調整
媒介因子 (処置の下流) → 調整すると効果が消えてしまう
合流点(collider) → 調整するとバイアスが新たに発生
🎭 3. 潜在結果モデル(Rubin Causal Model)
個体 $i$ について:
$Y_i(1)$:処置を受けた場合の結果(潜在結果)
$Y_i(0)$:処置を受けなかった場合の結果(反事実)
個体因果効果:$\tau_i = Y_i(1) - Y_i(0)$
根本問題: 同じ個体について両方は観測できない。 観測されるのは $Y_i = T_i Y_i(1) + (1-T_i) Y_i(0)$ のみ。
3.1 ATE と ATT
ATE(平均処置効果) :$\mathbb{E}[Y(1) - Y(0)]$(全集団)
ATT(処置群への平均処置効果) :$\mathbb{E}[Y(1) - Y(0) | T=1]$
ATU(対照群への) :$\mathbb{E}[Y(1) - Y(0) | T=0]$
3.2 反事実
「もし処置を受けていなかったら、 この個体の結果はどうだったか?」を推測することが因果推論の核心。
🎲 4. ランダム化比較試験 (RCT)
処置をランダム に割り当てれば、 処置群と対照群が平均的に類似 → $T \perp \{Y(0), Y(1)\}$ が成り立つ。 ATE は単純な差で不偏推定可能:
$$\widehat{\mathrm{ATE}} = \bar{Y}_{\text{処置}} - \bar{Y}_{\text{対照}}$$
RCT の限界
実施コスト・倫理的制約(喫煙の有害性を RCT で検証できない)
外的妥当性(実験集団と一般集団の乖離)
遵守率の問題(処置に従わない人の扱い)
長期効果の測定困難
📋 コピー import numpy as np
from scipy import stats
# 想定 RCT 結果:処置群 n=100、 対照群 n=100
# 実験データは手元に無いので、真の効果 0.5 を仕込んだ<架空の>データで手順を示す
_rng = np . random . default_rng ( 0 )
treat = _rng . normal ( loc = 10.5 , scale = 2.0 , size = 100 )
ctrl = _rng . normal ( loc = 10.0 , scale = 2.0 , size = 100 )
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 } ]' )
📊 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 推定量。
📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行)
都道府県 SSDSE-B-2026(年度) L3221(消費支出(二人以上の世帯)) A1303(65歳以上人口) A1101(総人口)
北海道 2,023 296,888 1,681,000 5,092,000
東京都 2,023 341,320 3,205,000 14,086,000
沖縄県 2,023 251,222 350,000 1,468,000
…(全 47 行)
📋 コピー 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 # ── この抜粋で使うパネルデータを用意します ──
# 「高齢化率が中央値超の県を処置群、2016 年以降を Post」とみなした例。
import pandas as pd
import statsmodels.formula.api as smf
panel = pd . read_csv ( 'data/raw/SSDSE-B-2026.csv' , encoding = 'cp932' , skiprows = 1 )
panel = panel . rename ( columns = { '年度' : 'Year' })
panel [ 'Y' ] = panel [ '消費支出(二人以上の世帯)' ] . astype ( float )
_aging = panel [ '65歳以上人口' ] / panel [ '総人口' ] * 100
panel [ 'Treat' ] = ( _aging >= _aging . median ()) . astype ( int )
panel [ 'Post' ] = ( panel [ 'Year' ] >= 2016 ) . astype ( int )
# パネルデータ:複数都道府県の時点 × 処置有無
model = smf . ols ( 'Y ~ Treat + Post + Treat:Post + C(都道府県)' , data = panel ) . fit ()
print ( model . summary ())
# Treat:Post の係数が DID 推定量
5.2 共通トレンド仮定の検証
処置前期間で処置群・対照群のトレンドが平行か → イベントスタディ・プラセボ検定。
🔧 6. 操作変数法 (Instrumental Variable)
処置 $T$ と結果 $Y$ の両方に影響する未観測交絡があるとき、 次の条件を満たす変数 $Z$(操作変数)を使って因果を識別:
関連性 :$Z$ が $T$ に影響
外生性 :$Z$ は $Y$ に直接影響しない($T$ 経由のみ)
6.1 2段階最小二乗法 (2SLS)
第1段階:$T$ を $Z$ で回帰 → 予測値 $\hat{T}$
第2段階:$Y$ を $\hat{T}$ で回帰
📋 コピー 1
2
3
4
5
6
7
8
9
10
11
12
13
14 # 骨格ではなく実データで 2SLS を動かす。
# Y=消費支出、内生変数 T=高齢化率、操作変数 Z=年少人口比率、統制 X1/X2
# (Z はあくまで手順を示すための例。除外制約が満たされる保証は無い)
from linearmodels.iv import IV2SLS
df = panel . copy () # 上のブロックで作った panel を使う
df [ 'T' ] = df [ '65歳以上人口' ] / df [ '総人口' ] * 100 # 内生変数:高齢化率(%)
df [ 'Z' ] = df [ '15歳未満人口' ] / df [ '総人口' ] * 100 # 操作変数:年少人口比率(%)
df [ 'X1' ] = df [ '総人口' ] / 1e4 # 統制:総人口(万人)
df [ 'X2' ] = df [ '婚姻件数' ] / df [ '総人口' ] * 1000 # 統制:婚姻率(‰)
mod = IV2SLS . from_formula ( 'Y ~ 1 + [T ~ Z] + X1 + X2' , data = df ) . fit ()
print ( mod . params . round ( 2 ))
print ( '第 1 段階 F 値:' , round ( float ( mod . first_stage . diagnostics [ 'f.stat' ] . iloc [ 0 ]), 2 ))
6.2 古典例
Angrist (1990):兵役(処置)の賃金(結果)への影響、 徴兵くじ番号(IV)
Card (1993):教育(処置)の賃金(結果)、 大学までの距離(IV)
📏 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]$$
例
合格点ぎりぎりの大学進学者 vs 不合格者
所得制限ぎりぎりで給付金を受けた世帯 vs 受けなかった世帯
選挙でぎりぎり勝った候補 vs 負けた候補
📋 コピー 1
2
3
4
5
6
7
8
9
10
11
12
13
14 import pandas as pd
# ── RDD の題材を用意する。高齢化率 30% を境目 (cutoff) とみなした例 ──
_d = pd . read_csv ( 'data/raw/SSDSE-B-2026.csv' , encoding = 'cp932' , skiprows = [ 1 ])
df = pd . DataFrame ({
'X' : _d [ 'A1303' ] / _d [ 'A1101' ] * 100 , # running variable: 高齢化率(%)
'Y' : _d [ 'L3221' ] . astype ( float ), # outcome: 消費支出
})
cutoff = 30.0
import numpy as np
from rdrobust import rdrobust
result = rdrobust ( y = df [ 'Y' ], x = df [ 'X' ], c = cutoff )
print ( result )
⚖️ 8. 傾向スコア (Propensity Score)
処置確率 $e(x) = P(T=1|X=x)$ を共変量から推定(ロジスティック等)。 「条件付き独立性 (CIA)」が成り立てば、 傾向スコアでバランスを取れば因果効果が同定可。
8.1 マッチング
処置群の各個体に「傾向スコアが近い対照群個体」をマッチ。
📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行)
都道府県 A1101(総人口) A1303(65歳以上人口) B4101(年平均気温)
北海道 5,092,000 1,681,000 11.0
東京都 14,086,000 3,205,000 17.6
沖縄県 1,468,000 350,000 23.8
…(全 47 行)
📋 コピー 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 # ── この抜粋で使うデータを用意します ──
import pandas as pd
from sklearn.linear_model import LogisticRegression
df = pd . read_csv ( 'data/raw/SSDSE-B-2026.csv' , encoding = 'cp932' , skiprows = [ 1 ])
df = df [ df [ 'SSDSE-B-2026' ] == 2023 ] # 2023 年の 47 都道府県
df = df . reset_index ( drop = True )
df = df . dropna ( subset = [ 'A1101' , 'B4101' , 'A1303' ]) # NaN があると最近傍探索が落ちる
X_covariates = df [[ 'A1101' , 'B4101' ]] . astype ( float ) . values
_aging = df [ 'A1303' ] / df [ 'A1101' ] * 100
T = ( _aging >= _aging . median ()) . astype ( int ) . values # 処置の有無
df [ 'T' ] = T
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
# df.T は「転置」の意味になってしまう。列を指すときは df['T'] と書く。
nn = NearestNeighbors ( n_neighbors = 1 ) . fit ( df [ df [ 'T' ] == 0 ][[ 'ps' ]])
dist , idx = nn . kneighbors ( df [ df [ 'T' ] == 1 ][[ 'ps' ]])
# マッチ後の差で ATT 推定
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 (都道府県, 年度) で整形。 説明:高齢化率(A1303 / A1101 × 100)、 目的:消費支出(L3221)。
📋 コピー from linearmodels.panel import PanelOLS
# 上のブロックで作った panel に説明変数を足し、都道府県 × 年の 2 重添字にする
panel [ 'X1' ] = panel [ '総人口' ] / 1e4 # 総人口(万人)
panel [ 'X2' ] = panel [ '65歳以上人口' ] / panel [ '総人口' ] * 100 # 高齢化率(%)
df_p = panel . set_index ([ '都道府県' , 'Year' ]) # 年の列名は 'Year'('年' ではない)
fe = PanelOLS . from_formula ( 'Y ~ X1 + X2 + EntityEffects' , data = df_p ) . fit ()
print ( fe . params . round ( 4 ))
print ( 'within R2 =' , round ( fe . rsquared_within , 6 ))
📤 出力 :Within R²≈0.00(実測:高齢化率→消費支出)。 県固定効果を除去すると高齢化率の係数はほぼゼロになる。 標準誤差はクラスター(県)でロバスト化。
💬 解釈 :パネル因果の鉄則:(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 (都道府県, 年度) で整形。 説明:高齢化率(A1303 / A1101 × 100)、 目的:消費支出(L3221)。
📋 コピー 1
2
3
4
5
6
7
8
9
10
11
12
13 from linearmodels.panel import PanelOLS , RandomEffects
panel [ 'X' ] = panel [ '65歳以上人口' ] / panel [ '総人口' ] * 100 # 高齢化率(%)
df_p = panel . set_index ([ '都道府県' , 'Year' ])
fe = PanelOLS . from_formula ( 'Y ~ X + EntityEffects' , data = df_p ) . fit ()
re_ = RandomEffects . from_formula ( 'Y ~ 1 + X' , data = df_p ) . fit () # re は標準モジュール名なので re_ にする
# Hausman 統計量を手で計算:(b_FE - b_RE)^2 / (Var_FE - Var_RE)
d = float ( fe . params [ 'X' ] - re_ . params [ 'X' ])
v = float ( fe . cov . loc [ 'X' , 'X' ] - re_ . cov . loc [ 'X' , 'X' ])
print ( 'FE beta =' , round ( float ( fe . params [ 'X' ]), 4 ))
print ( 'RE beta =' , round ( float ( re_ . params [ 'X' ]), 4 ))
print ( 'Hausman =' , round ( d ** 2 / v , 4 ))
📤 出力 :Within R²≈0.00(実測:高齢化率→消費支出)。 県固定効果を除去すると高齢化率の係数はほぼゼロになる。 標準誤差はクラスター(県)でロバスト化。
💬 解釈 :パネル因果の鉄則:(1)固定効果で県別レベル差を除去、(2)クラスター SE で時系列相関補正、(3)並行トレンド仮定の事前検証。
9.4 双方向固定効果(TWFE)
個体効果 $\alpha_i$ + 時間効果 $\delta_t$。 DID と同等。
🔍 10. 感度分析
未観測交絡があった場合に結論がどれだけ変わるか評価。
Rosenbaum bound :未観測交絡が処置確率を何倍歪めても結論が保たれるか
E-value :観測効果を打ち消す未観測交絡の強さ
プラセボ検定 :効果がないはずの群でも効果が出ないか確認
📊 11. 因果推論手法の使い分け
手法
必要な条件
推定する効果
RCT ランダム割当 ATE
DID 共通トレンド + 処置タイミング ATT
IV 関連性 + 外生性 LATE
RDD 明確な閾値 境界での効果
傾向スコア 条件付き独立性 (CIA) ATE/ATT
固定効果 パネルデータ + 時間不変交絡 時間内変動効果
合成統制法 十分な対照ユニット 単一処置ユニットの効果
📍 あなたが今見ているもの
🍰 まずはやさしく
データの分析手法をまとめた地図のようなページです。
データから本当の原因を見つけるために使います。
スマホの利用時間と成績の関係を正しく分析する例です。
因果関係を取り出すための仕組みを体系的に読みます。
本ページでは、 パネルデータ分析 と因果推論 を統合的に解説します。 固定効果・変量効果 ・RCT ・DID ・IV ・RDD ・傾向スコア ・交絡 を一気通貫で扱います。
「相関は因果ではない」は有名な格言。 では観察データから因果を取り出す にはどうすればよいか? 計量経済学・統計疫学・社会科学で発展した方法論を体系的に学びます。
🎨 直感で掴む — パネル因果分析
🍰 まずはやさしく
同じ人をずっと追いかける観察方法です。
人それぞれのクセを無視して効果を測るために使います。
部活で同じ人が練習法を変えた時の変化を見る例です。
まずはイメージで全体の流れを掴んでください。
パネル因果分析は「同じ個体(人・地域・企業)を複数時点で観察し、 個体の固定効果を制御した上で介入効果を推定する 」枠組み。 観察できない個体差をパネル構造で吸収できるのが強み。 SSDSE-B-2026 は 2012-2023 の 12 年×47 県のパネルなので、 県固定効果+年固定効果+政策ダミーで効果検証できる。
💡 学習のコツ :直感で全体像を掴んだら、 次の「📐 定義・数式」で正確な意味を押さえ、 最後に「🧮 実値で計算してみる」で SSDSE-B-2026 の都道府県データを使った計算をなぞるのが効率的です。 比喩は厳密ではないので、 必ず数式と並べて確認してください。
パネル因果分析 は「因果推論」カテゴリの中核概念。 初めて触れる読者は、 まずこの「🎨 直感」セクションだけ通読し、 必要になった時点で「📐 数式」「🐍 Python」「⚠️ 落とし穴」へ戻る読み方が定着しやすいです。
🔬 数式を言葉で読み解く — 数式を「言葉」に翻訳
上の数式を眺めるだけでは身につかないので、 各記号がどんな役割 を担っているかを言葉で押さえます。 「数式を音読する習慣」がつくと、 論文や教科書を読むスピードが体感で 2 倍ほど上がります。
左辺(結果側)
パネル因果分析 で定義したい量 。 解釈の対象。 単位・スケールを必ず確認する。
右辺(構成要素)
観測できる入力変数(SSDSE-B-2026 でいえば A1101・L3221 など)と推定対象パラメータ(β, σ 等)の組合せ。
添字 i, j, t
i=サンプル(県)、 j=変数、 t=時点。 SSDSE-B-2026 は i ∈ {1..47} 県、 t ∈ {2012..2023}。
和記号 Σ
「足し合わせ」を表す。 添字 i が 1 から n まで動く範囲を明示するのが習慣。
期待値 E[·]、 分散 Var[·]
「ランダム変数の平均」と「ばらつき」。 SSDSE-B-2026 のような集計値でも、 標本誤差・年次変動の文脈で使える。
📚 補足 :同じ記号でも分野・教科書によって意味が違うことがあります(例: $\hat{y}$ は予測値だが、 統計の文脈では推定量を意味することも)。 不明確なときは、 必ずその文書の記号定義表 を確認しましょう。
🧮 SSDSE-B-2026 で実値計算 — パネル因果推論の実例
SSDSE-B-2026 は 47都道府県 × 複数年のパネル構造を持つので、 固定効果モデル・DiD の練習に最適。
例:固定効果モデル(two-way FE)
🎯 目的 :linearmodels の PanelOLS で固定効果モデルを推定し、 SSDSE-B-2026 を年度 × 都道府県のパネルに整形して時間不変の県効果を除去する。
📥 入力 :data/raw/SSDSE-B-2026.csv。 MultiIndex (都道府県, 年度) で整形。 説明:高齢化率(A1303 / A1101 × 100)、 目的:消費支出(L3221)。
📋 コピー 1
2
3
4
5
6
7
8
9
10
11
12
13
14 import pandas as pd
from linearmodels import PanelOLS
import statsmodels.api as sm
df = pd . read_csv ( 'data/raw/SSDSE-B-2026.csv' , encoding = 'cp932' , skiprows = [ 1 ])
df = df . rename ( columns = { 'SSDSE-B-2026' : '年度' , 'L3221' : '消費支出' })
df [ '高齢化率' ] = df [ 'A1303' ] / df [ 'A1101' ] * 100 # 65歳以上人口 / 総人口
df = df . set_index ([ 'Prefecture' , '年度' ]) # パネル構造:県 × 年
y = df [ '消費支出' ]
X = sm . add_constant ( df [[ '高齢化率' ]])
model = PanelOLS ( y , X , entity_effects = True , time_effects = True ) . fit (
cov_type = 'clustered' , cluster_entity = True )
print ( model )
🧮 実値で計算してみる — SSDSE-B-2026
数式だけでは「実感」が湧きにくいので、 実データ data/raw/SSDSE-B-2026.csv(47 都道府県 × 12 年)で 1 度手計算してみると理解が定着します。
SSDSE-B-2026 を使い、 「2020 年コロナ以降で L3221(消費支出)が下がったか」を固定効果回帰で推定する。 県固定効果 47 個+ Post 2020 ダミーで OLS(Post は年固定効果の一次結合なので、 年ダミーと併用すると完全共線になる点に注意)。 47×12=564 観測。 実測では Post 2020 係数は約 -1,187 円(県クラスタロバスト t = -0.68、 非有意)で、 県差を取り除いた「コロナ前後の平均差」に統計的に明確な低下は確認されない。
都道府県 A1101 総人口 A1303 65 歳以上 L3221 消費支出
東京都 14,086,000 3,205,000 341,320
神奈川県 9,229,000 2,390,000 306,565
大阪府 8,763,000 2,424,000 271,246
愛知県 7,477,000 1,923,000 300,221
埼玉県 7,331,000 2,012,000 344,092
千葉県 6,257,000 1,756,000 306,943
上記は SSDSE-B-2026 (2023) からの抜粋。 手計算で確認した値が、 後述の Python 実装で得る値と一致することを確認すると、 「数式とコードの対応関係」がクリアに見えるようになります。
🧮 数式に値を入れて手で計算する: パネルデータの因果推定
合成データで個体固定効果 + DID で因果効果を推定する。
Step 1: 2 個体 × 2 期
個体 期 0 (前) 期 1 (後)
処置 i=1 5 14 対照 i=2 3 7
Step 2: DID
処置差 = 14 - 5 = 9
対照差 = 7 - 3 = 4
DID = 9 - 4 = +5 (因果効果)
🐍 Python で再現
📋 コピー treat = [ 5 , 14 ]
ctrl = [ 3 , 7 ]
did = ( treat [ 1 ] - treat [ 0 ]) - ( ctrl [ 1 ] - ctrl [ 0 ])
print ( f "DID: { did } " )
📤 実行結果
DID: 5
💬 手計算 (Step 2) +5 と Python 出力が完全一致。
🐍 15. ライブラリ早見表
用途
関数・パッケージ
パネル回帰 linearmodels.panel.PanelOLS, RandomEffects
2SLS linearmodels.iv.IV2SLS
DID statsmodels.formula.api.ols, differences (PyPI)
RDD rdrobust (PyPI)
傾向スコア causalinference, doWhy, EconML
マッチング causalml, dowhy
合成統制法 SyntheticControlMethods (PyPI)
DAG networkx, daggity (R), pgmpy
CATE推定 EconML, causalml
DoWhy(統合) dowhy (Microsoft)
📜 16. 因果推論の歴史
1923 :Neyman の潜在結果モデル原型
1935 :Fisher の RCT・実験計画法
1974 :Rubin の Causal Model(Rubin Causal Model)
1983 :Rosenbaum & Rubin の傾向スコア
1985 :Heckman の選択バイアス補正
1990 :Angrist の LATE と IV の現代解釈
1995 :Pearl の因果ダイアグラム・do演算子
2003 :Imbens & Lemieux の RDD 体系化
2008 :Abadie らの合成統制法
2021 :Card, Angrist, Imbens に「因果推論への貢献」でノーベル経済学賞
💼 17. 実務応用
政策評価 :最低賃金引上の雇用効果(Card & Krueger DID)
教育 :少人数学級の学力効果
医療 :薬剤効果(RCT が黄金律)、 観察研究なら傾向スコア
マーケティング :プロモーション効果の因果推定、 A/Bテスト
労働経済 :教育・職業訓練の賃金効果
金融 :金融政策の効果(自然実験)
テック企業 :レコメンド・UI 変更の因果効果
✅ 18. 因果推論チェックリスト
□ DAG を描いて交絡因子を特定したか?
□ 識別戦略(RCT/DID/IV/RDD/PS/FE)を明示したか?
□ 必要な前提(共通トレンド・外生性・CIA 等)を検証したか?
□ プラセボ検定・感度分析を行ったか?
□ 標準誤差をクラスタリングしたか(パネル)?
□ ATE/ATT/LATE などを区別したか?
□ 結果を「条件付き因果関係」として控え目に解釈したか?
□ 外的妥当性(他の集団・期間への適用可能性)を議論したか?
🚀 19. 発展トピック
19.1 合成統制法(Synthetic Control Method)
2003 Abadie ら。 単一処置ユニット(例:1 つの州・国)に対し、 他のユニットの加重平均で「人工的な対照」を作る。
例:カリフォルニア州のタバコ税引上の効果評価。 他の州の加重平均で「タバコ税のない仮想カリフォルニア」を作り比較。
19.2 異質処置効果(CATE)
個体・部分集団ごとの処置効果。 機械学習で推定(Causal Forest、 X-Learner、 R-Learner、 DR-Learner)。
📋 コピー 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 import pandas as pd
import numpy as np
# ── CausalForestDML に渡す変数を用意する ──
_d = pd . read_csv ( 'data/raw/SSDSE-B-2026.csv' , encoding = 'cp932' , skiprows = [ 1 ])
_aging = _d [ 'A1303' ] / _d [ 'A1101' ] * 100
y = _d [ 'L3221' ] . astype ( float ) . values # 結果: 消費支出
t = ( _d [ 'SSDSE-B-2026' ] >= 2016 ) . astype ( int ) . values # 処置: 2016 年以降
X_features = np . column_stack ([ _aging , np . log ( _d [ 'A1101' ] . astype ( float ))])
W_controls = _d [[ 'A4101' , 'A9101' ]] . astype ( float ) . values # 統制変数
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 ) # 各個体の処置効果
19.3 二重機械学習(DML)
Chernozhukov ら 2018。 結果と処置を高次元共変量から ML で予測し、 残差で因果効果を推定。 高次元共変量にも対応。
19.4 Doubly Robust ML
傾向スコアと結果モデルの両方を ML で推定し、 どちらか一方が正しければ一致。
🤖 20. 因果推論と機械学習の融合
EconML (Microsoft):CATE 推定の標準ライブラリ
DoWhy (Microsoft):因果推論の 4 ステップ(モデル→識別→推定→反証)を統合
CausalML (Uber):マーケティング・アップリフトモデリング用
causalnex (QuantumBlack):因果ベイジアンネットワーク
アップリフトモデリング
「処置すれば反応する人」を予測(CATE が大きい人)。 マーケティング配信の効率化。
❓ 21. よくある質問
Q. 単純な OLS で交絡変数を入れれば因果が出る?
A. すべての交絡が観測されていれば原理的には可能(CIA)。 ただし「すべてを観測」は実務でほぼ不可能。 識別戦略を明示し、 未観測交絡への感度分析が必須。
Q. DID とパネル固定効果はどう違う?
A. 数学的にはほぼ同じ。 DID は「2 群 × 2 時点」の単純設定、 双方向固定効果は同じロジックを多群・多時点に一般化したもの。
Q. RCT があれば因果推論手法は要らない?
A. RCT は黄金律ですが、 倫理・コスト・遵守率・外的妥当性の問題があり、 観察データの分析が必要な場面は多い。 また RCT 内でも CATE 推定は ML 因果推論が必要。
Q. 単年度の横断データしかない場合でも因果推論できる?
A. 横断データだけでは因果推論は本来困難。 ドメイン知識で交絡を網羅し、 傾向スコアで「条件付き因果関係」と限定的に解釈するのが現実的。
1. linearmodels — PanelOLS(FE/RE)
🎯 目的 :linearmodels の PanelOLS で固定効果モデルを推定し、 SSDSE-B-2026 を年度 × 都道府県のパネルに整形して時間不変の県効果を除去する。
📥 入力 :data/raw/SSDSE-B-2026.csv。 MultiIndex (都道府県, 年度) で整形。 説明:高齢化率(A1303 / A1101 × 100)、 目的:消費支出(L3221)。
📋 コピー from linearmodels.panel import PanelOLS , RandomEffects , compare
import pandas as pd
import statsmodels.api as sm
df = pd . read_csv ( 'data/raw/SSDSE-B-2026.csv' , encoding = 'cp932' , skiprows = [ 1 ])
df = df . rename ( columns = { 'SSDSE-B-2026' : '年度' , 'L3221' : '消費支出' })
df [ '高齢化率' ] = df [ 'A1303' ] / df [ 'A1101' ] * 100
df = df . set_index ([ 'Prefecture' , '年度' ])
y , X = df [ '消費支出' ], sm . add_constant ( df [[ '高齢化率' ]])
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 }))
🐍 Python 実装 — パネル因果分析
公的統計(SSDSE-B-2026)を題材に、 最小限の Python コードで パネル因果分析 を動作させます。 まずはこのまま実行してみてください。
📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行)
都道府県 A1101(総人口) A1303(65歳以上人口) L3221(消費支出(二人以上の世帯))
北海道 5,092,000 1,681,000 296,888
東京都 14,086,000 3,205,000 341,320
沖縄県 1,468,000 350,000 251,222
…(全 47 行)
📋 コピー 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 # パネル因果分析 を 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 statsmodels.api as sm
# panel = 全年度 (47 県 × 12 年 = 564 行) を使う
panel = pd . read_csv ( 'data/raw/SSDSE-B-2026.csv' , encoding = 'cp932' , skiprows = [ 1 ])
panel [ 'post' ] = ( panel [ 'SSDSE-B-2026' ] >= 2020 ) . astype ( int )
# 県ダミー + post(post は年ダミーの一次結合なので年ダミーとは併用しない)
X = pd . get_dummies ( panel [[ 'Prefecture' , 'post' ]],
columns = [ 'Prefecture' ], drop_first = True )
X = X . astype ( float )
X = sm . add_constant ( X )
y = panel [ 'L3221' ] . astype ( float )
res = sm . OLS ( y , X ) . fit ( cov_type = 'cluster' , cov_kwds = { 'groups' : panel [ 'Prefecture' ]})
print ( 'post係数:' , res . params [ 'post' ]) # 実測: 約 -1187 円
print ( 'post t:' , res . tvalues [ 'post' ]) # 実測: -0.68 (非有意)
▶ 実行 を押せばこのページの中でそのまま動きます(ライブラリもデータも同梱済みで、 準備は要りません)。 手元の Python に移して動かすときは pip install numpy pandas scikit-learn scipy statsmodels が必要です。 読んでいるデータは data/raw/SSDSE-B-2026.csv。 日本語を含むので encoding='cp932' の指定を落とさないでください。
⚠️ 12. よくある落とし穴
落とし穴
対処
「相関=因果」と書く 識別戦略を明示しない限り「関連が見られた」に留める。
媒介変数を調整 処置の下流変数を入れると効果が消える。 DAG で確認。
合流点を調整 処置と結果の両方に影響される変数を調整するとバイアスが新たに生じる。
DID で共通トレンド未確認 処置前期間のプレトレンドをプロット。
弱いIV 第1段階 F > 10 を目安。 弱IVは強いバイアス。
傾向スコアの overlap 不足 分布を可視化、 共通サポート領域に絞る。
標準誤差をクラスタリングしない パネルでは個体クラスタロバスト SE を使う。
🏋️ 13. 練習問題
SSDSE-B-2026 は 47 都道府県 × 12 年(2012-2023)= 564 行のバランス・パネルなので、 固定効果・DID の練習にそのまま使えます。
Q1. 都道府県パネルで固定効果回帰を行い、 OLS との係数の違いを観察しなさい。
📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行)
都道府県 SSDSE-B-2026(年度) L3221(消費支出(二人以上の世帯)) A1303(65歳以上人口) A1101(総人口) Prefecture(都道府県)
北海道 2,023 296,888 1,681,000 5,092,000 北海道
東京都 2,023 341,320 3,205,000 14,086,000 東京都
沖縄県 2,023 251,222 350,000 1,468,000 沖縄県
…(全 47 行)
📋 コピー 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 import pandas as pd
from linearmodels.panel import PanelOLS
import statsmodels.formula.api as smf
# 直前のブロックで panel を英字コード列で読み直しているので、日本語列名で読み直す
panel = pd . read_csv ( 'data/raw/SSDSE-B-2026.csv' , encoding = 'cp932' , skiprows = 1 )
panel = panel . rename ( columns = { '年度' : 'Year' })
panel [ 'Y' ] = panel [ '消費支出(二人以上の世帯)' ] . astype ( float )
panel [ 'X' ] = panel [ '65歳以上人口' ] / panel [ '総人口' ] * 100
df_p = panel . set_index ([ '都道府県' , 'Year' ])
ols = smf . ols ( 'Y ~ X' , data = panel ) . fit ()
fe = PanelOLS . from_formula ( 'Y ~ X + EntityEffects' , data = df_p ) . fit ()
print ( 'OLS beta:' , round ( float ( ols . params [ 'X' ]), 4 ))
print ( 'FE beta:' , round ( float ( fe . params [ 'X' ]), 4 ))
Q2. ある政策が一部の都道府県のみで導入されたケースを想定し、 DID で効果を推定しなさい。
📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行)
都道府県 SSDSE-B-2026(年度) L3221(消費支出(二人以上の世帯)) A1303(65歳以上人口) A1101(総人口) Prefecture(都道府県)
北海道 2,023 296,888 1,681,000 5,092,000 北海道
東京都 2,023 341,320 3,205,000 14,086,000 東京都
沖縄県 2,023 251,222 350,000 1,468,000 沖縄県
…(全 47 行)
📋 コピー 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 # ── この抜粋で使うパネルデータを用意します ──
import pandas as pd
panel = pd . read_csv ( 'data/raw/SSDSE-B-2026.csv' , encoding = 'cp932' , skiprows = 1 )
panel = panel . rename ( columns = { '年度' : '年' })
panel [ 'Y' ] = panel [ '消費支出(二人以上の世帯)' ] . astype ( float )
_aging = panel [ '65歳以上人口' ] / panel [ '総人口' ] * 100
panel [ 'Treat' ] = ( _aging >= _aging . median ()) . astype ( int )
panel [ 'Post' ] = ( panel [ '年' ] >= 2016 ) . astype ( int )
# 47 県 × 12 年すべてを使うと固定効果の数が多すぎるので、6 県 × 12 年に絞る
_use = [ '北海道' , '東京都' , '愛知県' , '大阪府' , '高知県' , '沖縄県' ]
panel = panel [ panel [ '都道府県' ] . isin ( _use )]
import statsmodels.formula.api as smf
m = smf . ols ( 'Y ~ Treat*Post + C(都道府県) + C(年)' , data = panel ,
cluster_kwds = { 'groups' : panel [ '都道府県' ]}) . fit ()
print ( m . summary ())
Q3. SSDSE-B 単年データで「都市規模高低」を処置とみなし、 傾向スコアで ATT を推定しなさい。
📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行)
都道府県 A1101(総人口) A1303(65歳以上人口) A4103(合計特殊出生率) L3221(消費支出(二人以上の世帯))
北海道 5,092,000 1,681,000 1.06 296,888
東京都 14,086,000 3,205,000 0.99 341,320
沖縄県 1,468,000 350,000 1.6 251,222
…(全 47 行)
📋 コピー import pandas as pd
from sklearn.linear_model import LogisticRegression
df = pd . read_csv ( 'data/raw/SSDSE-B-2026.csv' , encoding = 'cp932' , skiprows = [ 1 ])
df = df [ df [ 'SSDSE-B-2026' ] == 2023 ] . copy () # 単年 (2023) の横断で練習
df [ 'T' ] = ( df [ 'A1101' ] >= df [ 'A1101' ] . median ()) . astype ( int ) # 都市規模高低
df [ '高齢化率' ] = df [ 'A1303' ] / df [ 'A1101' ] * 100
X = df [[ '高齢化率' , 'A4103' ]] # 共変量: 高齢化率・合計特殊出生率
ps = LogisticRegression ( max_iter = 1000 ) . fit ( X , df [ 'T' ]) . predict_proba ( X )[:, 1 ]
# IPW で消費支出 (L3221) への ATE 推定(要:注意深く解釈)
ate = ( df [ 'T' ] * df [ 'L3221' ] / ps ) . mean () - (( 1 - df [ 'T' ]) * df [ 'L3221' ] / ( 1 - ps )) . mean ()
print ( f 'IPW-ATE = { ate : .0f } ' )
📝 14. 報告フォーマット
❌ NG例
「都市規模が大きい県ほど消費支出が多い、 という因果関係を確認した。」
✅ OK例
「DID(処置:政策導入 2022 年、 処置群 5 県、 対照群 42 県、 期間 2020-2024)で推定。 処置効果 = 23.4 万円 (95% CI [12.1, 34.7]、 都道府県クラスタロバスト SE)。 プレトレンドはイベントスタディで概ね平行(処置前のリード係数は有意でない)。 ただし共通トレンド仮定の検証は限定的で、 結果は条件付き因果関係として解釈すべき。」
⚠️ 識別仮定が成立しないと因果効果は推定できない
DID・自然実験を使うときの最も重要な仮定は「平行トレンド」です。
平行トレンド仮定 :「処置がなかったら、 処置群と対照群は同じ傾きで動いていたはず」という前提。
この仮定が崩れていると、 DID は因果効果ではなく単なる差 を測ることになります。
事前期間のグラフを必ず確認し、 平行に動いていたかをチェックしてください。
操作変数 (IV) 法では関連性(first stage) と除外制約 の両方が必要です。
RDD ではカットオフ周辺で他の要因も同時に変化していない ことが必要です。
⚠️ パネル因果推論の落とし穴(補強・各 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 補正の必要を検討する。 「データが揃っている県」だけを使うとバイアスが残る。
⚠️ よくある落とし穴 — パネル因果分析
パネル因果分析 (固定効果・DiD・IV) で実務的に最も多い失敗は (1) Pooled OLS と FE の混同で時間不変な交絡を放置、 (2) 平行トレンド仮定の未検証、 (3) クラスター標準誤差の付け忘れ の 3 つ。 SSDSE-B-2026 のように 47 都道府県 × 複数年の構造では、 同一県内の年次誤差が強く相関するため、 t 値・p 値が見かけ上小さくなり「有意」 と誤判断するリスクがある。
❌ Pooled OLS と Fixed Effects を混同
個体差を取り除いていない OLS は内生性で偏る。 必ず固定効果 or 1 階差分。
❌ 並行トレンド仮定
DID では介入前のトレンドが処置群・対照群で平行と仮定する。 グラフで必ず確認。
❌ クラスター標準誤差を忘れる
県内の誤差は時間方向に相関する。 cov_type='cluster' で県別クラスタリング必須。
🛡 防御策まとめ :「適用条件を確認する 」「結果と前提をセットで記述する 」「不確実性を必ず併記する 」の 3 点を習慣化すれば、 上記の罠の大半は回避できます。
📚 関連グループ教材 — 因果推論
パネル因果分析 の全体像 を学ぶには、 横断的な教材から入るのが効率的:
📊 SSDSE-B-2026 で読み解くパネル因果推論の実務 — 47 都道府県 12 年分の縦断データを徹底分析
パネルデータの真価は 同じ個体を時間軸で追跡する ところにある。 ここでは独立行政法人統計センター提供の SSDSE-B-2026 (47 都道府県 × 12 年 = 564 観測、 2012-2023)を用い、 「高齢化率(A1303 / A1101 × 100)」 と 「消費支出(L3221、 二人以上世帯・月平均、 円)」 の関係を 4 通りの推定方法で比較する。 同じデータでも 推定方法を変えると因果効果の符号さえ変わりうる ことを、 具体的な数値で体感していこう。
🔭 何を見るか — 全体マップ
この章で扱うのは次の 6 ステップである。 各ステップで 必ず「なぜそうするのか」 を一言で添える。 ステップを追うごとに「クロスセクション分析の限界」「固定効果がなぜ必要か」「動学パネルでどこまで因果を切り出せるか」が立体的に見えるはずである。
Step 1 : 生データの県規模のばらつき を視覚化する(総人口のヒストグラム)。
Step 2 : 県グループ(クラスタ)別の箱ひげ図で、個体間変動(県差) の大きさを確認する。
Step 3 : 総人口 × 一般診療所数の散布図で、県差が生む強い水準相関 と個体内の因果的問いの違いを押さえる。
Step 4 : Pooled OLS / FE / RE / FD の 4 推定量を計算し、係数の符号と大きさを比較する。
Step 5 : Hausman 検定で FE vs RE のどちらを採用すべきか判定する。
Step 6 : クラスタロバスト標準誤差で頑健性を確認し、結論を導く。
📥 入力データ — SSDSE-B-2026 抜粋(pandas で読み込み)
このコードでやること : SSDSE-B-2026 から「年度」「Code(地域コード)」「Prefecture(都道府県)」を軸に、 「高齢化率(A1303 / A1101 × 100)」「消費支出(L3221、 円)」を計算・抽出し、 long 形式の DataFrame として保持する。 long 形式(県 × 年度ごと 1 行)が以後のパネル分析で必須となる。
📥 入力データ(SSDSE-B-2026 long 形式の先頭 6 行):
年度 Code Prefecture 高齢化率 消費支出
0 2012 R01000 北海道 26.0 281574
1 2012 R02000 青森県 27.0 251153
2 2012 R03000 岩手県 27.9 275001
3 2012 R04000 宮城県 22.9 295433
4 2012 R05000 秋田県 30.7 295071
5 2012 R06000 山形県 28.3 299735
... (全 564 行: 47 都道府県 × 12 年)
📋 コピー 1
2
3
4
5
6
7
8
9
10
11
12
13
14 # SSDSE-B-2026 を long 形式で読む
import pandas as pd
df = pd.read_csv('data/raw/SSDSE-B-2026.csv' , encoding='cp932' , skiprows=[1])
# 必要列だけ抽出 + 名前変更
df = df.rename(columns={'SSDSE-B-2026' : '年度' , 'L3221' : '消費支出' })
panel = df[['年度' , 'Code' , 'Prefecture' , 'A1101' , 'A1303' , '消費支出' ]].copy()
panel['高齢化率' ] = (panel['A1303' ] / panel['A1101' ] * 100 ).round(1 )
panel = panel[['年度' , 'Code' , 'Prefecture' , '高齢化率' , '消費支出' ]]
panel = panel.sort_values(['年度' , 'Code' ]).reset_index(drop=True )
print (f'観測数 = {len(panel)} (47 県 × {panel["年度"].nunique()} 年)' )
print (panel.head())
📤 実行結果:
観測数 = 564 (47 県 × 12 年)
年度 Code Prefecture 高齢化率 消費支出
0 2012 R01000 北海道 26.0 281574
1 2012 R02000 青森県 27.0 251153
2 2012 R03000 岩手県 27.9 275001
3 2012 R04000 宮城県 22.9 295433
4 2012 R05000 秋田県 30.7 295071
💬 47 都道府県 × 12 年 = 564 観測のバランス・パネルが得られた。 以後はこの panel DataFrame を全コードで使い回す。 「Code(都道府県)」と「年度」の 2 重インデックス がパネル分析の鍵である点を意識しておこう。
📈 図 1: 総人口のヒストグラム — 県規模分布の右裾を確認
この図で見ること : 47 都道府県の総人口(A1101、 2023 年度)をヒストグラムで描き、 分布形(右裾の重さ)と中心位置を確認する。 パネルデータでは 個体差(県差) と 時間差(年差) が混ざるため、 まず県規模のばらつきを「全体感」として掴んでおく。 分布を見るだけでは因果効果は読めない。
図 1: SSDSE-B-2026 の 47 都道府県の総人口分布(2023 年度、 百万人)。 中央値は約 1.55 百万人、 右裾に東京都(14.1 百万人)・神奈川県・大阪府・愛知県の大規模県がある。
💬 中央値 1.55 百万人付近にピーク、 右側に 14 百万人超(東京都)の外れ値。 パネル推定では こうした県規模の水準差が固定効果として吸収される ため、 後の係数推定で東京 1 県の水準の影響は小さくなる。 「外れ値があっても FE で吸収できる」ことがパネルの強みでもある。
📦 図 2: クラスタ別の一般診療所数(箱ひげ) — 県グループ間の水準差を定量化
この図で見ること : 47 都道府県を KMeans で 3 クラスタに分け、 クラスタ別に一般診療所数(I5102、 2023 年度)の分布を箱ひげ図で並べる。 県グループ間の水準差(個体間変動) がいかに大きいかを一目で確認できる。 この水準差こそ、 FE が固定効果として吸収する成分である。
図 2: KMeans クラスタ別の一般診療所数(施設、 47 都道府県、 2023 年度)の箱ひげ図。 cluster1 は東京都のみ(約 14,900 施設)、 cluster2 は大規模県、 cluster0 はその他。 箱の位置差が個体間変動の大きさを示す。
💬 箱の 位置差(中央値の高低) が個体間変動(=固定効果として吸収される部分)に対応する。 東京都(cluster1)は 1 県だけで別水準にあり、 大規模県(cluster2)とその他(cluster0)の間にも大きな水準差がある。 パネル因果推論ではこの水準差ではなく、 同じ県の中での年次変動(個体内変動)だけ を使って因果効果を推定する。
🔵 図 3: 総人口 × 一般診療所数 — 散布図に潜む「県差による相関」
この図で見ること : 横軸=総人口(A1101、 百万人)、 縦軸=一般診療所数(I5102、 施設)の散布図(2023 年度、 47 県)。 極めて強い正の相関 (r = 0.972)が見えるが、 これは 「県規模による水準相関(個体間変動)」 であり、 同じ県内で人口が増えた年に診療所が増えるか (個体内変動)は別問題である。 因果的な問いに答えるには、 後述の FE 推定のように個体内変動を切り出す必要がある。
図 3: 総人口(百万人)× 一般診療所数(施設)の散布図(SSDSE-B-2026、 2023 年度、 47 都道府県)。 単純相関は r = 0.972 で極めて強い「正の関係」が見える。
💬 r = 0.972 という強い相関も、 その大半は 県規模という水準差(個体間変動) の反映である。 同様に、 本章の主題である高齢化率 × 消費支出でも、 プールした 564 観測の単純相関は r ≒ -0.11 と負に見えるが、 これを「高齢化が消費を減らす」と読むのは早計である。 「同じ県で 1 年経って高齢化率が上がったとき、 消費支出は変わるか?」 という 因果的問い には FE 推定が必要となる。
📊 表 1: 4 つの推定量の比較 — 同じデータ、 異なる係数
SSDSE-B-2026 の panel(564 観測)に対し、 4 つの推定方法を適用した実測結果を以下に示す。 被説明変数: 消費支出(L3221、 円)。 説明変数: 高齢化率(%、 A1303 / A1101 × 100)。
推定量
係数 β̂
標準誤差
t 値
解釈
Pooled OLS -729.6 284.4 -2.57 高齢化率 1pt 高い観測ほど消費支出 730 円減で有意(県差混入)
Fixed Effects (FE) +8.9 304.1 +0.03 同県内ではほぼゼロ(符号は正) — 符号反転に注目
Random Effects (RE) -82.2 290.7 -0.28 中間値(個体効果と説明変数の無相関を仮定)・非有意
First Difference (FD) +2067.3 1111.5 +1.86 FE と同じく正符号(誤差項のシリアル相関を仮定変更)・5% 水準では非有意
💬 同じデータでも符号が逆転する という事実を必ず確認したい。 Pooled OLS は「県差」と「時間内変動」を混ぜているため負で有意な符号が出るが、 これは因果ではない。 FE 推定は「同じ県の時間内変動」だけを使うので、 係数はほぼゼロ(+8.9、 非有意)となり、 高齢化の進行が県内の消費支出を押し下げる証拠は見られない 。 Pooled の負の相関は、 高齢化率の高い県が構造的に消費支出も低めという地域構造の反映にすぎない。 これが パネル因果推論の威力 である。
🐍 4 推定量を一気に出す Python コード(linearmodels)
このコードでやること : linearmodels.panel の PooledOLS / PanelOLS / RandomEffects / FirstDifferenceOLS を用いて 4 推定量を同時に計算し、 上の表 1 を再現する。 panel.set_index(['Code', '年度']) で MultiIndex 化することが必須。
📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行)
都道府県 A1101(総人口) A1303(65歳以上人口) L3221(消費支出(二人以上の世帯))
北海道 5,092,000 1,681,000 296,888
東京都 14,086,000 3,205,000 341,320
沖縄県 1,468,000 350,000 251,222
…(全 47 行)
📋 コピー 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 # 4 推定量比較 (Pooled OLS / FE / RE / FD)
from linearmodels.panel import PooledOLS, PanelOLS, RandomEffects, FirstDifferenceOLS
import pandas as pd
import statsmodels.api as sm
panel = pd.read_csv('data/raw/SSDSE-B-2026.csv' , encoding='cp932' , skiprows=[1])
panel = panel.rename(columns={'SSDSE-B-2026' : '年度' , 'L3221' : '消費支出' })
panel['高齢化率' ] = panel['A1303' ] / panel['A1101' ] * 100
panel = panel.set_index(['Code' , '年度' ])
y = panel['消費支出' ]
X = sm.add_constant(panel[['高齢化率' ]])
models = {
'Pooled OLS' : PooledOLS(y, X).fit(),
'Fixed Effects' : PanelOLS(y, X, entity_effects=True ).fit(),
'Random Effects' : RandomEffects(y, X).fit(),
'First Difference' : FirstDifferenceOLS(y, panel[['高齢化率' ]]).fit(), # FD は定数項なし
}
for name, m in models.items():
print (f'{name:18s} β̂ = {m.params["高齢化率"]:+8.1f} SE = {m.std_errors["高齢化率"]:6.1f}' )
📤 実行結果:
Pooled OLS β̂ = -729.6 SE = 284.4
Fixed Effects β̂ = +8.9 SE = 304.1
Random Effects β̂ = -82.2 SE = 290.7
First Difference β̂ = +2067.3 SE = 1111.5
💬 表 1 と一致する。 「同じデータ・同じ変数ペアでも、 推定量を変えると符号が反転する」 ことが実測値で確認できた。 「どれが正しいか」 ではなく、 「どの仮定を信じるか 」 が問われる場面である。 次の Hausman 検定で FE vs RE の判定を行う。
🧪 表 2: Hausman 検定で FE と RE のどちらを採用すべきか
Hausman 検定の帰無仮説は 「個体効果と説明変数は無相関 → RE が一致性を持つ」 である。 帰無仮説が棄却されれば FE を選ぶ 。 SSDSE-B-2026(高齢化率 → 消費支出)で実際に計算すると以下のようになる。
統計量
値
解釈
H 統計量 1.04 $(β̂_{FE} - β̂_{RE})^2 / [Var(β̂_{FE}) - Var(β̂_{RE})]$
自由度 1 説明変数の数
p 値 0.307 帰無仮説 (RE 妥当) は棄却されない
判定 RE も許容 個体効果と高齢化率の相関は検出されず → RE も一致性を保つ
💬 実測では p = 0.31 と帰無仮説 (RE 妥当) を棄却できない。 FE(+8.9)と RE(-82.2)の点推定は見かけ上異なるが、 いずれも標準誤差(約 300)に比べて小さく、 「差が偶然の範囲」 と判定されたわけである。 教科書的には RE を採用してよい ケースだが、 検定力が低い(効果自体がほぼゼロ)点には注意が必要で、 実務では保守的に FE を併記報告するのが安全である。 いずれの推定でも 「高齢化率が県内の消費支出を動かす」 という証拠は得られない。
🛡 表 3: クラスタロバスト標準誤差で頑健性確認
パネルデータでは 同じ県内の誤差項に系列相関 がある(不景気が複数年続く等)。 標準的な標準誤差では過小評価され、 t 値が過大化する。 そこで クラスタロバスト標準誤差 (cluster by entity) を計算する。
標準誤差タイプ
SE (β̂_FE)
t 値
p 値
判定
通常 SE 304.1 +0.03 0.977 全く非有意
Heteroskedasticity-robust 307.8 +0.03 0.977 同様に非有意
Cluster-robust (entity) 395.0 +0.02 0.982 系列相関考慮 → さらに非有意
💬 クラスタロバスト SE は通常 SE の 約 1.3 倍 に膨らみ、 p 値は 0.98 とさらに 1 に近づく。 「高齢化率が県内の消費支出に与える因果効果は 統計的に有意ではない 」 が結論となる。 「単純 OLS だと負で有意(t = -2.57)、 FE だと係数はほぼゼロで非有意」 — このギャップを示すこと自体が SSDSE-B-2026 で学べる重要な教訓である。
⚠️ 落とし穴 5 件 — パネル因果推論で実際に起きる失敗
「FE は時間不変変数を吸収する」を忘れる — 性別・人種・出身県など 時間とともに変わらない 変数は FE に取り込まれて推定不能になる。 これらの効果を見たいなら RE か Hausman-Taylor 推定が必要。
強外生性を仮定し続ける — FE の一致性には「説明変数と全期間の誤差項が無相関」が必要。 ラグ付き被説明変数を入れた瞬間に強外生性は崩壊し、 動学パネル(Arellano-Bond GMM)に切り替えるべきである。
クラスタ数が少ないと SE が過小評価される — クラスタ数 (=都道府県数 47) が小さい場合、 クラスタロバスト SE も漸近正規性が破れる。 47 程度ならまだ大丈夫だが、 都道府県ではなく 8 地方区分(北海道・東北...)でクラスタリングするなら wild cluster bootstrap が必要。
不均衡パネル (unbalanced panel) の脱落バイアス — 一部の県だけ年次データが欠落している場合、 脱落理由が説明変数と相関すると FE でもバイアスが残る。 attrition bias と呼ばれる。 SSDSE-B-2026 は完全バランスなのでこの問題はないが、 一般のパネルでは要注意。
Hausman 検定の結果を盲信する — Hausman は標本数が大きいと「些細な相関」でも棄却される傾向がある。 564 観測なら一応信頼できるが、 数千観測のパネルでは「Hausman で必ず FE が選ばれる」現象が起きる。 結果の 大きさ (β̂_FE と β̂_RE の差)も併せて判断したい。
📚 表 4: 関連手法の使い分け早見表
手法
何を仮定するか
使うべき場面
参照
Pooled OLS 個体効果なし プールしても問題ないと信じる時 OLS
Fixed Effects 個体効果と説明変数に相関あり 同個体内変動だけで因果を識別したい時 因果推論
Random Effects 個体効果と説明変数は無相関 時間不変変数の効果も見たい時 Hausman 検定で判定
First Difference 差分後の誤差が iid 2 時点パネル、 系列相関対策 FE と漸近的に同等
DID 平行トレンド仮定 処置群と対照群の前後比較 DID
Arellano-Bond GMM 弱外生性で OK ラグ付き被説明変数を含む動学モデル 操作変数
🧩 章末ミニ Q&A — 自分の理解度を確認しよう
Q1. Pooled OLS と FE で係数の符号が逆転するのはなぜか? → 個体間変動(県差)と個体内変動(時間内変動)の比が異なるため。 Pooled は両方混ぜ、 FE は後者のみ。
Q2. Hausman 検定が p<0.05 を返したらどう判断するか? → RE の前提(個体効果と説明変数の無相関)を棄却 → FE を採用。
Q3. クラスタロバスト SE が通常 SE より大きいときの意味は? → 同一クラスタ内の誤差項に正の系列相関がある(実質的な情報量が見かけより少ない)。
Q4. 時間不変変数(例: 県の地理的位置)の効果は FE で測れるか? → 測れない。 RE か Hausman-Taylor を使う。
Q5. SSDSE-B-2026 の場合、 「高齢化が消費支出を下げる」は因果関係と言えるか? → 言えない。 FE 推定では係数はほぼゼロ(+8.9)かつ統計的に非有意。 Pooled OLS の負で有意な係数は地域構造の反映にすぎない。
この章で学んだのは 「同じデータでも仮定を変えれば結論が変わる」 という統計分析の本質である。 パネルデータと因果推論は、 単なる手法のカタログではなく、 どの変動を使って何を識別するか を自覚するための強力な枠組みである。 SSDSE-B-2026 を手元で動かしながら、 自分の問いに合った推定方法を選び抜く感覚を磨いていこう。
🔬 within 変換と between 変換 — 数式で理解する FE の正体
FE 推定 (固定効果推定) の数学的本質は、 観測値 $y_{it}$ から各個体の時間平均 $\bar{y}_i$ を引く within 変換 である。 説明変数 $x_{it}$ にも同じ変換を施し、 変換後の量に OLS を当てるだけで FE 推定量が得られる。 数式で書けば次のとおり。
$$\tilde{y}_{it} = y_{it} - \bar{y}_i, \quad \tilde{x}_{it} = x_{it} - \bar{x}_i, \quad \hat{\beta}_{FE} = \frac{\sum_i \sum_t \tilde{x}_{it} \tilde{y}_{it}}{\sum_i \sum_t \tilde{x}_{it}^2}$$
この変換により 個体固有の時間不変成分 (県固有の地理・歴史・産業構造など)は完全に消える。 代わりに、 同じ県の中で「平均より高い年・低い年」の差 だけを使って $\beta$ を推定する。 SSDSE-B-2026 の実測では、 東京都の消費支出(L3221)は 12 年平均 327,682 円、 2018 年は 326,229 円なので、 within 変換後の値は -1,453 円となる。 高齢化率も同様に変換し、 両者の共変動から係数を出すわけだ。
対照的に between 推定 は、 各個体の時間平均だけを使って OLS を行う。 数式は次のとおり。
$$\hat{\beta}_{BE} = \frac{\sum_i (\bar{x}_i - \bar{\bar{x}})(\bar{y}_i - \bar{\bar{y}})}{\sum_i (\bar{x}_i - \bar{\bar{x}})^2}$$
SSDSE-B-2026 では between 推定は「47 県の 12 年平均同士の相関」を測ることになる。 実測では between の係数は -1,048(非有意)で、 Pooled OLS(-729.6)と同じ負の符号を返す。 一方 FE は within 変動だけを使うため符号は正(+8.9)になる。 RE 推定量は FE と BE の加重平均 として定式化でき、 重みは個体内分散と個体間分散の比に依存する。 これが「同じデータで 4 推定量が違う値を返す」内部メカニズムである。
⏱ 平行トレンド仮定の数学的意味と SSDSE での確認
DID (Difference-in-Differences) を語るとき必ず登場する 平行トレンド仮定 は、 数式では「処置がなかった場合の対照群と処置群の平均アウトカム差は時間に対して不変」と表現される。 処置 $D_i \in \{0,1\}$、 期間 $t \in \{pre, post\}$ に対し、
$$E[y_{i,post}^{0} - y_{i,pre}^{0} | D_i = 1] = E[y_{i,post}^{0} - y_{i,pre}^{0} | D_i = 0]$$
が成り立つとき、 平均処置効果 (ATT) は単純な差の差で識別できる: $\widehat{ATT} = (\bar{y}_{1,post} - \bar{y}_{1,pre}) - (\bar{y}_{0,post} - \bar{y}_{0,pre})$。 SSDSE-B-2026 で擬似実験を組むなら、 たとえば 「2020 年からの新型感染症拡大」 を処置と見なし、 「人口減少県 (処置群)」 と 「人口増加県 (対照群)」 を比較する設計が考えられる。 ただし平行トレンドが本当に成立するかは、 処置前の数年の消費支出の推移をプロット して目視確認するのが王道である。
FE 推定は 2 時点 × 2 グループの DID の自然な拡張 でもある。 多時点・多グループに一般化すると、 「event study 設計」 と呼ばれる柔軟な推定が可能になる。 近年は 処置効果の異質性 (heterogeneous treatment effects) を考慮した Callaway-Sant'Anna 推定量や Sun-Abraham 推定量が標準的になっており、 単純な双方向 FE が バイアスを持つことがある と知られている。
📐 RE 推定量の閉じた形 — FE と BE の最適加重
Random Effects (RE) 推定量は、 個体効果 $\alpha_i$ と特異誤差 $u_{it}$ の分散を $\sigma_\alpha^2, \sigma_u^2$ として、 次の準差分変換 (quasi-demeaning) で得られる:
$$\theta = 1 - \sqrt{\frac{\sigma_u^2}{T \sigma_\alpha^2 + \sigma_u^2}}, \quad \tilde{y}_{it}^{RE} = y_{it} - \theta \bar{y}_i, \quad \tilde{x}_{it}^{RE} = x_{it} - \theta \bar{x}_i$$
$\theta = 0$ なら Pooled OLS、 $\theta = 1$ なら FE と一致する。 RE はその中間値を最尤的に決める手続きと言える。 SSDSE-B-2026 のように $T = 12$・$\sigma_\alpha^2 \gg \sigma_u^2$ のデータでは実測で $\theta \approx 0.80$ となり、 RE は FE に近い動きをするはずだが、 説明変数と個体効果が相関すれば RE は一致性を失う 。 Hausman 検定はまさにこの相関の有無を試す。
実務上は次の順で判断するのが王道である: (1) Pooled OLS と FE で係数が大きく違うか? (2) 違うなら Hausman 検定で FE を採用、 違わないなら RE で時間不変変数も推定。 (3) 系列相関が疑われるならクラスタロバスト SE。 (4) ラグ付き被説明変数があるなら GMM (Arellano-Bond) へ移行。 — この 4 ステップを覚えておけば、 ほとんどの政策評価・経済分析・労働市場分析でパネル推定を正しく使い分けられる。
🌏 政策評価で実際に使われた事例 — SSDSE と接続できる文脈
パネル因果推論は学術論文だけでなく、 各国政府の政策評価レポート でも標準ツールである。 たとえば日本の地方創生政策、 米国の最低賃金引き上げ効果、 欧州の炭素税導入効果 — いずれも州・都道府県・自治体レベルのパネル分析で検証されてきた。 SSDSE-B-2026 と同じ構造 (47 個体 × 複数年) のデータを使えば、 自分自身で 「もしこの政策がなかったら消費支出はどう推移していたか?」 を反実仮想的に推定できる。
学習用には、 SSDSE-B-2026 の年度列を 「2012-2019 (政策前)」 と 「2020-2023 (政策後)」 に分け、 仮想の処置変数 $D_i$ を 「人口減少率上位 20 県 = 処置群」 として DID を試すのが良い練習になる。 平行トレンドの確認 → 処置効果推定 → クラスタロバスト SE → 異質性検定 (年齢層別・産業別) という流れを SSDSE 1 本でひととおり経験できる。
こうした実務経験は、 因果推論 の理論と DID ・操作変数法 ・回帰不連続デザイン といった応用手法を有機的に結びつけてくれる。 単一手法を完璧にマスターするよりも、 SSDSE のような実データで複数手法を比較する経験 のほうが、 統計学習の定着には圧倒的に効果的である。
📋 表 5: パネル推定の前提条件チェックリスト (実務必携)
チェック項目
確認方法
違反時の対応
バランス・パネルか 各個体の観測数を panel.groupby('id').size() 不均衡なら脱落理由を調査、 場合により Heckman 補正
時系列の長さ T が十分か T >= 5 推奨 (動学パネルは T >= 8) 短いなら FD や繰り返し横断分析へ
説明変数に時間内変動があるか within 分散をチェック 時間不変なら FE では推定不能 → RE or HT
説明変数と個体効果の相関 Hausman 検定 棄却 → FE 採用、 非棄却 → RE 採用
誤差項の系列相関 Wooldridge 検定 (linearmodels の panel モジュール) クラスタロバスト SE 必須
異質処置効果の可能性 処置タイミング × グループのクロス表 Callaway-Sant'Anna 推定へ切替
💬 このチェックリストは、 SSDSE-B-2026 のような実データで 「とりあえず FE を当てる」前に必ず通る関門 である。 6 項目すべてに目を通してから推定に進めば、 投稿論文や政策レポートで査読者から指摘される典型的な誤りはほぼ防げる。
🔁 内部リンクで広げる学習動線
パネル因果推論を本格的に理解するには、 同じ用語集の中で関連ページを横断するのが近道である。 以下に学習動線として推奨するページ群を示す。 ここから「自分の問いに最も近い手法」を探し、 用語ページ → グループ教材 → 元論文の順で深掘りすると効率が良い。
SSDSE-B-2026 を題材にしたパネル因果分析は、 統計学を実社会の意思決定に橋渡しする 最良の練習場である。 47 都道府県という適度なサンプル数、 12 年というほどよい時系列長、 そして経済・人口・教育・医療と多領域にまたがる変数群 — これだけ揃った無料データセットは世界的にも稀有である。 学んだ手法を自分の関心領域 (地方創生・教育格差・医療アクセス・労働市場など) に適用し、 「同じデータから何種類の結論を引き出せるか」 を意識して分析を重ねていこう。
🧠 さらに深掘り — パネル因果推論の哲学的含意
パネル因果推論が他の統計手法と決定的に違うのは、 「同じ個体を時間軸で追跡する」 という観察方法そのものが因果識別の鍵となる点である。 横断データだけ集めても (n を大きくしても) 個体固有の交絡は取り除けない。 たとえば 「教育年数が高い人ほど健康」 という相関は、 横断データだけだと「教育の効果なのか、 もともと健康な人が教育を受けやすいのか」が分離できない。 ところが同じ人を 20 年追跡したパネルデータがあれば、 「教育年数が増えた前後で本人の健康指標がどう変わったか」 を直接観察でき、 個人固有の体質・性格・家庭環境 といった時間不変の交絡は完全に消去できる。
この発想は 「個体内変動 = 自然実験」 という見方と等価である。 同じ個体が時間とともに変化することを、 あたかもランダム化臨床試験のように扱える。 もちろん完全なランダム化ではないため、 並行トレンド仮定や強外生性といった追加の仮定は必要だが、 横断データに比べれば識別の自由度は飛躍的に高まる。 ノーベル経済学賞 (2021 年: David Card, Joshua Angrist, Guido Imbens) も、 こうした準実験デザインを政策評価に体系化した功績で授与された。
SSDSE-B-2026 の枠組みで言えば、 「47 都道府県が 12 年間それぞれ独自の経済・人口動態を経験した」 こと自体が、 一種の自然実験の集合体である。 ある県が突然高齢化を加速させた年があれば、 その県を「処置群」、 加速しなかった県を「対照群」と見なして DID を組める。 こうした発想こそがパネル因果推論の真髄であり、 学習者が「横断データの相関分析」から「縦断データの因果分析」 へとレベルアップする転換点である。
📖 推奨リーディング — 一段上を目指すための基本書
パネル因果推論を本格的に学びたい学習者向けに、 段階別の書籍リストを提示する。 すべて SSDSE-B-2026 の実データで手を動かしながら読むと、 理論と実装の両輪が同時に身につく。
入門レベル : Mostly Harmless Econometrics (Angrist & Pischke) — 因果推論全体の見取り図。 第 5 章でパネル分析の基礎が学べる。
中級レベル : Econometric Analysis of Panel Data (Baltagi) — パネル推定量の数学的導出が網羅的。 院生・研究者向けの定番テキスト。
応用レベル : Causal Inference: The Mixtape (Cunningham) — DID, IV, RDD, Synthetic Control を平易に解説。 R/Python コード付き。
最新動向 : Callaway & Sant'Anna (2021) や Sun & Abraham (2021) の論文を読み、 異質処置効果に対応した推定法を理解する。
日本語 : 西山慶彦・新谷元嗣・川口大司・奥井亮 著『計量経済学』 (有斐閣) — パネル分析の章が SSDSE 級の例で詳しく解説されている。
読書だけでなく 「読んだ手法を SSDSE で再現してみる」 ことが何より重要である。 本に出てくる理論を 1 つ覚えたら、 必ず 1 回は SSDSE-B-2026 か類似のオープンデータで手を動かしてコードを書く。 この繰り返しがパネル因果推論を「使える知識」 に変える唯一の道である。
🎯 まとめ — 47 県 12 年のデータが教えてくれたこと
この章を通じて、 SSDSE-B-2026 の 564 観測がパネル因果推論の教科書例として極めて優秀であることを確認した。 同じデータ・同じ説明変数ペア (高齢化率 → 消費支出) でも、 Pooled OLS は -729.6、 FE は +8.9、 RE は -82.2、 FD は +2067.3 と、 推定量によって符号さえ反転する。 この事実を 「統計手法の混乱」 ではなく 「それぞれの手法が答える問いが違う」 と理解できれば、 パネル因果推論の入口はクリアしたと言える。
最終的に重要なのは 「自分の問いはどの変動で答えられるか」 を意識することである。 「県差を含む全体的な関係を知りたい」 なら Pooled OLS、 「同じ県内の時間変動だけで因果を識別したい」 なら FE、 「個体効果と説明変数の無相関を仮定できる」 なら RE — このように 問いに合わせて推定量を選ぶ 姿勢こそが、 統計学を真に活用するための最高の心構えである。 SSDSE-B-2026 を起点に、 自分の興味分野のパネルデータへと分析範囲を広げ、 統計的因果推論を実装できる人材へとレベルアップしていこう。
🛠 補足: linearmodels と statsmodels の使い分け実務メモ
Python でパネル因果推論を実装する際、 ライブラリ選択でつまずく学習者が多い。 linearmodels (Kevin Sheppard 開発) はパネル推定に特化しており、 PanelOLS / RandomEffects / FirstDifferenceOLS / BetweenOLS が直接呼べる。 一方 statsmodels はパネル機能が限定的で、 ダミー変数を自前で作る LSDV (Least Squares Dummy Variables) しか提供しない。 SSDSE-B-2026 規模 (564 観測) であれば LSDV でも動くが、 数千観測を超えると計算が重くなるため、 実務では linearmodels が標準である。
クラスタロバスト SE を計算したいときは、 linearmodels の fit(cov_type='clustered', cluster_entity=True) を呼ぶだけで良い。 統計検定 (Hausman, Wooldridge) は別途 linearmodels.panel.compare や手動コードが必要だが、 ライブラリの公式ドキュメントに例が豊富である。 GMM (Arellano-Bond) を使いたい場合は linearmodels.panel.AbsorbingLS や R の plm パッケージへの委譲を検討する。 学習段階では linearmodels の 4 推定量比較 + Hausman + クラスタロバスト SE まで触れれば十分である。
R を使う場合は plm (Croissant & Millo) と fixest (Berge) が二大ライブラリである。 fixest は高速で多重固定効果に強く、 SSDSE-B-2026 のような中小規模データなら一瞬で推定が終わる。 「Python だけで完結させたい」 なら linearmodels、 「R も併用できる」 なら fixest を覚えておくと、 学術・実務双方で困らない。 言語に固執せず、 「推定したい量に最も適したツール」 を選ぶ柔軟さを身につけることが重要である。
🌟 最後に — パネル因果推論を「使える」レベルにする 3 つの心得
第 1 に 「データの形を見ずに推定するな」 である。 パネルか繰り返し横断か、 バランスか不均衡か、 T と N のバランスはどうか — これを把握するだけで、 適用すべき推定量が 9 割決まる。 SSDSE-B-2026 を読み込んだら、 まず panel.shape と panel.groupby('Code').size().describe() を確認することを習慣にしよう。 「47 県 × 12 年 = 564 観測のバランス・パネル」と一言で言える状態を作るのが、 すべての分析の出発点である。
第 2 に 「最低でも 4 推定量を並べて報告せよ」 である。 Pooled OLS / FE / RE / FD の 4 つを並べると、 個体効果の重要性、 系列相関の影響、 説明変数と個体効果の相関がほぼ視覚的に判別できる。 査読論文や政策レポートで「FE だけ報告」 はもはや時代遅れであり、 表 1 のような並列比較表が標準的な提示形式になっている。 SSDSE-B-2026 で本稿の表 1 をそのまま再現できる学習者は、 すでに実務水準のスキルを持っていると言って良い。
第 3 に 「結論の頑健性をクラスタロバスト SE で必ず確認せよ」 である。 パネルデータでは誤差項に系列相関がほぼ確実に存在する。 通常 SE で報告した有意性は 過大評価 されている可能性が高く、 クラスタロバスト SE で再計算すると有意性が消えるケースが頻繁にある。 SSDSE-B-2026 の高齢化率 × 消費支出分析でも、 通常 SE で非有意、 クラスタロバスト SE では更に余裕を持って非有意 — というように、 結論の頑健性を二重に確認できた。 この 3 つの心得を習慣化すれば、 統計分析の質は飛躍的に向上する。
そして最後に強調したいのは 「失敗を恐れず実データに触れ続ける」 ことである。 パネル因果推論は最初は難解に映るかもしれないが、 SSDSE-B-2026 のような身近で良質なデータセットを使い、 何度も推定量を比較し、 検定を回し、 標準誤差を見比べていくうちに、 自然と「どの仮定が現実的か」 が直感で判断できるようになる。 教科書を一冊読破するよりも、 手元の SSDSE で一週間こもって 4 推定量を 10 個の説明変数について比較表化する方が、 圧倒的に深い理解につながる。 統計手法は 使ってナンボ の世界である。 自分の手で動かしたコードと、 自分の目で見たグラフだけが、 本当の知識として血肉化する。 ぜひ SSDSE-B-2026 を起点にパネル因果推論を「使える技術」 へと昇華させてほしい。 そしてその技術を自分の関心領域 (地方創生・教育格差・労働市場・医療アクセスなど) に応用し、 社会に貢献できる分析者を目指して学びを継続していこう。 統計と社会の架け橋になるのは、 まさに今この瞬間からの実践である。
🗺 概念マップ
パネル因果推論を中心に、 入力 (パネルデータ・個体 ID×時点)、 主要手法 (差分の差分 DiD・固定効果モデル・合成統制法・操作変数法)、 識別仮定 (並行トレンド・SUTVA・無作為割当)、 検定 (Hausman 検定・プラセボ検定)、 周辺概念 (交絡・因果・反事実) を配置した SVG マップ。
panel causal
並行トレンド仮定
DID・固定効果
合成統制法・DML
SSDSE 政策評価
操作変数法 (IV)
Hausman 検定
本セクションでは パネル因果分析の 固定効果モデル・差分の差分 (DID)・操作変数 (IV) との組合せ を補足する。 SSDSE-B のような複数年・複数都道府県データは、 時間固定効果と県固定効果を同時に入れることで「観測されない交絡」のかなりを吸収でき、 政策効果評価の説得力が増す。
🔗 隣接手法への橋渡し
「パネル因果推論」は単独で完結せず、 前後の手法と組み合わさって価値が発揮される。 入力データの準備 (上流)・同目的の代替手法との比較 (並列)・結果の活用 (下流) という 3 軸で隣接領域を整理する。
この上流・並列・下流の対応を地図化することで、 「パネル因果推論」を中核に据えた分析パイプライン (データ準備 → 手法選択 → 結果の検証と展開) の全体像が見えてくる。
🌳 手法選択フロー
「パネルデータの因果推論」を実際に使うとき、 何をどう選ぶかを順に判断する。 上から順に答えていくと、 使うべき手法と評価の仕方が決まる。
個体を追跡できているか 同じ都道府県を複数年にわたって観測しているなら固定効果が使える。 SSDSE-B-2026 は 47 県 × 12 年度なので、 県ごとの時間不変な差を落とせる。 毎年別の対象なら、 それは繰り返し横断データで別の扱いになる。
落としたいのは何の差か 県ごとの気質・地理のように時間で変わらない要因は固定効果で落ちる。 全国一律の景気変動は年次ダミーで落ちる。 両方入れる(二元固定効果)のが基本形。
固定効果か、 変量効果か 個体効果が説明変数と相関していそうなら固定効果。 相関が無いと仮定できるなら変量効果のほうが効率的。 ハウスマン検定はこの仮定を検討する材料になる。
標準誤差をどう計算するか 同じ県の観測は年をまたいで相関する。 通常の標準誤差では過小評価になるので、 県単位でクラスタ頑健にする。
固定効果で落とせるのは「時間で変わらない差」だけ。 年ごとに変わる未観測要因が残っていれば、 依然として因果とは言えない。
3. econml — Causal Forest(heterogeneous treatment effect)
📋 コピー 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' , skiprows = [ 1 ])
# 47 県 × 12 年 = 564 行のパネルをプールして使用
Y = df [ 'L3221' ] . values # 消費支出
T = ( df [ 'A1101' ] >= df [ 'A1101' ] . median ()) . astype ( int ) . values # 都市規模
X = df [[ 'A1303' , 'A4103' , 'I5102' , 'G7101' , 'H1800' ]] . 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 ())
4. scipy — Hausman 検定の手書き
📋 コピー 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 import numpy as np
from scipy.stats import chi2
from linearmodels.panel import PanelOLS , RandomEffects # fe / re はこの推定結果
# 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 } ' )
5. dowhy — 因果推論パイプライン
📋 コピー 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' , skiprows = [ 1 ])
df [ 'T' ] = ( df [ 'A1101' ] >= df [ 'A1101' ] . median ()) . astype ( int ) # 都市規模高低
df [ 'Y' ] = df [ 'L3221' ] # 消費支出
# 共通原因: 高齢者数・合計特殊出生率・一般診療所数
model = CausalModel ( data = df , treatment = 'T' , outcome = 'Y' ,
common_causes = [ 'A1303' , 'A4103' , 'I5102' ])
identified = model . identify_effect ()
estimate = model . estimate_effect ( identified , method_name = 'backdoor.linear_regression' )
print ( 'ATE:' , estimate . value )