このページの分析を自分で再現するには、以下の手順でデータを準備してください。コードの編集は不要です。
data/raw/ フォルダに入れます。html/figures/ に自動保存されます。
日本の人口移動は長年にわたり「東京一極集中」の構造を維持してきた。若者は就職を機に地方を離れ、東京・神奈川・埼玉・千葉の首都圏に集積する。しかし2020年以降、COVID-19パンデミックによるテレワーク普及が人口移動のパターンを変えつつある。
まず「地方移住と地域活性化転入増加要因の分析」を統計的にとらえることが有効だと考えられる。 その理由は感覚や経験則だけでは、複雑な社会要因の中で「何が本当に効いているか」を見極めにくいからである。 本研究では公開データと統計手法を組み合わせ、この問いに定量的な答えを出すことを目指す。
本研究は SSDSE-B(都道府県別統計データ)を用い、どのような地域特性が転入者数の増加と関連しているのかを多角的に分析した。SSDSE-B の12年分(2012〜2023年)のパネルデータを活用し、固定効果モデルで地域固有の特性を除去した上で、雇用・住宅・保育・人口構造の各要因を検証している。
SSDSE-B 都道府県 相関分析 PanelOLS 固定効果 時系列分析 COVID前後比較
SSDSE-B-2026(統計数理研究所)は都道府県別の主要統計を年度別に収録したパネルデータセットである。本分析では2012〜2023年の12年間、47都道府県、計564観測値を使用する。
人口移動の分析では、転入者の絶対数でなく人口規模で割った「率」を用いることで都道府県間の比較が可能になる。
| 変数名 | SSDSE-B 列 | 単位 | 想定する効果の方向 |
|---|---|---|---|
| 転入率(目的変数) | A5101 / A1101 | ‰ | — |
| 純移動率(参考) | (A5101-A5102) / A1101 | ‰ | — |
| 求人倍率 | F3103 / F3102 | 倍 | 正(雇用機会の多さ) |
| 住宅地価_log | log(C5401) | — | 負(居住コスト)or 正(経済活力) |
| 保育所密度 | J2503 / A1101 | 所/万人 | 正(子育て環境) |
| 高齢化率 | A1303 / A1101 | % | 負(若者が少ない地域) |
| 消費支出_log | log(L3221) | — | 正(生活水準の高さ) |
「転入率」と「純移動率」は似ているが意味が異なる。転入率は「どれだけ人が来るか」、純移動率は「差し引きで人口が増えるか」を表す。地域活性化の評価には純移動率が有用だが、要因分析には転入率が適している(転出は別のメカニズムで決まる可能性があるため)。
まず都道府県を地域ブロックに分類し、2012〜2023年の純移動率の平均推移を確認する。
| 地域 | 2019年 純移動率(推計) | 2021年 純移動率(推計) | 2022年 純移動率 | トレンド |
|---|---|---|---|---|
| 東京都 | +6.2‰ | ▼ 急低下 | +2.4‰ | 回復傾向 |
| 埼玉・千葉・神奈川 | +2〜3‰ | 維持・やや増 | +2.4〜3.0‰ | 堅調 |
| 東北(平均) | −3〜−5‰ | やや改善 | −3〜−4‰ | 依然マイナス |
| 九州・沖縄(平均) | −1〜−2‰ | わずかに改善 | −1〜−2‰ | 緩やかにマイナス |
1 2 3 4 5 6 7 8 9 10 | import os import warnings import numpy as np import pandas as pd import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt import matplotlib.font_manager as fm warnings.filterwarnings('ignore') |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。import pandas as pd など — 必要なライブラリをまとめて呼び出します。as pd は短い別名(alias)。matplotlib.use('Agg') — グラフを画面表示せずファイルに保存するためのおまじない。f"...{x}..." はf-string。文字列の中に {変数} と書くだけで埋め込めて、{x:.2f} のように書式も指定できます。11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | # ── パス設定 ───────────────────────────────────────────────────────────────── FIG_DIR = 'html/figures' DATA_B = 'data/raw/SSDSE-B-2026.csv' os.makedirs(FIG_DIR, exist_ok=True) # ── フォント設定 ────────────────────────────────────────────────────────────── def get_japanese_font(): candidates = ['Hiragino Sans', 'Hiragino Kaku Gothic ProN', 'AppleGothic', 'Noto Sans CJK JP', 'IPAexGothic'] available = [f.name for f in fm.fontManager.ttflist] for c in candidates: if c in available: return c return 'sans-serif' FONT = get_japanese_font() plt.rcParams['font.family'] = FONT plt.rcParams['axes.unicode_minus'] = False |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。plt.rcParams['font.family'] — グラフの日本語表示用フォント指定(Macは Hiragino Sans、Windowsなら Yu Gothic 等)。os.makedirs('html/figures', exist_ok=True) — 図の保存先フォルダを作る(既にあってもOK)。df['A'] / df['B'] — pandasの列同士の四則演算は要素ごと(element-wise)。forループ不要なのが強み。29 30 31 32 33 34 35 | # ── データ読み込み ──────────────────────────────────────────────────────────── df = pd.read_csv(DATA_B, encoding='cp932', header=0, skiprows=[1]) df.rename(columns={'SSDSE-B-2026': 'year'}, inplace=True) # 都道府県のみ(県コード R + 5桁 = 6文字、末尾000) df = df[df['Code'].str.startswith('R') & df['Code'].str.endswith('000')].copy() df['year'] = df['year'].astype(int) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。pd.read_csv(...) でCSVを読み込みます。encoding='cp932' は日本語Windows由来の文字コード、header=1 は「2行目を列名として使う」。.astype(int) — 列を整数に変換(年度などを数値比較するため)。.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。36 37 38 39 40 41 42 43 | # ── 変数構築 ────────────────────────────────────────────────────────────────── df['転入率'] = df['A5101'] / df['A1101'] * 1000 df['純移動率'] = (df['A5101'] - df['A5102']) / df['A1101'] * 1000 df['求人倍率'] = df['F3103'] / df['F3102'] # 月間有効求人 / 求職 df['住宅地価_log'] = np.log(df['C5401'].replace(0, np.nan)) df['保育所密度'] = df['J2503'] / df['A1101'] * 10000 df['高齢化率'] = df['A1303'] / df['A1101'] * 100 df['消費支出_log'] = np.log(df['L3221'].replace(0, np.nan)) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。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 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | # ── 地域グループ定義 ────────────────────────────────────────────────────────── tokyo_area = ['東京都', '神奈川県', '埼玉県', '千葉県'] kinki_area = ['大阪府', '京都府', '兵庫県', '奈良県', '滋賀県', '和歌山県'] tohoku = ['青森県', '岩手県', '宮城県', '秋田県', '山形県', '福島県'] kyushu = ['福岡県', '佐賀県', '長崎県', '熊本県', '大分県', '宮崎県', '鹿児島県', '沖縄県'] def region_label(pref): if pref in tokyo_area: return '東京圏' elif pref in kinki_area: return '近畿圏' elif pref in tohoku: return '東北' elif pref in kyushu: return '九州・沖縄' else: return 'その他' df['region'] = df['Prefecture'].apply(region_label) REGION_COLORS = { '東京圏': '#E53935', '近畿圏': '#1E88E5', '東北': '#43A047', '九州・沖縄': '#FB8C00', 'その他': '#90A4AE', } print(f"データ行数: {len(df)}, 年度: {sorted(df['year'].unique())}") print(f"都道府県数(2022年): {len(df[df['year']==2022])}") fig1, ax1 = plt.subplots(figsize=(10, 6), dpi=150) region_ts = df.groupby(['year', 'region'])['純移動率'].mean().reset_index() years_all = sorted(df['year'].unique()) for region, color in REGION_COLORS.items(): sub = region_ts[region_ts['region'] == region].sort_values('year') lw = 2.8 if region == '東京圏' else 1.8 ls = '-' if region in ['東京圏', '近畿圏', '東北'] else '--' ax1.plot(sub['year'], sub['純移動率'], color=color, lw=lw, ls=ls, marker='o', markersize=4, label=region) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。df.groupby('列').apply(関数) — グループごとに関数を適用。時系列や地域別の集計でよく使います。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。sort_values('列名', ascending=False) — 指定列で並べ替え(降順)。r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | # COVID前後の境界線 ax1.axvline(2019.5, color='gray', lw=1.2, ls=':', alpha=0.7) ax1.axvline(2020.5, color='gray', lw=1.2, ls=':', alpha=0.7) ax1.axhline(0, color='black', lw=0.8, alpha=0.5) ax1.annotate('COVID-19\n感染拡大', xy=(2020, ax1.get_ylim()[1] if ax1.get_ylim()[1] > 0 else 2), xytext=(2020.3, 3.5), fontsize=9, color='gray', ha='left') ax1.set_title('純移動率の時系列推移(地域別平均)\n東京圏の一人勝ちとCOVID後の変化', fontsize=13, fontweight='bold', pad=12) ax1.set_xlabel('年度', fontsize=11) ax1.set_ylabel('純移動率(‰)', fontsize=11) ax1.legend(loc='upper left', fontsize=10, framealpha=0.9) ax1.grid(axis='y', alpha=0.3) ax1.set_xticks(years_all) ax1.tick_params(axis='x', rotation=45) plt.tight_layout() fig1_path = os.path.join(FIG_DIR, '2022_H5_13_fig1_timeseries.png') fig1.savefig(fig1_path, dpi=150, bbox_inches='tight') plt.close(fig1) print(f"Fig1 saved: {fig1_path}") |
データ行数: 564, 年度: [np.int64(2012), np.int64(2013), np.int64(2014), np.int64(2015), np.int64(2016), np.int64(2017), np.int64(2018), np.int64(2019), np.int64(2020), np.int64(2021), np.int64(2022), np.int64(2023)] 都道府県数(2022年): 47 Fig1 saved: html/figures/2022_H5_13_fig1_timeseries.png
ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。x if cond else y は三項演算子。リスト内包表記と組み合わせると、forとifを1行で書けます。2022年の47都道府県を対象に、求人倍率(月間有効求人数 / 求職者数)と転入率の関係を散布図で確認する。
| 都道府県 | 求人倍率 | 転入率(‰) | 純移動率(‰) |
|---|---|---|---|
| 東京都 | 1.99 | 28.4 | +2.4 |
| 神奈川県 | 1.12 | 23.3 | +2.4 |
| 千葉県 | 1.21 | 23.1 | +3.0 |
| 埼玉県 | 1.07 | 22.4 | +2.6 |
| 秋田県 | 1.27 | 9.6 | −6.4 |
| 青森県 | 1.13 | 10.3 | −5.1 |
求人倍率の出典:SSDSE-B 月間有効求人数(F3103)/ 月間有効求職者数(F3102)
109 110 111 112 113 114 115 116 | df22 = df[df['year'] == 2022].dropna(subset=['求人倍率', '転入率']).copy() fig2, ax2 = plt.subplots(figsize=(10, 7), dpi=150) for region, color in REGION_COLORS.items(): sub = df22[df22['region'] == region] ax2.scatter(sub['求人倍率'], sub['転入率'], color=color, s=60, alpha=0.8, label=region, zorder=3) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。df['A'] / df['B'] — pandasの列同士の四則演算は要素ごと(element-wise)。forループ不要なのが強み。117 118 119 120 121 122 123 124 125 126 | # 都道府県ラベル(省略なし — ただし重複を避けるため主要県のみ表示) label_prefs = ['東京都', '大阪府', '愛知県', '沖縄県', '秋田県', '青森県', '福岡県', '神奈川県', '埼玉県', '京都府', '宮城県', '北海道'] for _, row in df22.iterrows(): pref = row['Prefecture'] if pref in label_prefs: ax2.annotate(pref.replace('都','').replace('道','').replace('府','').replace('県',''), xy=(row['求人倍率'], row['転入率']), xytext=(4, 4), textcoords='offset points', fontsize=8, color='#333', zorder=4) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。for _, row in df.iterrows() — DataFrameを1行ずつ取り出すループ。1点ずつ描画したいときに使用。.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | # 回帰直線 from numpy.polynomial.polynomial import polyfit as npfit mask = df22[['求人倍率', '転入率']].notna().all(axis=1) x_v = df22.loc[mask, '求人倍率'].values y_v = df22.loc[mask, '転入率'].values coef = np.polyfit(x_v, y_v, 1) xline = np.linspace(x_v.min(), x_v.max(), 100) ax2.plot(xline, np.polyval(coef, xline), 'k--', lw=1.5, alpha=0.6, label='回帰直線') from scipy import stats as spstats r, p = spstats.pearsonr(x_v, y_v) ax2.text(0.97, 0.05, f'r = {r:.3f} (p = {p:.3f})', transform=ax2.transAxes, ha='right', va='bottom', fontsize=10, bbox=dict(boxstyle='round', facecolor='#EEE', alpha=0.8)) ax2.set_title('求人倍率 vs 転入率(2022年・47都道府県)', fontsize=13, fontweight='bold', pad=12) ax2.set_xlabel('求人倍率(月間有効求人 / 求職者数)', fontsize=11) ax2.set_ylabel('転入率(転入者数 / 総人口 × 1000)', fontsize=11) ax2.legend(loc='upper left', fontsize=9, framealpha=0.9) ax2.grid(alpha=0.3) plt.tight_layout() fig2_path = os.path.join(FIG_DIR, '2022_H5_13_fig2_scatter.png') fig2.savefig(fig2_path, dpi=150, bbox_inches='tight') plt.close(fig2) print(f"Fig2 saved: {fig2_path}") |
Fig2 saved: html/figures/2022_H5_13_fig2_scatter.png
import pandas as pd など — 必要なライブラリをまとめて呼び出します。as pd は短い別名(alias)。stats.pearsonr(x, y) — Pearson相関係数 r と p値を同時に返します。[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。時系列のある都道府県データを「パネルデータ」として扱い、各都道府県の観測できない固有要因(立地・気候・歴史的経緯など)を「固定効果」として制御した上で、説明変数の純効果を推定する。
ここで μᵢ は都道府県 i の固定効果(時間不変の地域特性を全て吸収する)。この項のおかげで「東京は元々転入が多い」という見えない要因を除去できる。
PanelOLS(エンティティ固定効果、クラスター標準誤差)
N = 564(47都道府県 × 12年)
R-squared (Within): 0.087
変数 係数(生) t統計量 p値
求人倍率 +0.128 0.673 0.501
住宅地価_log −0.894 −0.784 0.433
保育所密度 +0.031 0.253 0.800
高齢化率 −0.083 −2.494 0.013 *
消費支出_log +0.854 1.417 0.157
| 変数 | 標準化係数 β | p値 | 解釈 |
|---|---|---|---|
| 住宅地価_log | +2.336 | 0.000 *** | 地価が高い都市圏ほど転入率が高い(OLS) |
| 高齢化率 | −0.884 | 0.000 *** | 高齢化が進む地域は転入が少ない(OLS) |
| 保育所密度 | +0.553 | 0.000 *** | 保育所が多い地域は転入が多い(OLS) |
| 消費支出_log | +0.478 | 0.000 *** | 生活水準の高い地域は転入が多い(OLS) |
| 求人倍率 | −0.156 | 0.219 | 有意でない(交絡の可能性) |
上表はOLS(プーリング)の標準化係数。固定効果除去後は高齢化率のみ有意(詳細は図3)。
パネルデータでは「観測できない個体固有の要因」(例:東京の利便性、秋田の気候)が結果変数と説明変数の両方に影響しているケースが多い(交絡)。固定効果モデルはこの問題を「各個体内の時間変動」のみを使って係数を推定することで解決する。
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 186 187 188 189 190 191 192 193 194 195 196 197 198 | panel_vars = ['転入率', '求人倍率', '住宅地価_log', '保育所密度', '高齢化率', '消費支出_log'] df_panel = df[['year', 'Prefecture'] + panel_vars].dropna().copy() var_labels = { '求人倍率': '求人倍率', '住宅地価_log': '住宅地価(log)', '保育所密度': '保育所密度\n(人口1万対)', '高齢化率': '高齢化率', '消費支出_log': '消費支出(log)', } exog_vars = list(var_labels.keys()) try: from linearmodels.panel import PanelOLS import statsmodels.api as sm df_panel = df_panel.set_index(['Prefecture', 'year']) endog = df_panel['転入率'] exog = sm.add_constant(df_panel[exog_vars]) model = PanelOLS(endog, exog, entity_effects=True, time_effects=False, drop_absorbed=True) result = model.fit(cov_type='clustered', cluster_entity=True) print(result.summary) coefs = result.params.drop('const', errors='ignore') se = result.std_errors.drop('const', errors='ignore') pvals = result.pvalues.drop('const', errors='ignore') ci_low = coefs - 1.96 * se ci_hi = coefs + 1.96 * se method_label = 'PanelOLS 固定効果(クラスター標準誤差)' except Exception as e: print(f"PanelOLS failed ({e}), falling back to OLS") import statsmodels.api as sm df_ols = df[panel_vars].dropna().copy() X = sm.add_constant(df_ols[exog_vars]) res_ols = sm.OLS(df_ols['転入率'], X).fit() coefs = res_ols.params.drop('const', errors='ignore') se = res_ols.bse.drop('const', errors='ignore') pvals = res_ols.pvalues.drop('const', errors='ignore') ci_low = coefs - 1.96 * se ci_hi = coefs + 1.96 * se method_label = 'OLS(固定効果なし)' |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。import pandas as pd など — 必要なライブラリをまとめて呼び出します。as pd は短い別名(alias)。sm.add_constant(X) — 切片項(定数1の列)を先頭に追加。statsmodelsで必須。sm.OLS(y, X).fit() — 最小二乗法でモデルを推定。model.params, model.pvalues, model.conf_int() で結果取得。.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 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 | # 標準化係数でなく生係数を表示(スケール差があるため正規化して可視化) # 視覚的比較のため z-score 標準化 df_std = df[panel_vars].dropna().copy() for v in exog_vars: df_std[v] = (df_std[v] - df_std[v].mean()) / df_std[v].std() import statsmodels.api as sm_std X_std = sm_std.add_constant(df_std[exog_vars]) res_std = sm_std.OLS(df_std['転入率'], X_std).fit() coefs_std = res_std.params.drop('const') se_std = res_std.bse.drop('const') pvals_std = res_std.pvalues.drop('const') ci_low_std = coefs_std - 1.96 * se_std ci_hi_std = coefs_std + 1.96 * se_std fig3, ax3 = plt.subplots(figsize=(8, 5), dpi=150) y_pos = np.arange(len(exog_vars)) colors_bar = ['#E53935' if p < 0.05 else '#90A4AE' for p in pvals_std] ax3.barh(y_pos, coefs_std.values, xerr=se_std.values, color=colors_bar, alpha=0.85, height=0.6, capsize=4) ax3.axvline(0, color='black', lw=1.0) ax3.set_yticks(y_pos) ax3.set_yticklabels([var_labels[v] for v in exog_vars], fontsize=10) ax3.set_xlabel('標準化回帰係数(β)\n赤:p < 0.05', fontsize=10) ax3.set_title(f'転入率の決定要因\n({method_label})', fontsize=12, fontweight='bold', pad=10) for i, (c, p, cl, ch) in enumerate(zip(coefs_std.values, pvals_std.values, ci_low_std.values, ci_hi_std.values)): sig = '***' if p < 0.001 else ('**' if p < 0.01 else ('*' if p < 0.05 else '')) label = f'β={c:.3f}{sig}' x_off = 0.01 if c >= 0 else -0.01 ha = 'left' if c >= 0 else 'right' ax3.text(c + x_off, i, label, va='center', ha=ha, fontsize=8.5) ax3.grid(axis='x', alpha=0.3) ax3.invert_yaxis() plt.tight_layout() fig3_path = os.path.join(FIG_DIR, '2022_H5_13_fig3_panel.png') fig3.savefig(fig3_path, dpi=150, bbox_inches='tight') plt.close(fig3) print(f"Fig3 saved: {fig3_path}") |
PanelOLS Estimation Summary
================================================================================
Dep. Variable: 転入率 R-squared: 0.0873
Estimator: PanelOLS R-squared (Between): -0.1489
No. Observations: 564 R-squared (Within): 0.0873
Date: Mon, May 18 2026 R-squared (Overall): -0.1451
Time: 11:24:05 Log-likelihood -366.47
Cov. Estimator: Clustered
F-statistic: 9.7949
Entities: 47 P-value 0.0000
Avg Obs: 12.000 Distribution: F(5,512)
Min Obs: 12.000
Max Obs: 12.000 F-statistic (robust): 1.8824
P-value 0.0958
Time periods: 12 Distribution: F(5,512)
Avg Obs: 47.000
Min Obs: 47.000
Max Obs: 47.000
Parameter Estimates
==============================================================================
Parameter Std. Err. T-stat P-value Lower CI Upper CI
------------------------------------------------------------------------------
const 16.444 12.091 1.3600 0.1744 -7.3110 40.198
求人倍率 0.1280 0.1902 0.6728 0.5014 -0.2458 0.5018
住宅地価_log -0.8936 1.1400 -0.7839 0.4335 -3.1333 1.3460
保育所密度 0.0309 0.1219 0.2532 0.8002 -0.2087 0.2705
高齢化率 -0.0828 0.0332 -2.4937 0.0130 -0.1481 -0.0176
消費支出_log 0.8538 0.6027 1.4167 0.1572 -0.3302 2.0377
==============================================================================
F-test for Poolability: 327.33
P-value: 0.0000
Distribution: F(46,512)
Included effects: Entity
Fig3 saved: html/figures/2022_H5_13_fig3_panel.pngimport pandas as pd など — 必要なライブラリをまとめて呼び出します。as pd は短い別名(alias)。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。COVID-19パンデミック(2020年〜)が人口移動に与えた影響を定量的に評価する。2019年(コロナ前)と2022年(コロナ後、行動制限緩和後)の純移動率を比較し、変化が大きかった都道府県をランキングで示す。
| 都道府県 | 変化(‰) |
|---|---|
| 山梨県 | +2.87 |
| 熊本県 | +2.05 |
| 長野県 | +2.03 |
| 茨城県 | +1.98 |
| 宮城県 | +1.76 |
| 鹿児島県 | +1.62 |
| 奈良県 | +1.58 |
| 和歌山県 | +1.55 |
| 高知県 | +1.50 |
| 宮崎県 | +1.48 |
| 都道府県 | 変化(‰) |
|---|---|
| 東京都 | −3.77 |
| 沖縄県 | −1.04 |
| 愛知県 | −0.82 |
| 大阪府 | −0.37 |
| 広島県 | −0.30 |
| 香川県 | −0.23 |
| 岡山県 | −0.16 |
| 神奈川県 | −0.15 |
| 京都府 | ±0.02 |
| 福島県 | ±0.02 |
「COVID後に地方移住が増えた」という主張を検証するには、①単年ではなく前後の比較をすること、②「転入の増加」と「転出の減少」を区別すること、③一時的変動と構造変化を見分けることが重要。時系列データを適切に分析することで、センセーショナルな報道と実際のデータを峻別できる。
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 | df19 = df[df['year'] == 2019][['Prefecture', '純移動率']].set_index('Prefecture') df22b = df[df['year'] == 2022][['Prefecture', '純移動率']].set_index('Prefecture') change = (df22b['純移動率'] - df19['純移動率']).dropna().sort_values(ascending=False) change.name = '純移動率変化(2022−2019)' # 上位10・下位10 top10 = change.head(10) bottom10 = change.tail(10) plot_data = pd.concat([top10, bottom10]).sort_values(ascending=True) fig4, ax4 = plt.subplots(figsize=(9, 8), dpi=150) bar_colors = ['#E53935' if v > 0 else '#1E88E5' for v in plot_data.values] bars = ax4.barh(plot_data.index, plot_data.values, color=bar_colors, alpha=0.85, height=0.7) ax4.axvline(0, color='black', lw=1.0) for bar, val in zip(bars, plot_data.values): x_off = 0.02 if val >= 0 else -0.02 ha = 'left' if val >= 0 else 'right' ax4.text(val + x_off, bar.get_y() + bar.get_height()/2, f'{val:+.2f}‰', va='center', ha=ha, fontsize=8.5) ax4.set_xlabel('純移動率の変化(2022年 − 2019年) [‰]', fontsize=10) ax4.set_title('COVID前後の純移動率変化\n上位10都道府県(赤)・下位10都道府県(青)', fontsize=12, fontweight='bold', pad=10) ax4.grid(axis='x', alpha=0.3) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。sort_values('列名', ascending=False) — 指定列で並べ替え(降順)。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 | # 注釈 ax4.text(0.98, 0.98, '赤:流入増加\n青:流入減少 / 流出増加', transform=ax4.transAxes, ha='right', va='top', fontsize=9, bbox=dict(boxstyle='round', facecolor='#FFF9C4', alpha=0.9)) plt.tight_layout() fig4_path = os.path.join(FIG_DIR, '2022_H5_13_fig4_covid.png') fig4.savefig(fig4_path, dpi=150, bbox_inches='tight') plt.close(fig4) print(f"Fig4 saved: {fig4_path}") print("\n全図の生成が完了しました。") print(f" Fig1: {os.path.basename(fig1_path)}") print(f" Fig2: {os.path.basename(fig2_path)}") print(f" Fig3: {os.path.basename(fig3_path)}") print(f" Fig4: {os.path.basename(fig4_path)}") |
Fig4 saved: html/figures/2022_H5_13_fig4_covid.png 全図の生成が完了しました。 Fig1: 2022_H5_13_fig1_timeseries.png Fig2: 2022_H5_13_fig2_scatter.png Fig3: 2022_H5_13_fig3_panel.png Fig4: 2022_H5_13_fig4_covid.png
r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。統計分析が「政策の限界」を示すことがある。固定効果モデルで交絡を除去したら効果が消えた — これは「変数の効果がゼロ」を意味するのではなく、「その効果は地域の構造的特性と分離できない」ことを示している。政策評価には観察データだけでなく、自然実験・差の差分析(DiD)・回帰不連続デザイン(RDD)といった准実験的手法が有効。
| データ | 出典・列コード |
|---|---|
| 転入者数(日本人移動者) | SSDSE-B-2026: A5101 |
| 転出者数(日本人移動者) | SSDSE-B-2026: A5102 |
| 総人口 | SSDSE-B-2026: A1101 |
| 65歳以上人口 | SSDSE-B-2026: A1303 |
| 月間有効求人数・求職者数(一般) | SSDSE-B-2026: F3103, F3102 |
| 標準価格(平均価格)(住宅地) | SSDSE-B-2026: C5401 |
| 保育所等数 | SSDSE-B-2026: J2503 |
| 消費支出(二人以上の世帯) | SSDSE-B-2026: L3221 |
分析には実データ(SSDSE-B-2026.csv)のみを使用。合成データは一切使用していない。
統計分析の解釈で初心者がやりがちな勘違いをまとめます。特に「相関と因果の混同」「p値の過信」は研究現場でもよく起きる落とし穴です。本文を読む前にも、読んだ後にも、目を通してみてください。
統計の基本用語を初心者向けに解説します。本文中で見慣れない言葉が出てきたら、ここに戻って確認してください。
統計手法について「何のためか」「結果をどう読むか」を初心者向けに解説します。
この研究をさらに発展させるための3つの方向性を示します。「今回わかったこと(X)」から「次に検証すべき仮説(Y)」を立て、「具体的に何をするか(Z)」まで考えてみましょう。
学んだだけでは身につきません。実際に手を動かすのが最強の学習方法です。本論文のスクリプトをベースに、以下のチャレンジに挑戦してみてください。難易度別に5つ用意しました。
本論文で学んだ手法は、研究の世界だけでなく、行政・企業・NPO の現場でも様々に活用されています。具体的なシーンを紹介します。
この論文を読んで初心者が抱きやすい疑問に、教育的観点から答えます。