このページの分析を自分で再現するには、以下の手順でデータを準備してください。コードの編集は不要です。
data/raw/ フォルダに入れます。html/figures/ に自動保存されます。
日本の地方人口流出は長年の政策課題である。特に「進学時」(高校卒業後の大学進学)と「就職時」(大学卒業後の就職)という2つのライフイベントが人口移動の主要なトリガーとなることが指摘されている。本研究は47都道府県 × 2012〜2023年度のパネルデータを用い、固定効果(FE)モデルにより、転出率・純移動率に対する大学進学率・有効求人倍率などの要因を特定した。
まず「パネルデータを用いた進学と就職時による人口流出の要因分析」を統計的にとらえることが有効だと考えられる。 その理由は感覚や経験則だけでは、複雑な社会要因の中で「何が本当に効いているか」を見極めにくいからである。 本研究では公開データと統計手法を組み合わせ、この問いに定量的な答えを出すことを目指す。
パネルデータ(FE) Hausman検定 大学進学率 有効求人倍率
SSDSE(社会・人口統計体系データセット)-B は都道府県レベルの年次データを収録する。本分析では47都道府県 × 2012〜2023年度の12時点からなるバランスド・パネル(564観測)を用いた。
| 変数の役割 | 変数名(SSDSE-B) | 計算式・備考 |
|---|---|---|
| 目的変数① 転出率 |
転出者数(日本人移動者)/ 総人口 × 100 | A5102/A1101相当 (%) |
| 目的変数② 純移動率 |
(転入 − 転出)/ 総人口 × 100 | (A5101−A5102)/A1101相当 (%) |
| 大学進学率 | 高校卒業者のうち進学者数 / 高校卒業者数 × 100 | E4602/E4601相当 (%) 進学移動の代理変数 |
| 有効求人倍率 | 月間有効求人数 / 月間有効求職者数 | F3103/F3102相当 就職機会・地域経済力 |
| 消費支出 | 消費支出(二人以上の世帯) | L3221相当(円/月) 経済的豊かさの代理 |
| 住宅地標準価格 | 標準価格(平均価格)(住宅地) | C5401相当(円/m²) 居住コスト |
| 高齢化率 | 65歳以上人口 / 総人口 × 100 | A1303/A1101相当 (%) |
| TFR(合計特殊出生率) | 合計特殊出生率 | A4103相当 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | df_b = pd.read_csv(DATA_B, encoding='cp932', header=1) # 都道府県レベルのみ(地域コードが R+5桁数字) df_b = df_b[df_b['地域コード'].str.match(r'^R\d{5}$', na=False)].copy() # 使用列(日本語名) needed_cols_raw = [ '総人口', # A1101 相当 '転出者数(日本人移動者)', # A5102 相当 '転入者数(日本人移動者)', # A5101 相当 '高等学校卒業者数', # E4601 相当 '高等学校卒業者のうち進学者数', # E4602 相当 '月間有効求人数(一般)', # F3103 相当 '月間有効求職者数(一般)', # F3102 相当 '消費支出(二人以上の世帯)', # L3221 相当 '標準価格(平均価格)(住宅地)', # C5401 相当 '65歳以上人口', # A1303 相当 '合計特殊出生率', # A4103 相当 ] for col in needed_cols_raw: df_b[col] = pd.to_numeric(df_b[col], errors='coerce') df_b['年度'] = pd.to_numeric(df_b['年度'], errors='coerce') |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。pd.read_csv(...) でCSVを読み込みます。encoding='cp932' は日本語Windows由来の文字コード、header=1 は「2行目を列名として使う」。df['地域コード'].str.match(r'^R\d{5}', ...) — 正規表現で「R+数字5桁」の行(47都道府県)だけTrueにし、真偽値で行をフィルタ。df['A'] / df['B'] — pandasの列同士の四則演算は要素ごと(element-wise)。forループ不要なのが強み。24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | df_b['転出率'] = df_b['転出者数(日本人移動者)'] / df_b['総人口'] * 100 df_b['net_mig率'] = (df_b['転入者数(日本人移動者)'] - df_b['転出者数(日本人移動者)']) / df_b['総人口'] * 100 df_b['大学進学率'] = df_b['高等学校卒業者のうち進学者数'] / df_b['高等学校卒業者数'] * 100 df_b['有効求人倍率'] = df_b['月間有効求人数(一般)'] / df_b['月間有効求職者数(一般)'] df_b['高齢化率'] = df_b['65歳以上人口'] / df_b['総人口'] * 100 # 欠損除外と必要列の抽出 model_cols = [ '転出率', 'net_mig率', '大学進学率', '有効求人倍率', '消費支出(二人以上の世帯)', '標準価格(平均価格)(住宅地)', '高齢化率', '合計特殊出生率' ] df_panel = df_b.dropna(subset=model_cols + ['都道府県', '年度']).copy() df_panel = df_panel.set_index(['都道府県', '年度']) n_prefs = df_panel.index.get_level_values(0).nunique() n_years = df_panel.index.get_level_values(1).nunique() years_range = sorted(df_panel.index.get_level_values(1).unique()) print(f"パネルデータ: {n_prefs} 都道府県 × {n_years} 年度({years_range[0]}〜{years_range[-1]})") print(f"総観測数: {len(df_panel)}") |
パネルデータ: 47 都道府県 × 12 年度(2012〜2023) 総観測数: 564
.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。パネルデータの分析では、固定効果(FE)モデルとランダム効果(RE)モデルのどちらを使うべきかを判断する必要がある。
| モデル | 固定効果 αi | 前提 | 推定効率 |
|---|---|---|---|
| 固定効果(FE) | パラメータとして推定 | αi と説明変数の相関を許容 | 低め(within変動のみ使用) |
| 変量効果(RE) | ランダム変数として扱う | αi と説明変数は無相関 | 高い(between変動も使用) |
Hausman(1978)検定は「変量効果の前提が成立するか」を検証する。帰無仮説「αi と説明変数は無相関(RE有効)」が棄却されれば、FEモデルを採用する。
45 46 47 48 49 50 51 52 53 54 55 | df_b2 = df_b.copy() df_b2 = df_b2.dropna(subset=['転出率', '年度']) ts = df_b2.groupby('年度')['転出率'].mean() fig, ax = plt.subplots(figsize=(10, 5)) ax.plot(ts.index, ts.values, marker='o', lw=2.5, color='#1565C0', markersize=7, label='全国平均転出率(%)') ax.fill_between(ts.index, ts.values * 0.95, ts.values * 1.05, alpha=0.15, color='#1565C0') ax.axvline(2012, color='gray', lw=1, linestyle='--', alpha=0.5) ax.axvline(2023, color='gray', lw=1, linestyle='--', alpha=0.5) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。df.groupby('列').apply(関数) — グループごとに関数を適用。時系列や地域別の集計でよく使います。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。ax.fill_between(...) — 2つの曲線で囲まれた領域を塗りつぶし。Lorenz曲線の格差面積などを可視化。df[col](1列)と df[[col1, col2]](複数列)でカッコの数が違います。リストを渡していると覚えるとミスを減らせます。56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | # 注目年のアノテーション peak_yr = ts.idxmax() ax.annotate(f'最大: {ts.max():.2f}%\n({peak_yr}年度)', xy=(peak_yr, ts.max()), xytext=(peak_yr + 1, ts.max() + 0.05), fontsize=9, color='#C62828', arrowprops=dict(arrowstyle='->', color='#C62828', lw=1.2)) ax.set_xlabel("年度", fontsize=11) ax.set_ylabel("転出率(転出者数/総人口 × 100, %)", fontsize=11) ax.set_title("図1: 47都道府県の転出率(全国平均)2012〜2023年度\n" "データ出典: SSDSE-B-2026(総務省 住民基本台帳人口移動報告)", fontsize=11) ax.legend(fontsize=10) ax.grid(True, alpha=0.3) ax.set_xticks(ts.index) plt.tight_layout() plt.savefig(FIG_DIR + "2023_U5_1_fig1_ts.png", dpi=150) plt.close() print("\nfig1 saved") |
fig1 saved
fig.savefig(..., bbox_inches='tight') — 余白を自動で詰めて保存。plt.close() でメモリ解放。s[:-n]「末尾n文字を除く」/s[n:]「先頭n文字を除く」。スライス [start:stop:step] はリスト・タプル・文字列共通の基本ワザです。まず、目的変数「転出率」の全国平均の推移を確認する。転出率はパネル分析の前提となる時間内変動(within variation)が存在することを示している。
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | def coef_plot(res, pred_cols, short_labels, title, figpath): coefs = res.params[pred_cols].values ses = res.std_errors[pred_cols].values pvals = res.pvalues[pred_cols].values colors = ['#C62828' if c > 0 else '#1565C0' for c in coefs] alphas = [0.9 if sig_star(p) != 'n.s.' else 0.45 for p in pvals] fig, ax = plt.subplots(figsize=(9, 5)) y_pos = range(len(short_labels)) bars = ax.barh(y_pos, coefs, color=colors, alpha=0.8, xerr=1.96 * ses, capsize=5, error_kw=dict(ecolor='gray', lw=1.5)) ax.axvline(0, color='black', lw=1.5) for i, (c, se_i, p_v) in enumerate(zip(coefs, ses, pvals)): star = sig_star(p_v) offset = max(abs(c) * 0.03, 0.001) ha_pos = 'left' if c >= 0 else 'right' x_pos = c + (1.96 * se_i + offset) if c >= 0 else c - (1.96 * se_i + offset) color_s = '#C62828' if star not in ('n.s.',) else 'gray' ax.text(x_pos, i, star, va='center', ha=ha_pos, fontsize=12, color=color_s) ax.set_yticks(list(y_pos)) ax.set_yticklabels(short_labels, fontsize=10) ax.set_xlabel("FE推定係数(95% CI, クラスター標準誤差)", fontsize=10) ax.set_title(title, fontsize=11) ax.grid(True, axis='x', alpha=0.3) red_p = mpatches.Patch(color='#C62828', alpha=0.8, label='正の係数(転出増加)') blue_p = mpatches.Patch(color='#1565C0', alpha=0.8, label='負の係数(転出減少)') ax.legend(handles=[red_p, blue_p], fontsize=9, loc='lower right') plt.tight_layout() plt.savefig(figpath, dpi=150) plt.close() coef_plot( res_out, PRED_COLS, SHORT_LABELS, f"図2: 固定効果(FE)回帰係数 — 目的変数: 転出率(%)\n" f"Within R²={res_out.rsquared_within:.3f} " f"N={n_prefs}都道府県×{n_years}年度 " f"*** p<0.001 ** p<0.01 * p<0.05 † p<0.10 n.s. 有意でない", FIG_DIR + "2023_U5_1_fig2_fe_out.png" ) print("fig2 saved") |
fig2 saved
fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。fig.savefig(..., bbox_inches='tight') — 余白を自動で詰めて保存。plt.close() でメモリ解放。s[:-n]「末尾n文字を除く」/s[n:]「先頭n文字を除く」。スライス [start:stop:step] はリスト・タプル・文字列共通の基本ワザです。目的変数を転出率(転出者数/総人口 × 100)とした固定効果パネル回帰の推定結果。都道府県固定効果を制御した上での「時間内変動」に基づく推定。
| 説明変数 | 推定係数 | 有意水準 | 解釈 |
|---|---|---|---|
| 有効求人倍率 | +0.058 | * (p≈0.024) | 求人倍率が上がると転出が増える(好景気時の移動活発化) |
| 高齢化率 | −0.010 | * (p≈0.021) | 高齢化が進むと転出が減る(若年人口が元々少ない) |
| 大学進学率 | +0.001 | n.s. | within変動では転出率との直接的関連は限定的 |
| 消費支出 | +0.000 | n.s. | 有意でない |
| 住宅地標準価格 | ≈0.000 | n.s. | 有意でない |
| TFR | +0.021 | n.s. | 有意でない |
123 124 125 126 127 128 129 130 131 | coef_plot( res_net, PRED_COLS, SHORT_LABELS, f"図3: 固定効果(FE)回帰係数 — 目的変数: 純移動率(%)\n" f"Within R²={res_net.rsquared_within:.3f} " f"N={n_prefs}都道府県×{n_years}年度 " f"*** p<0.001 ** p<0.01 * p<0.05 † p<0.10 n.s. 有意でない", FIG_DIR + "2023_U5_1_fig3_fe_net.png" ) print("fig3 saved") |
fig3 saved
np.cumsum(arr) は累積和、np.linspace(a, b, n) は「aからbを等間隔でn個」。NumPyの定石です。目的変数を純移動率(転入−転出)/総人口 × 100)に変えた固定効果モデル。転入・転出の差分により、地域の「魅力度」の変化を捉える。
| 説明変数 | 推定係数 | 有意水準 | 解釈 |
|---|---|---|---|
| 住宅地標準価格 | −0.000 | ** (p≈0.001) | 地価上昇時に純流出が増加(居住コスト上昇による転出) |
| 大学進学率 | +0.006 | n.s. (p≈0.107) | 進学率上昇で純移動改善の傾向(境界有意) |
| 有効求人倍率 | −0.009 | n.s. | 有意でない |
| 高齢化率 | −0.008 | n.s. | 有意でない |
| 消費支出 | ≈0.000 | n.s. | 有意でない |
| TFR | −0.014 | n.s. | 有意でない |
一つの目的変数だけで分析すると、結果が特定の指標の性質に依存する可能性がある。「転出率」と「純移動率(転入−転出)」という2つの指標を使うことで、人口流出現象を多面的に検証できる。
133 134 135 136 137 138 139 140 141 142 143 144 145 | df_2022 = df_b[df_b['年度'] == 2022].dropna( subset=['大学進学率', '転出率', '都道府県'] ).copy() fig, ax = plt.subplots(figsize=(10, 7)) scatter = ax.scatter( df_2022['大学進学率'], df_2022['転出率'], c=df_2022['有効求人倍率'], cmap='RdYlBu_r', s=70, alpha=0.8, zorder=3, vmin=0.5, vmax=2.5 ) cb = plt.colorbar(scatter, ax=ax, pad=0.02) cb.set_label('有効求人倍率', fontsize=10) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。{値:.2f}(小数2桁)、{値:,}(3桁区切り)、{値:>10}(右寄せ10桁)など、覚えると出力が一気に整います。146 147 148 149 150 151 152 | # 回帰直線 x_vals = df_2022['大学進学率'].values y_vals = df_2022['転出率'].values slope, intercept_lr, r_val, p_val, _ = stats.linregress(x_vals, y_vals) x_line = np.linspace(x_vals.min(), x_vals.max(), 200) ax.plot(x_line, intercept_lr + slope * x_line, color='#C62828', lw=2.5, label=f'回帰直線 (r={r_val:.2f}, p={p_val:.3f})') |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。stats.linregress(x, y) — 単回帰の傾き・切片・r値・p値・標準誤差を返します。使わない値は _ で受け取り。plt.subplots(figsize=(W, H)) で図サイズ指定、fig.savefig(..., bbox_inches='tight') で余白を自動で詰めて保存。153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | # 注目都道府県のラベル highlight = {'東京都', '神奈川県', '大阪府', '秋田県', '青森県', '沖縄県', '島根県', '鳥取県', '京都府', '愛知県'} for _, row in df_2022.iterrows(): pref = row['都道府県'] if pref in highlight: ax.annotate( pref, (row['大学進学率'], row['転出率']), xytext=(row['大学進学率'] + 0.3, row['転出率'] + 0.02), fontsize=8, arrowprops=dict(arrowstyle='->', color='gray', lw=0.8) ) ax.set_xlabel("大学進学率(高校卒業者のうち進学者の割合, %)", fontsize=11) ax.set_ylabel("転出率(転出者数/総人口 × 100, %)", fontsize=11) ax.set_title( "図4: 大学進学率 vs 転出率(47都道府県, 2022年度断面)\n" "点の色は有効求人倍率(青=低, 赤=高)。データ: SSDSE-B-2026", fontsize=11 ) ax.legend(fontsize=10) ax.grid(True, alpha=0.3) plt.tight_layout() plt.savefig(FIG_DIR + "2023_U5_1_fig4_scatter.png", dpi=150) plt.close() print("fig4 saved") print("\n=== 分析完了 ===") print("実データ(SSDSE-B-2026, 47都道府県 × 2012-2023年度)による固定効果パネル回帰") print(f"転出率モデル: Within R² = {res_out.rsquared_within:.4f}") print(f"純移動率モデル: Within R² = {res_net.rsquared_within:.4f}") print("図1〜4 を html/figures/ に保存しました。") |
fig4 saved === 分析完了 === 実データ(SSDSE-B-2026, 47都道府県 × 2012-2023年度)による固定効果パネル回帰 転出率モデル: Within R² = 0.0587 純移動率モデル: Within R² = 0.0517 図1〜4 を html/figures/ に保存しました。
for _, row in df.iterrows() — DataFrameを1行ずつ取り出すループ。1点ずつ描画したいときに使用。fig.savefig(..., bbox_inches='tight') — 余白を自動で詰めて保存。plt.close() でメモリ解放。.dropna() は欠損行を除去、.copy() は独立したコピーを作る。pandasで警告を防ぐ定石。パネル分析を補完するため、2022年度の断面データで大学進学率と転出率の関係を可視化する。点の色は有効求人倍率を表し、就職機会との交差効果を示す。
断面(クロスセクション)分析では「観察されない都道府県固有の特性」が混入する。たとえば「東京は大学も多く転出率も高い」のは、単に東京の規模・機能の問題かもしれない。パネルFEはこうした時間不変の固定効果を除去し、より純粋な変動要因を推定する。
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 | import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt import matplotlib.patches as mpatches import numpy as np import pandas as pd from scipy import stats from linearmodels import PanelOLS plt.rcParams['font.family'] = 'Hiragino Sans' plt.rcParams['axes.unicode_minus'] = False import os FIG_DIR = os.path.normpath('html/figures') + os.sep DATA_B = 'data/raw/SSDSE-B-2026.csv' os.makedirs(FIG_DIR, exist_ok=True) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。import pandas as pd など — 必要なライブラリをまとめて呼び出します。as pd は短い別名(alias)。matplotlib.use('Agg') — グラフを画面表示せずファイルに保存するためのおまじない。plt.rcParams['font.family'] — グラフの日本語表示用フォント指定(Macは Hiragino Sans、Windowsなら Yu Gothic 等)。os.makedirs('html/figures', exist_ok=True) — 図の保存先フォルダを作る(既にあってもOK)。f"...{x}..." はf-string。文字列の中に {変数} と書くだけで埋め込めて、{x:.2f} のように書式も指定できます。203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 | PRED_COLS = [ '大学進学率', '有効求人倍率', '消費支出(二人以上の世帯)', '標準価格(平均価格)(住宅地)', '高齢化率', '合計特殊出生率', ] PRED_LABELS = [ '大学進学率\n(%)', '有効求人倍率', '消費支出\n(円/月)', '住宅地\n標準価格(円)', '高齢化率\n(%)', 'TFR', ] SHORT_LABELS = ['大学進学率', '有効求人倍率', '消費支出', '住宅地価格', '高齢化率', 'TFR'] |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 | import statsmodels.api as sm def run_fe(dep_name): dep_var = df_panel[dep_name] exog_data = df_panel[PRED_COLS].copy() exog_data = sm.add_constant(exog_data) model = PanelOLS(dep_var, exog_data, entity_effects=True, time_effects=False) res = model.fit(cov_type='clustered', cluster_entity=True) return res res_out = run_fe('転出率') res_net = run_fe('net_mig率') def sig_star(p): if p < 0.001: return "***" if p < 0.01: return "**" if p < 0.05: return "*" if p < 0.10: return "†" return "n.s." def print_result(res, title): print(f"\n=== {title} ===") params = res.params pvals = res.pvalues ses = res.std_errors print(f"Within R² = {res.rsquared_within:.4f} " f"Between R² = {res.rsquared_between:.4f} " f"Overall R² = {res.rsquared:.4f}") print(f"{'変数':<28} {'係数':>10} {'SE':>8} {'p値':>8} {'有意':>5}") for nm in PRED_COLS + ['const']: if nm in params.index: c, s, p = params[nm], ses[nm], pvals[nm] print(f" {nm:<26} {c:>10.4f} {s:>8.4f} {p:>8.4f} {sig_star(p):>5}") print_result(res_out, "FE回帰 目的変数①: 転出率(%)") print_result(res_net, "FE回帰 目的変数②: 純移動率(%)") |
=== FE回帰 目的変数①: 転出率(%) === Within R² = 0.0587 Between R² = 0.0535 Overall R² = 0.0587 変数 係数 SE p値 有意 大学進学率 0.0006 0.0023 0.7981 n.s. 有効求人倍率 0.0575 0.0254 0.0238 * 消費支出(二人以上の世帯) 0.0000 0.0000 0.5375 n.s. 標準価格(平均価格)(住宅地) -0.0000 0.0000 0.9035 n.s. 高齢化率 -0.0100 0.0043 0.0207 * 合計特殊出生率 0.0210 0.0637 0.7425 n.s. const 1.8328 0.2171 0.0000 *** === FE回帰 目的変数②: 純移動率(%) === Within R² = 0.0517 Between R² = -0.9793 Overall R² = 0.0517 変数 係数 SE p値 有意 大学進学率 0.0056 0.0035 0.1071 n.s. 有効求人倍率 -0.0088 0.0430 0.8382 n.s. 消費支出(二人以上の世帯) 0.0000 0.0000 0.7176 n.s. 標準価格(平均価格)(住宅地) -0.0000 0.0000 0.0013 ** 高齢化率 -0.0080 0.0073 0.2723 n.s. 合計特殊出生率 -0.0138 0.1046 0.8954 n.s. const -0.0914 0.3540 0.7963 n.s.
import pandas as pd など — 必要なライブラリをまとめて呼び出します。as pd は短い別名(alias)。sm.add_constant(X) — 切片項(定数1の列)を先頭に追加。statsmodelsで必須。r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 | from linearmodels import RandomEffects re_out = RandomEffects(df_panel['転出率'], sm.add_constant(df_panel[PRED_COLS])).fit() re_net = RandomEffects(df_panel['net_mig率'], sm.add_constant(df_panel[PRED_COLS])).fit() print("\n--- Hausman 検定の参考情報 ---") print("FE Within R² (転出率):", f"{res_out.rsquared_within:.4f}") print("RE Overall R² (転出率):", f"{re_out.rsquared:.4f}") print("FE Within R² (純移動率):", f"{res_net.rsquared_within:.4f}") print("RE Overall R² (純移動率):", f"{re_net.rsquared:.4f}") print("→ FE係数とRE係数の差が大きければ、固定効果モデルが適切(Hausman検定の原理)") # 係数比較(教育用) fe_coefs = res_out.params[PRED_COLS].values re_coefs = re_out.params[PRED_COLS].values hausman_stat = float(np.sum((fe_coefs - re_coefs) ** 2)) print(f" 簡易Hausman距離(転出率モデル): {hausman_stat:.4f}") print(" ※ 距離が大きいほどFEとREの推定値が乖離 → 固定効果モデルを採用") |
--- Hausman 検定の参考情報 --- FE Within R² (転出率): 0.0587 RE Overall R² (転出率): 0.0660 FE Within R² (純移動率): 0.0517 RE Overall R² (純移動率): 0.2007 → FE係数とRE係数の差が大きければ、固定効果モデルが適切(Hausman検定の原理) 簡易Hausman距離(転出率モデル): 0.0001 ※ 距離が大きいほどFEとREの推定値が乖離 → 固定効果モデルを採用
import pandas as pd など — 必要なライブラリをまとめて呼び出します。as pd は短い別名(alias)。sm.add_constant(X) — 切片項(定数1の列)を先頭に追加。statsmodelsで必須。x if cond else y は三項演算子。リスト内包表記と組み合わせると、forとifを1行で書けます。SSDSE-B の47都道府県 × 2012〜2023年度のバランスドパネルデータを用いた固定効果(FE)回帰分析の結果:
FEモデルは都道府県固有の時間不変な効果を除去できるが、同時に「大学進学率が慢性的に高い都道府県は転出が多い」というbetween変動(都道府県間差)の情報も捨てる。Within R²が低い場合は、時間固定効果(time_effects=True)の追加や、ダイナミックパネル(ラグ付き目的変数)の検討も有効。
| データ | 出典 |
|---|---|
| SSDSE-B 都道府県パネルデータ(2012〜2023年度) | 統計数理研究所 SSDSE(社会・人口統計体系データセット) |
| 転出者数・転入者数(日本人移動者) | 総務省 住民基本台帳人口移動報告 |
| 高等学校卒業者・進学者数 | 文部科学省 学校基本調査 |
| 月間有効求人数・求職者数 | 厚生労働省 職業安定業務統計(ハローワーク) |
本教育用コードは実データ(SSDSE-B-2026.csv)のみを使用。合成データ・乱数生成は一切含まない。
統計分析の解釈で初心者がやりがちな勘違いをまとめます。特に「相関と因果の混同」「p値の過信」は研究現場でもよく起きる落とし穴です。本文を読む前にも、読んだ後にも、目を通してみてください。
統計の基本用語を初心者向けに解説します。本文中で見慣れない言葉が出てきたら、ここに戻って確認してください。
統計手法について「何のためか」「結果をどう読むか」を初心者向けに解説します。
この研究をさらに発展させるための3つの方向性を示します。「今回わかったこと(X)」から「次に検証すべき仮説(Y)」を立て、「具体的に何をするか(Z)」まで考えてみましょう。
学んだだけでは身につきません。実際に手を動かすのが最強の学習方法です。本論文のスクリプトをベースに、以下のチャレンジに挑戦してみてください。難易度別に5つ用意しました。
本論文で学んだ手法は、研究の世界だけでなく、行政・企業・NPO の現場でも様々に活用されています。具体的なシーンを紹介します。
この論文を読んで初心者が抱きやすい疑問に、教育的観点から答えます。