このページの分析を自分で再現するには、以下の手順でデータを準備してください。コードの編集は不要です。
data/raw/ フォルダに入れます。html/figures/ に自動保存されます。
日本の地方では人口減少と若年層の流出が続き、都市圏への一極集中が深刻化している。本研究は「なぜある都道府県では人が集まり、別の都道府県では人が流出するのか」という問いに統計的に答えることを目的とする。
まず「地域格差と人口移動固定効果パネルモデルによる転出入要因の分析」を統計的にとらえることが有効だと考えられる。 その理由は感覚や経験則だけでは、複雑な社会要因の中で「何が本当に効いているか」を見極めにくいからである。 本研究では公開データと統計手法を組み合わせ、この問いに定量的な答えを出すことを目指す。
鍵となる概念は純移動率(転入超過率)であり、単純な人口増減ではなく「人の移動そのもの」を捉える指標である。47都道府県 × 12年(2012〜2023年)のパネルデータを用いて、固定効果モデル(FE-OLS)により転出入の決定要因を推定する。
SSDSE-B パネルデータ 固定効果モデル Hausman検定 クラスター標準誤差 時系列分析
| 項目 | 内容 |
|---|---|
| 出典 | SSDSE-B-2026.csv(社会・人口統計体系 都道府県データ) |
| 対象 | 全47都道府県 |
| 期間 | 2012〜2023年度(12時点) |
| サンプル数 | 最大 47 × 12 = 564 観測 |
| 推定手法 | PanelOLS(固定効果)、クラスタリング標準誤差 |
転入者数と転出者数はいずれも「日本人移動者」を使用。千分率(‰)で表示することで都道府県の規模差を除去している。
まず、代表的な都道府県について純移動率の時系列変化を確認する。東京都(関東)、大阪府・愛知県(三大都市圏)、秋田県・島根県(人口流出地域)、沖縄県(独自のパターン)を選んだ。
単純な「転入数 - 転出数」は都道府県の人口規模に依存する。東京都は規模が大きいため差が大きくなりやすい。人口で割って千分率にすることで比較可能な指標を作る。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | import numpy as np import pandas as pd import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt import warnings warnings.filterwarnings('ignore') from linearmodels.panel import PanelOLS, RandomEffects import statsmodels.api as sm from scipy import stats plt.rcParams['font.family'] = 'Hiragino Sans' plt.rcParams['axes.unicode_minus'] = False plt.rcParams['figure.dpi'] = 150 import os FIG_DIR = 'html/figures' DATA_B = 'data/raw/SSDSE-B-2026.csv' os.makedirs(FIG_DIR, exist_ok=True) df_b = pd.read_csv(DATA_B, encoding='cp932', header=1) df_b = df_b[df_b['地域コード'].str.match(r'^R\d{5}$', na=False)].copy() df_b['年度'] = df_b['年度'].astype(int) df_b = df_b.sort_values(['都道府県', '年度']).reset_index(drop=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)。pd.read_csv(...) でCSVを読み込みます。encoding='cp932' は日本語Windows由来の文字コード、header=1 は「2行目を列名として使う」。df['地域コード'].str.match(r'^R\d{5}', ...) — 正規表現で「R+数字5桁」の行(47都道府県)だけTrueにし、真偽値で行をフィルタ。.astype(int) — 列を整数に変換(年度などを数値比較するため)。sort_values('列名', ascending=False) — 指定列で並べ替え(降順)。f"...{x}..." はf-string。文字列の中に {変数} と書くだけで埋め込めて、{x:.2f} のように書式も指定できます。26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | # 変数生成 df_b['純移動率'] = (df_b['転入者数(日本人移動者)'] - df_b['転出者数(日本人移動者)']) / df_b['総人口'] * 1000 df_b['求人倍率'] = df_b['月間有効求人数(一般)'] / df_b['月間有効求職者数(一般)'].replace(0, np.nan) df_b['消費支出_log'] = np.log(df_b['消費支出(二人以上の世帯)'].clip(lower=1)) df_b['住宅地価_log'] = np.log(df_b['標準価格(平均価格)(住宅地)'].clip(lower=1)) df_b['高齢化率'] = df_b['65歳以上人口'] / df_b['総人口'] * 100 df_b['保育所密度'] = df_b['保育所等数'] / df_b['総人口'] * 10000 region_map = { '北海道': '北海道・東北', '青森県': '北海道・東北', '岩手県': '北海道・東北', '宮城県': '北海道・東北', '秋田県': '北海道・東北', '山形県': '北海道・東北', '福島県': '北海道・東北', '茨城県': '関東', '栃木県': '関東', '群馬県': '関東', '埼玉県': '関東', '千葉県': '関東', '東京都': '関東', '神奈川県': '関東', '新潟県': '中部', '富山県': '中部', '石川県': '中部', '福井県': '中部', '山梨県': '中部', '長野県': '中部', '岐阜県': '中部', '静岡県': '中部', '愛知県': '中部', '三重県': '近畿', '滋賀県': '近畿', '京都府': '近畿', '大阪府': '近畿', '兵庫県': '近畿', '奈良県': '近畿', '和歌山県': '近畿', '鳥取県': '中国・四国', '島根県': '中国・四国', '岡山県': '中国・四国', '広島県': '中国・四国', '山口県': '中国・四国', '徳島県': '中国・四国', '香川県': '中国・四国', '愛媛県': '中国・四国', '高知県': '中国・四国', '福岡県': '九州・沖縄', '佐賀県': '九州・沖縄', '長崎県': '九州・沖縄', '熊本県': '九州・沖縄', '大分県': '九州・沖縄', '宮崎県': '九州・沖縄', '鹿児島県': '九州・沖縄', '沖縄県': '九州・沖縄', } df_b['地域'] = df_b['都道府県'].map(region_map) region_colors = { '北海道・東北': '#4e9af1', '関東': '#e05c5c', '中部': '#f0a500', '近畿': '#5cb85c', '中国・四国': '#9b59b6', '九州・沖縄': '#f39c12', } target_prefs = ['東京都', '大阪府', '愛知県', '秋田県', '島根県', '沖縄県'] fig, ax = plt.subplots(figsize=(10, 5)) for pref in target_prefs: grp = df_b[df_b['都道府県'] == pref] ax.plot(grp['年度'], grp['純移動率'], marker='o', markersize=4, label=pref) ax.axhline(0, color='black', linewidth=0.8, linestyle='--') ax.set_xlabel('年度', fontsize=12) ax.set_ylabel('純移動率(‰)', fontsize=12) ax.set_title('代表都道府県の純移動率推移(2012〜2023年)', fontsize=14, fontweight='bold') ax.legend(fontsize=9) ax.grid(alpha=0.3) plt.tight_layout() plt.savefig(os.path.join(FIG_DIR, '2022_U5_11_fig1_ts.png'), dpi=150, bbox_inches='tight') plt.close() print("Fig1 saved") |
Fig1 saved
fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。fig.savefig(..., bbox_inches='tight') — 余白を自動で詰めて保存。plt.close() でメモリ解放。df['A'] / df['B'] — pandasの列同士の四則演算は要素ごと(element-wise)。forループ不要なのが強み。2022年時点での47都道府県の純移動率を一覧する。地域間格差の「現在地」を確認する断面図である。
| 特性 | 代表的な都道府県 | 純移動率の傾向 |
|---|---|---|
| 三大都市圏 | 東京都・神奈川県・大阪府・愛知県 | 正(転入超過) |
| 政令市中心地 | 宮城県・広島県・福岡県 | 正〜ほぼゼロ |
| 東北・山陰 | 秋田県・青森県・島根県・鳥取県 | 負(転出超過) |
| 特殊ケース | 沖縄県(高出生率・観光雇用) | 正(転入超過) |
73 74 75 76 77 78 79 80 81 82 83 84 85 | df_2022 = df_b[df_b['年度'] == 2022].copy().dropna(subset=['純移動率']) df_sorted = df_2022.sort_values('純移動率', ascending=True) fig, ax = plt.subplots(figsize=(8, 12)) bar_colors = ['#e05c5c' if v > 0 else '#4e9af1' for v in df_sorted['純移動率']] ax.barh(df_sorted['都道府県'], df_sorted['純移動率'], color=bar_colors, alpha=0.8) ax.axvline(0, color='black', linewidth=1) ax.set_xlabel('純移動率(‰)', fontsize=12) ax.set_title('都道府県別 純移動率ランキング(2022年)\n(赤=流入超過, 青=流出超過)', fontsize=13, fontweight='bold') ax.grid(axis='x', alpha=0.3) plt.tight_layout() plt.savefig(os.path.join(FIG_DIR, '2022_U5_11_fig2_rank.png'), dpi=150, bbox_inches='tight') plt.close() print("Fig2 saved") |
Fig2 saved
fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。sort_values('列名', ascending=False) — 指定列で並べ替え(降順)。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。fig.savefig(..., bbox_inches='tight') — 余白を自動で詰めて保存。plt.close() でメモリ解放。df['A'] / df['B'] — pandasの列同士の四則演算は要素ごと(element-wise)。forループ不要なのが強み。純移動率の決定要因を推定するために、固定効果パネルOLS(Entity Fixed Effects)を適用する。固定効果モデルは「都道府県ごとの観察できない恒常的な特性(文化・地理・歴史等)」を除去した上で、変数間の純粋な関係を推定できる。
固定効果と変量効果のどちらを使うべきかは、Hausman検定で判断する。帰無仮説「ランダム効果が一致推定量」が棄却された場合、固定効果モデルを採用する。本分析では都道府県固有の不観察要因(歴史・地理・文化)が説明変数と相関している可能性が高いため、固定効果モデルが適切である。
OLSは都道府県間の差(例:東京と秋田の絶対的な差)も係数に取り込んでしまう。固定効果モデルは各都道府県の「平均からのかい離」だけを分析するため、歴史・地理・文化などの観察できない違いに惑わされにくい。
都道府県パネルでは、同一都道府県内の誤差が年度をまたいで相関(系列相関)する。この無視は標準誤差の過小推計を招き、見かけ上の有意性が過大評価される。都道府県クラスターで標準誤差を補正することで頑健な推論が可能になる。
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 | panel_vars = ['求人倍率', '消費支出_log', '住宅地価_log', '高齢化率', '保育所密度'] df_panel = df_b[['年度', '都道府県', '純移動率'] + panel_vars].dropna() df_panel = df_panel.set_index(['都道府県', '年度']) y = df_panel['純移動率'] X = sm.add_constant(df_panel[panel_vars]) try: mod = PanelOLS(y, X, entity_effects=True, drop_absorbed=True) res = mod.fit(cov_type='clustered', cluster_entity=True) coefs = res.params.drop('const', errors='ignore') ses = res.std_errors.drop('const', errors='ignore') pvals = res.pvalues.drop('const', errors='ignore') except Exception as e: print(f"FE error: {e}") df_reg = df_b[df_b['年度'] == 2022][['純移動率'] + panel_vars].dropna() res_ols = sm.OLS(df_reg['純移動率'], sm.add_constant(df_reg[panel_vars])).fit() coefs = res_ols.params.drop('const') ses = res_ols.bse.drop('const') pvals = res_ols.pvalues.drop('const') fig, ax = plt.subplots(figsize=(8, 5)) colors_c = ['#e05c5c' if p < 0.05 else '#888888' for p in pvals] ax.barh(range(len(coefs)), coefs, xerr=1.96 * ses, color=colors_c, alpha=0.8, error_kw={'elinewidth': 1.5, 'capsize': 4}) ax.set_yticks(range(len(coefs))) ax.set_yticklabels(coefs.index, fontsize=10) ax.axvline(0, color='black', linewidth=0.8) ax.set_xlabel('回帰係数', fontsize=12) ax.set_title('純移動率の決定要因 — FEパネルOLS係数\n(赤=p<0.05, 灰=非有意)', fontsize=12, fontweight='bold') ax.grid(axis='x', alpha=0.3) plt.tight_layout() plt.savefig(os.path.join(FIG_DIR, '2022_U5_11_fig3_fe_coef.png'), dpi=150, bbox_inches='tight') plt.close() print("Fig3 saved") |
Fig3 saved
fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。sm.add_constant(X) — 切片項(定数1の列)を先頭に追加。statsmodelsで必須。sm.OLS(y, X).fit() — 最小二乗法でモデルを推定。model.params, model.pvalues, model.conf_int() で結果取得。fig.savefig(..., bbox_inches='tight') — 余白を自動で詰めて保存。plt.close() でメモリ解放。.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。2022年断面の散布図で「消費支出(所得水準の代理)」と「純移動率」の相関を可視化する。地域ブロック別に色分けすることで、地域ごとのクラスタリングパターンも把握できる。
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | df_sc = df_b[df_b['年度'] == 2022][['都道府県', '純移動率', '消費支出_log', '地域']].dropna() fig, ax = plt.subplots(figsize=(9, 6)) for reg, grp in df_sc.groupby('地域'): ax.scatter(grp['消費支出_log'], grp['純移動率'], color=region_colors.get(reg, 'gray'), label=reg, s=60, alpha=0.8) for _, row in df_sc.iterrows(): ax.annotate(row['都道府県'][:2], (row['消費支出_log'], row['純移動率']), fontsize=6, alpha=0.6, xytext=(2, 2), textcoords='offset points') x_vals = df_sc['消費支出_log'] y_vals = df_sc['純移動率'] slope, intercept, r, p, _ = stats.linregress(x_vals, y_vals) xr = np.linspace(x_vals.min(), x_vals.max(), 100) ax.plot(xr, slope * xr + intercept, 'k--', linewidth=1.5, label=f'回帰直線 (r={r:.2f}, p={p:.3f})') ax.axhline(0, color='gray', linewidth=0.8, linestyle=':') ax.set_xlabel('消費支出(log)', fontsize=12) ax.set_ylabel('純移動率(‰)', fontsize=12) ax.set_title('消費支出 vs 純移動率(2022年, N=47)', fontsize=13, fontweight='bold') ax.legend(fontsize=8, ncol=2) ax.grid(alpha=0.3) plt.tight_layout() plt.savefig(os.path.join(FIG_DIR, '2022_U5_11_fig4_scatter.png'), dpi=150, bbox_inches='tight') plt.close() print("Fig4 saved") print("All done!") |
Fig4 saved All done!
df.groupby('列').apply(関数) — グループごとに関数を適用。時系列や地域別の集計でよく使います。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。stats.linregress(x, y) — 単回帰の傾き・切片・r値・p値・標準誤差を返します。使わない値は _ で受け取り。for _, row in df.iterrows() — DataFrameを1行ずつ取り出すループ。1点ずつ描画したいときに使用。fig.savefig(..., bbox_inches='tight') — 余白を自動で詰めて保存。plt.close() でメモリ解放。[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。SSDSE-B(47都道府県 × 12年)の固定効果パネルOLS分析から、以下が示唆される。
固定効果モデルが示すのは「地域内の時系列変動」に基づく関係だが、散布図や時系列グラフは「地域間の断面格差」が固定化・拡大していることを示す。この「格差の固定化」は単なる収束現象ではなく、正のフィードバックループ(集積経済)が働く可能性を示唆する。
| データ・資料 | 出典 |
|---|---|
| SSDSE-B-2026.csv(都道府県パネルデータ) | 統計数理研究所 SSDSE(社会・人口統計体系) |
| 転入者数・転出者数(日本人移動者) | 総務省 住民基本台帳人口移動報告 |
| 月間有効求人数・求職者数(一般) | 厚生労働省 職業安定業務統計 |
| 消費支出(二人以上の世帯) | 総務省 家計調査 |
| 標準価格(住宅地平均価格) | 国土交通省 地価公示 |
| 保育所等数 | 厚生労働省 保育所等関連状況取りまとめ |
本教育用コードはSSDSE-B-2026.csv(実公的統計データ)のみを使用。合成データは一切使用していない。
統計分析の解釈で初心者がやりがちな勘違いをまとめます。特に「相関と因果の混同」「p値の過信」は研究現場でもよく起きる落とし穴です。本文を読む前にも、読んだ後にも、目を通してみてください。
統計の基本用語を初心者向けに解説します。本文中で見慣れない言葉が出てきたら、ここに戻って確認してください。
統計手法について「何のためか」「結果をどう読むか」を初心者向けに解説します。
この研究をさらに発展させるための3つの方向性を示します。「今回わかったこと(X)」から「次に検証すべき仮説(Y)」を立て、「具体的に何をするか(Z)」まで考えてみましょう。
学んだだけでは身につきません。実際に手を動かすのが最強の学習方法です。本論文のスクリプトをベースに、以下のチャレンジに挑戦してみてください。難易度別に5つ用意しました。
本論文で学んだ手法は、研究の世界だけでなく、行政・企業・NPO の現場でも様々に活用されています。具体的なシーンを紹介します。
この論文を読んで初心者が抱きやすい疑問に、教育的観点から答えます。