このページの分析を自分で再現するには、以下の手順でデータを準備してください。コードの編集は不要です。
data/raw/ フォルダに入れます。html/figures/ に自動保存されます。
2020年初頭に始まった新型コロナウイルス感染症(COVID-19)のパンデミックは、日本社会に様々な変化をもたらした。緊急事態宣言やテレワークの急速な普及により、人々の居住地選択に対する意識が変化し、長年続いてきた「東京一極集中」と呼ばれる人口流入構造に変化が生じた可能性がある。
まず「コロナ禍が人口移動に与えた影響転入・転出データのパネル分析」を統計的にとらえることが有効だと考えられる。 その理由は感覚や経験則だけでは、複雑な社会要因の中で「何が本当に効いているか」を見極めにくいからである。 本研究では公開データと統計手法を組み合わせ、この問いに定量的な答えを出すことを目指す。
2020年には東京都の純移動率(転入超過率)が前年比で大きく低下し、「コロナ禍が東京一極集中を緩和した」という議論が社会的な注目を集めた。本研究は、SSDSE(社会・人口統計体系データセット)-Bを用いて、47都道府県の2012〜2023年パネルデータを分析し、この仮説を統計的に検証する。
総務大臣賞 時系列分析 差の差分析 重回帰分析 パネルデータ
独立行政法人統計センターが提供するSSDSE(社会・人口統計体系データセット)-Bを使用する。SSDSE-Bは都道府県レベルの年次統計データを収録しており、人口・人口移動に関する項目が含まれている。
| データセット | 対象 | 期間 | 観測数 |
|---|---|---|---|
| SSDSE-B-2026 | 47都道府県(地域コード R\d{5}) | 2012〜2023年 | 564行(47×12年) |
人口移動の規模を都道府県間で比較するため、人口1000人あたりの移動率(‰)に基準化する。
| 変数名 | 計算式 | 単位 | 説明 |
|---|---|---|---|
| 転入率 | 転入者数 ÷ 総人口 × 1000 | ‰ | 都道府県への転入の活発さ |
| 転出率 | 転出者数 ÷ 総人口 × 1000 | ‰ | 都道府県からの転出の活発さ |
| 純移動率 | (転入者数 − 転出者数)÷ 総人口 × 1000 | ‰ | +は転入超過、−は転出超過 |
| COVIDダミー | 2020・2021年=1、それ以外=0 | − | COVID-19流行期の識別変数 |
COVID前の基準年(2019年)の純移動率に基づき、転入超過上位10都道府県を「都市型」、下位10都道府県を「地方型」として分類した。
東京都(人口約1400万人)と鳥取県(人口約55万人)の転入者数を直接比較することはできない。人口規模の違いを調整するため、人口1000人あたりの「率(‰)」に変換する。これを「基準化(normalization)」または「人口千対」表示と呼ぶ。
地域ブロック別に純移動率を集計し、2012〜2023年の時系列変化を可視化した。黄色のシェーディングはCOVID-19流行期(2020〜2021年)を示す。
| 都道府県・地域 | 2019年 | 2020年 | 変化量 | 解釈 |
|---|---|---|---|---|
| 東京都 | 6.18‰ | 2.73‰ | −3.45‰ | 一極集中が大きく緩和 |
| 大阪府 | 1.21‰ | 1.51‰ | +0.31‰ | 若干の転入超過拡大 |
| 鳥取県 | (転出超過) | (転出超過) | +1.29‰ | 最大改善(地方移住の動き) |
| 全国平均 | −1.97‰ | −1.44‰ | +0.54‰ | 全体的に転出超過が緩和 |
2022年の純移動率ランキングでは、千葉県(2.99‰)、埼玉県(2.63‰)、神奈川県(2.44‰)が上位3位を占め、首都圏での転入超過が引き続き続いている。一方、長崎県(−4.03‰)、福島県(−3.72‰)、岩手県(−3.71‰)が転出超過上位となっており、地方の人口流出が続いていることがわかる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | import os import numpy as np import pandas as pd import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt 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 FIG_DIR = 'html/figures' 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} のように書式も指定できます。17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | # ── データ読み込み ───────────────────────────────────────────── 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) # ── 地域マップ ───────────────────────────────────────────────── region_map = { '北海道': '北海道・東北', '青森県': '北海道・東北', '岩手県': '北海道・東北', '宮城県': '北海道・東北', '秋田県': '北海道・東北', '山形県': '北海道・東北', '福島県': '北海道・東北', '茨城県': '関東', '栃木県': '関東', '群馬県': '関東', '埼玉県': '関東', '千葉県': '関東', '東京都': '関東', '神奈川県': '関東', '新潟県': '中部', '富山県': '中部', '石川県': '中部', '福井県': '中部', '山梨県': '中部', '長野県': '中部', '岐阜県': '中部', '静岡県': '中部', '愛知県': '中部', '三重県': '近畿', '滋賀県': '近畿', '京都府': '近畿', '大阪府': '近畿', '兵庫県': '近畿', '奈良県': '近畿', '和歌山県': '近畿', '鳥取県': '中国・四国', '島根県': '中国・四国', '岡山県': '中国・四国', '広島県': '中国・四国', '山口県': '中国・四国', '徳島県': '中国・四国', '香川県': '中国・四国', '愛媛県': '中国・四国', '高知県': '中国・四国', '福岡県': '九州・沖縄', '佐賀県': '九州・沖縄', '長崎県': '九州・沖縄', '熊本県': '九州・沖縄', '大分県': '九州・沖縄', '宮崎県': '九州・沖縄', '鹿児島県': '九州・沖縄', '沖縄県': '九州・沖縄' } region_colors = { '北海道・東北': '#4e9af1', '関東': '#e05c5c', '中部': '#f0a500', '近畿': '#5cb85c', '中国・四国': '#9b59b6', '九州・沖縄': '#f39c12' } |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。pd.read_csv(...) でCSVを読み込みます。encoding='cp932' は日本語Windows由来の文字コード、header=1 は「2行目を列名として使う」。df['地域コード'].str.match(r'^R\d{5}', ...) — 正規表現で「R+数字5桁」の行(47都道府県)だけTrueにし、真偽値で行をフィルタ。.astype(int) — 列を整数に変換(年度などを数値比較するため)。df['A'] / df['B'] — pandasの列同士の四則演算は要素ごと(element-wise)。forループ不要なのが強み。43 44 45 46 47 48 | # ── 変数作成 ────────────────────────────────────────────────── df_b['転入率'] = df_b['転入者数(日本人移動者)'] / df_b['総人口'] * 1000 df_b['転出率'] = df_b['転出者数(日本人移動者)'] / df_b['総人口'] * 1000 df_b['純移動率'] = (df_b['転入者数(日本人移動者)'] - df_b['転出者数(日本人移動者)']) / df_b['総人口'] * 1000 df_b['COVID'] = df_b['年度'].isin([2020, 2021]).astype(int) df_b['地域'] = df_b['都道府県'].map(region_map) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。.astype(int) — 列を整数に変換(年度などを数値比較するため)。.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。49 50 51 52 53 54 55 56 | # ── 都市型・地方型の分類(2019年純移動率の上位10・下位10)────── pivot_2019 = df_b[df_b['年度'] == 2019].set_index('都道府県')['純移動率'] top10_prefs = pivot_2019.nlargest(10).index.tolist() bot10_prefs = pivot_2019.nsmallest(10).index.tolist() df_b['都市型'] = df_b['都道府県'].apply( lambda x: '都市型(上位10)' if x in top10_prefs else ('地方型(下位10)' if x in bot10_prefs else 'その他') ) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。57 58 59 60 61 62 | # ── 統計値の計算(HTMLへの埋め込み用)──────────────────────── # 地域別平均純移動率の時系列 region_ts = df_b.groupby(['年度', '地域'])['純移動率'].mean().unstack() # 都道府県別2022年純移動率 nmr_2022 = df_b[df_b['年度'] == 2022].set_index('都道府県')['純移動率'].sort_values(ascending=False) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。df.groupby('列').apply(関数) — グループごとに関数を適用。時系列や地域別の集計でよく使います。sort_values('列名', ascending=False) — 指定列で並べ替え(降順)。r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | # 2019→2020の変化量 nmr_2019 = df_b[df_b['年度'] == 2019].set_index('都道府県')['純移動率'] nmr_2020 = df_b[df_b['年度'] == 2020].set_index('都道府県')['純移動率'] delta_2019_2020 = (nmr_2020 - nmr_2019).dropna() # 差の差分析(DiD) # 都市型vs地方型 × COVID前後 did_df = df_b[df_b['都市型'] != 'その他'].copy() did_df['post'] = (did_df['年度'] >= 2020).astype(int) did_df['treat'] = (did_df['都市型'] == '都市型(上位10)').astype(int) did_df['did'] = did_df['treat'] * did_df['post'] X_did = sm.add_constant(did_df[['treat', 'post', 'did']]) ols_did = sm.OLS(did_df['純移動率'], X_did).fit() print("=== 差の差分析 結果 ===") print(ols_did.summary()) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。.astype(int) — 列を整数に変換(年度などを数値比較するため)。sm.add_constant(X) — 切片項(定数1の列)を先頭に追加。statsmodelsで必須。sm.OLS(y, X).fit() — 最小二乗法でモデルを推定。model.params, model.pvalues, model.conf_int() で結果取得。x if cond else y は三項演算子。リスト内包表記と組み合わせると、forとifを1行で書けます。79 80 81 82 83 84 85 86 87 | # 重回帰分析(パネル固定効果なし簡易版) reg_df = df_b.copy() pref_dummies = pd.get_dummies(reg_df['都道府県'], drop_first=True, dtype=float) X_reg = sm.add_constant(pd.concat([reg_df[['COVID', '転入率']], pref_dummies], axis=1)) ols_reg = sm.OLS(reg_df['純移動率'], X_reg).fit() print("\n=== 重回帰分析(主要係数) ===") print(f"COVID係数: {ols_reg.params['COVID']:.4f}, p={ols_reg.pvalues['COVID']:.4f}") print(f"転入率係数: {ols_reg.params['転入率']:.4f}, p={ols_reg.pvalues['転入率']:.4f}") |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。sm.add_constant(X) — 切片項(定数1の列)を先頭に追加。statsmodelsで必須。sm.OLS(y, X).fit() — 最小二乗法でモデルを推定。model.params, model.pvalues, model.conf_int() で結果取得。df[col](1列)と df[[col1, col2]](複数列)でカッコの数が違います。リストを渡していると覚えるとミスを減らせます。88 89 90 91 92 93 94 95 | # 東京都の純移動率推移 tokyo_ts = df_b[df_b['都道府県'] == '東京都'].set_index('年度')['純移動率'] print(f"\n=== 東京都の純移動率 ===\n{tokyo_ts.to_string()}") # 2020年の全国平均純移動率と2019年の差 avg_2020 = df_b[df_b['年度'] == 2020]['純移動率'].mean() avg_2019 = df_b[df_b['年度'] == 2019]['純移動率'].mean() print(f"\n全国平均純移動率 2019: {avg_2019:.4f}, 2020: {avg_2020:.4f}, 差: {avg_2020-avg_2019:.4f}") |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。s[:-n]「末尾n文字を除く」/s[n:]「先頭n文字を除く」。スライス [start:stop:step] はリスト・タプル・文字列共通の基本ワザです。96 97 98 99 100 101 102 | # DiD係数 did_coef = ols_did.params['did'] did_pval = ols_did.pvalues['did'] treat_coef = ols_did.params['treat'] post_coef = ols_did.params['post'] print(f"\nDiD係数: {did_coef:.4f}, p={did_pval:.4f}") print(f"treat係数: {treat_coef:.4f}, post係数: {post_coef:.4f}") |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。np.cumsum(arr) は累積和、np.linspace(a, b, n) は「aからbを等間隔でn個」。NumPyの定石です。103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | # 2019基準の変化トップ・ワースト print(f"\n=== 2019→2020 純移動率変化量 Top5 ===") print(delta_2019_2020.nlargest(5).to_string()) print(f"\n=== 2019→2020 純移動率変化量 Bottom5 ===") print(delta_2019_2020.nsmallest(5).to_string()) # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ fig, ax = plt.subplots(figsize=(10, 5.5)) region_order = ['関東', '近畿', '中部', '北海道・東北', '中国・四国', '九州・沖縄'] line_styles = ['-', '--', '-.', ':', '-', '--'] markers = ['o', 's', '^', 'D', 'v', 'P'] for i, region in enumerate(region_order): if region in region_ts.columns: ax.plot(region_ts.index, region_ts[region], label=region, color=region_colors[region], linestyle=line_styles[i], marker=markers[i], markersize=5, linewidth=1.8) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。{値:.2f}(小数2桁)、{値:,}(3桁区切り)、{値:>10}(右寄せ10桁)など、覚えると出力が一気に整います。123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | # COVID期シェーディング ax.axvspan(2019.5, 2021.5, color='#ffd700', alpha=0.18, label='COVID-19流行期(2020–2021年)') ax.axhline(0, color='gray', linewidth=0.8, linestyle='--') ax.set_xlabel('年度', fontsize=12) ax.set_ylabel('純移動率(‰:人口1000人あたり)', fontsize=12) ax.set_title('図1 地域ブロック別 純移動率の推移(2012〜2023年)', fontsize=13, fontweight='bold', pad=12) ax.legend(loc='upper right', fontsize=10, framealpha=0.9) ax.set_xticks(range(2012, 2024)) ax.set_xticklabels([str(y) for y in range(2012, 2024)], rotation=45, fontsize=9) ax.grid(axis='y', alpha=0.3) plt.tight_layout() fig.savefig(os.path.join(FIG_DIR, '2020_H1_fig1.png')) plt.close(fig) print("fig1 saved") |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。fig.savefig(..., bbox_inches='tight') — 余白を自動で詰めて保存。plt.close() でメモリ解放。plt.subplots(figsize=(W, H)) で図サイズ指定、fig.savefig(..., bbox_inches='tight') で余白を自動で詰めて保存。138 | # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
=== 差の差分析 結果 ===
OLS Regression Results
==============================================================================
Dep. Variable: 純移動率 R-squared: 0.732
Model: OLS Adj. R-squared: 0.729
Method: Least Squares F-statistic: 215.1
Date: Mon, 18 May 2026 Prob (F-statistic): 3.21e-67
Time: 11:23:34 Log-Likelihood: -419.22
No. Observations: 240 AIC: 846.4
Df Residuals: 236 BIC: 860.4
Df Model: 3
Covariance Type: nonrobust
==============================================================================
coef std err t P>|t| [0.025 0.975]
------------------------------------------------------------------------------
const -3.4840 0.156 -22.264 0.000 -3.792 -3.176
treat 4.6314 0.221 20.928 0.000 4.195 5.067
post 0.1485 0.271 0.548 0.584 -0.386 0.682
did -0.1281 0.383 -0.334 0.739 -0.883 0.627
==============================================================================
Omnibus: 35.130 Durbin-Watson: 0.435
Prob(Omnibus): 0.000 Jarque-Bera (JB): 60.325
Skew: 0.802 Prob(JB): 7.95e-14
Kurtosis: 4.861 Cond. No. 6.32
==============================================================================
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
=== 重回帰分析(主要係数) ===
COVID係数: 0.4355, p=0.0000
転入率係数: 1.0921, p=0.0000
=== 東京都の純移動率 ===
年度
2023 4.152279
2022 2.415515
2021 0.771949
2020 2.731713
2019 6.180838
2018 5.960539
2017 5.483585
2016 5.435805
2015 6.044718
2014 5.469065
2013 5.273315
2012 4.269080
全国平均純移動率 2019: -1.9721, 2020: -1.4352, 差: 0.5369
DiD係数: -0.1281, p=0.7385
treat係数: 4.6314, post係数: 0.1485
=== 2019→2020 純移動率変化量 Top5 ===
都道府県
鳥取県 1.293457
宮城県 1.262235
青森県 1.243100
山梨県 1.210520
秋田県 1.188193
=== 2019→2020 純移動率変化量 Bottom5 ===
都道府県
東京都 -3.449125
愛知県 -0.428792
京都府 -0.195292
千葉県 -0.066998
滋賀県 -0.057791
fig1 saved.dropna() は欠損行を除去、.copy() は独立したコピーを作る。pandasで警告を防ぐ定石。差の差分析(Difference-in-Differences, DiD)は、政策・外部ショックの因果効果を推定する計量経済学の手法である。「処置群(COVID影響が大きいと考えられる都市型)」と「対照群(地方型)」を比較することで、COVID-19が純移動率に与えた固有の効果を推定する。
| 区分 | 定義 | 都道府県数 |
|---|---|---|
| 都市型(処置群) | 2019年純移動率 上位10都道府県 | 10 |
| 地方型(対照群) | 2019年純移動率 下位10都道府県 | 10 |
| COVID前期 | 2012〜2019年 | − |
| COVID後期 | 2020〜2023年 | − |
| 係数 | 推定値 | t値 | p値 | 解釈 |
|---|---|---|---|---|
| 定数項(地方型・COVID前) | −3.484‰ | −22.26 | p < 0.001 | 地方型のCOVID前ベースライン |
| 都市型ダミー(β₁) | +4.631‰ | 20.93 | p < 0.001 | 都市型は地方型より平均4.6‰高い |
| COVID後ダミー(β₂) | +0.149‰ | 0.55 | p = 0.584 | 全体的な時代効果(有意でない) |
| DiD係数(β₃) | −0.128‰ | −0.33 | p = 0.739 | 統計的に有意でない |
散布図(図3)では、東京都が最も大きく対角線の下方に位置し、2019年(6.18‰)から2020年(2.73‰)への急落が際立っている。一方、地方圏の多くの都道府県は対角線の上方(改善方向)に分布しており、東京一極集中の部分的な緩和が視覚的に確認できる。ただし、DiD分析での統計的有意性は得られておらず、この傾向が純粋にCOVIDに起因するかは慎重に解釈する必要がある。
DiDは、「処置群」と「対照群」の「COVID前後の差」を比較することで、コロナ禍の固有効果を推定する。重要な仮定は「平行トレンド仮定」(COVID前に両群が同じトレンドを持つ)である。
140 141 142 143 144 145 146 147 148 149 150 151 152 | nmr_2022_sorted = nmr_2022.sort_values(ascending=True) colors_fig2 = [region_colors[region_map.get(pref, '関東')] for pref in nmr_2022_sorted.index] fig, ax = plt.subplots(figsize=(9, 11)) bars = ax.barh(range(len(nmr_2022_sorted)), nmr_2022_sorted.values, color=colors_fig2, edgecolor='white', linewidth=0.4) ax.set_yticks(range(len(nmr_2022_sorted))) ax.set_yticklabels(nmr_2022_sorted.index, fontsize=9.5) ax.axvline(0, color='black', linewidth=0.8) ax.set_xlabel('純移動率(‰:人口1000人あたり)', fontsize=11) ax.set_title('図2 都道府県別 純移動率ランキング(2022年)\n(+は転入超過、−は転出超過)', fontsize=12, fontweight='bold', pad=10) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。sort_values('列名', ascending=False) — 指定列で並べ替え(降順)。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。df['A'] / df['B'] — pandasの列同士の四則演算は要素ごと(element-wise)。forループ不要なのが強み。153 154 155 156 157 158 159 160 161 162 | # 凡例 from matplotlib.patches import Patch legend_elements = [Patch(facecolor=region_colors[r], label=r) for r in region_order] ax.legend(handles=legend_elements, loc='lower right', fontsize=9, title='地域ブロック') ax.grid(axis='x', alpha=0.3) plt.tight_layout() fig.savefig(os.path.join(FIG_DIR, '2020_H1_fig2.png')) plt.close(fig) print("fig2 saved") |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。import pandas as pd など — 必要なライブラリをまとめて呼び出します。as pd は短い別名(alias)。fig.savefig(..., bbox_inches='tight') — 余白を自動で詰めて保存。plt.close() でメモリ解放。.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。163 | # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
fig2 saved
[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。都市型・地方型それぞれについて、COVID前(2012〜2019年)とCOVID後(2020〜2023年)の純移動率分布を箱ひげ図で比較した。また、両期間の平均値の差をt検定で検証した。
| グループ | COVID前(2012〜2019) | COVID後(2020〜2023) | 変化量 |
|---|---|---|---|
| 都市型(上位10県) | +1.147‰ | +1.168‰ | +0.021‰ |
| 地方型(下位10県) | −3.484‰ | −3.336‰ | +0.148‰ |
パネルデータは、同じ対象(都道府県)を複数時点にわたって観察したデータである。横断面データ(1時点のみ)より多くの情報を持ち、個体固有の効果(都道府県の特性)を制御できる。
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | common_prefs = nmr_2019.index.intersection(nmr_2020.index) x_vals = nmr_2019[common_prefs] y_vals = nmr_2020[common_prefs] delta_vals = (y_vals - x_vals) fig, ax = plt.subplots(figsize=(10, 8)) for pref in common_prefs: region = region_map.get(pref, '関東') color = region_colors.get(region, '#888888') ax.scatter(x_vals[pref], y_vals[pref], color=color, s=55, zorder=3, edgecolors='white', linewidth=0.5) offset_x, offset_y = 0.03, 0.02 ax.annotate(pref.replace('県', '').replace('都', '').replace('府', '').replace('道', ''), (x_vals[pref], y_vals[pref]), xytext=(x_vals[pref] + offset_x, y_vals[pref] + offset_y), fontsize=6.5, color='#333333') |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | # 対角線(変化なし線) lims = [min(x_vals.min(), y_vals.min()) - 0.5, max(x_vals.max(), y_vals.max()) + 0.5] ax.plot(lims, lims, 'k--', linewidth=0.9, alpha=0.5, label='変化なし(y=x)') ax.set_xlabel('2019年 純移動率(‰)', fontsize=12) ax.set_ylabel('2020年 純移動率(‰)', fontsize=12) ax.set_title('図3 COVID前後の純移動率変化(2019年→2020年)\n(対角線より下:転出超過が悪化、上:改善)', fontsize=12, fontweight='bold', pad=10) legend_elements = [Patch(facecolor=region_colors[r], label=r) for r in region_order] legend_elements.append(plt.Line2D([0], [0], color='k', linestyle='--', label='変化なし(y=x)')) ax.legend(handles=legend_elements, loc='upper left', fontsize=9) ax.axhline(0, color='gray', linewidth=0.6, linestyle=':') ax.axvline(0, color='gray', linewidth=0.6, linestyle=':') ax.grid(alpha=0.2) plt.tight_layout() fig.savefig(os.path.join(FIG_DIR, '2020_H1_fig3.png')) plt.close(fig) print("fig3 saved") |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。fig.savefig(..., bbox_inches='tight') — 余白を自動で詰めて保存。plt.close() でメモリ解放。[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。202 | # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
fig3 saved
r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。東京都の純移動率は2020年に大きく低下したが、これは主にCOVIDによる移動抑制・テレワーク拡大によるものと考えられる。ただし、2022年以降に回復傾向が見られ、構造的な変化(恒久的な地方移住の増加)に結びついたとは言いにくい。
| 順位 | 都道府県 | 純移動率変化量 | 特徴 |
|---|---|---|---|
| 1 | 鳥取県 | +1.29‰ | 地方移住の受け皿 |
| 2 | 宮城県 | +1.26‰ | 東北の拠点都市 |
| 3 | 青森県 | +1.24‰ | 転出超過が縮小 |
| 4 | 山梨県 | +1.21‰ | 首都圏近郊での地方移住 |
| 5 | 秋田県 | +1.19‰ | 転出超過が縮小 |
本研究のDiD分析では、都市型と地方型の格差縮小は統計的に有意ではなかった(p = 0.739)。しかし、東京都の純移動率が−3.45‰という「実際の大きな変化」も事実である。
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 245 246 247 248 249 | fig, axes = plt.subplots(1, 2, figsize=(11, 5.5), sharey=False) box_data = {} for city_type in ['都市型(上位10)', '地方型(下位10)']: sub = did_df[did_df['都市型'] == city_type] box_data[city_type] = { 'pre': sub[sub['年度'] < 2020]['純移動率'].values, 'post': sub[sub['年度'] >= 2020]['純移動率'].values } colors_box = {'pre': '#90CAF9', 'post': '#EF9A9A'} labels_box = {'pre': 'COVID前\n(2012〜2019)', 'post': 'COVID後\n(2020〜2023)'} for ax, city_type in zip(axes, ['都市型(上位10)', '地方型(下位10)']): bp = ax.boxplot( [box_data[city_type]['pre'], box_data[city_type]['post']], patch_artist=True, widths=0.45, medianprops=dict(color='black', linewidth=2), whiskerprops=dict(linewidth=1.2), capprops=dict(linewidth=1.2), flierprops=dict(marker='o', markerfacecolor='#666', markersize=4, alpha=0.5) ) for patch, col in zip(bp['boxes'], [colors_box['pre'], colors_box['post']]): patch.set_facecolor(col) # t検定 t_stat, t_pval = stats.ttest_ind( box_data[city_type]['pre'], box_data[city_type]['post'] ) sig_str = f"p = {t_pval:.3f}" + (" *" if t_pval < 0.05 else " (n.s.)") ax.set_xticks([1, 2]) ax.set_xticklabels([labels_box['pre'], labels_box['post']], fontsize=10) ax.set_ylabel('純移動率(‰)', fontsize=11) ax.set_title(f'{city_type}\n({sig_str})', fontsize=11, fontweight='bold') ax.axhline(0, color='gray', linewidth=0.7, linestyle='--') ax.grid(axis='y', alpha=0.3) fig.suptitle('図4 都市型・地方型 COVID前後の純移動率分布比較(箱ひげ図)', fontsize=12, fontweight='bold', y=1.01) plt.tight_layout() fig.savefig(os.path.join(FIG_DIR, '2020_H1_fig4.png'), bbox_inches='tight') plt.close(fig) print("fig4 saved") |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。fig.savefig(..., bbox_inches='tight') — 余白を自動で詰めて保存。plt.close() でメモリ解放。[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 | # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # 統計サマリー出力 # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ print("\n=== 統計サマリー(HTML埋め込み用) ===") tokyo_2019 = df_b[(df_b['都道府県'] == '東京都') & (df_b['年度'] == 2019)]['純移動率'].values[0] tokyo_2020 = df_b[(df_b['都道府県'] == '東京都') & (df_b['年度'] == 2020)]['純移動率'].values[0] tokyo_2021 = df_b[(df_b['都道府県'] == '東京都') & (df_b['年度'] == 2021)]['純移動率'].values[0] tokyo_2022 = df_b[(df_b['都道府県'] == '東京都') & (df_b['年度'] == 2022)]['純移動率'].values[0] print(f"東京都 純移動率: 2019={tokyo_2019:.3f}‰, 2020={tokyo_2020:.3f}‰, 2021={tokyo_2021:.3f}‰, 2022={tokyo_2022:.3f}‰") print(f"東京 2019→2020 変化: {tokyo_2020-tokyo_2019:.3f}‰") osaka_2020 = df_b[(df_b['都道府県'] == '大阪府') & (df_b['年度'] == 2020)]['純移動率'].values[0] osaka_2019 = df_b[(df_b['都道府県'] == '大阪府') & (df_b['年度'] == 2019)]['純移動率'].values[0] print(f"大阪府 純移動率: 2019={osaka_2019:.3f}‰, 2020={osaka_2020:.3f}‰, 変化={osaka_2020-osaka_2019:.3f}‰") |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。266 267 268 269 270 271 272 | # DiD print(f"\nDiD分析:") print(f" 都市型平均 COVID前: {did_df[(did_df['都市型']=='都市型(上位10)')&(did_df['post']==0)]['純移動率'].mean():.3f}‰") print(f" 都市型平均 COVID後: {did_df[(did_df['都市型']=='都市型(上位10)')&(did_df['post']==1)]['純移動率'].mean():.3f}‰") print(f" 地方型平均 COVID前: {did_df[(did_df['都市型']=='地方型(下位10)')&(did_df['post']==0)]['純移動率'].mean():.3f}‰") print(f" 地方型平均 COVID後: {did_df[(did_df['都市型']=='地方型(下位10)')&(did_df['post']==1)]['純移動率'].mean():.3f}‰") print(f" DiD係数={did_coef:.4f}, p={did_pval:.4f}") |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。x if cond else y は三項演算子。リスト内包表記と組み合わせると、forとifを1行で書けます。273 274 275 276 277 278 279 | # 2022年ランキング print(f"\n2022年 純移動率 Top5:") print(nmr_2022.head(5).to_string()) print(f"2022年 純移動率 Bottom5:") print(nmr_2022.tail(5).to_string()) print("\n全図の生成完了。") |
fig4 saved === 統計サマリー(HTML埋め込み用) === 東京都 純移動率: 2019=6.181‰, 2020=2.732‰, 2021=0.772‰, 2022=2.416‰ 東京 2019→2020 変化: -3.449‰ 大阪府 純移動率: 2019=1.209‰, 2020=1.514‰, 変化=0.305‰ DiD分析: 都市型平均 COVID前: 1.147‰ 都市型平均 COVID後: 1.168‰ 地方型平均 COVID前: -3.484‰ 地方型平均 COVID後: -3.336‰ DiD係数=-0.1281, p=0.7385 2022年 純移動率 Top5: 都道府県 千葉県 2.990744 埼玉県 2.626414 神奈川県 2.436308 東京都 2.415515 福岡県 1.736904 2022年 純移動率 Bottom5: 都道府県 山形県 -3.611912 青森県 -3.635382 岩手県 -3.707028 福島県 -3.716201 長崎県 -4.031956 全図の生成完了。
df[col](1列)と df[[col1, col2]](複数列)でカッコの数が違います。リストを渡していると覚えるとミスを減らせます。本研究では、SSDSE-B(47都道府県・2012〜2023年)を用いて、COVID-19が日本の都道府県間人口移動に与えた影響を、時系列分析・差の差分析・重回帰分析の3つの手法で検証した。
統計分析の解釈で初心者がやりがちな勘違いをまとめます。特に「相関と因果の混同」「p値の過信」は研究現場でもよく起きる落とし穴です。本文を読む前にも、読んだ後にも、目を通してみてください。
統計の基本用語を初心者向けに解説します。本文中で見慣れない言葉が出てきたら、ここに戻って確認してください。
統計手法について「何のためか」「結果をどう読むか」を初心者向けに解説します。
この研究をさらに発展させるための3つの方向性を示します。「今回わかったこと(X)」から「次に検証すべき仮説(Y)」を立て、「具体的に何をするか(Z)」まで考えてみましょう。
学んだだけでは身につきません。実際に手を動かすのが最強の学習方法です。本論文のスクリプトをベースに、以下のチャレンジに挑戦してみてください。難易度別に5つ用意しました。
本論文で学んだ手法は、研究の世界だけでなく、行政・企業・NPO の現場でも様々に活用されています。具体的なシーンを紹介します。
この論文を読んで初心者が抱きやすい疑問に、教育的観点から答えます。