このページの分析を自分で再現するには、以下の手順でデータを準備してください。コードの編集は不要です。
data/raw/ フォルダに入れます。html/figures/ に自動保存されます。
日本の都道府県間経済格差は「一億総中流」の時代から変化しているのか。地域間格差が縮小(収束)しているのか、それとも拡大(発散)しているのかは、地域政策・税財政の再配分設計において極めて重要な問いである。
まず「都道府県間経済格差の収束仮説検証β収束とσ収束の実証分析」を統計的にとらえることが有効だと考えられる。 その理由は感覚や経験則だけでは、複雑な社会要因の中で「何が本当に効いているか」を見極めにくいからである。 本研究では公開データと統計手法を組み合わせ、この問いに定量的な答えを出すことを目指す。
本分析では、新古典派成長理論(Solow模型)から導かれる「収束仮説」を実証的に検証する。経済水準の代理変数として消費支出(二人以上の世帯)を用い、SSDSE-B-2026から47都道府県・2012〜2023年の時系列データを使用する。
β収束 σ収束 OLS回帰 パネルデータ Solow模型
Solow(1956)の新古典派成長モデルでは、資本の限界生産力逓減の仮定から、初期資本水準(所得水準)が低い経済ほど定常状態への移行速度が速いことが導かれる。これが「絶対β収束仮説」の理論的基礎となる。
Barro & Sala-i-Martin(1992)は、Solow模型から以下の成長回帰式を導出した。これを都道府県データに適用する。
β収束は「貧しい地域が速く成長する」という条件であり、格差が縮小するための必要条件だが十分条件ではない。たとえば初期の順位変動(富裕・貧困地域の入れ替わり)が激しければ、β収束があってもσが増大することがある(Quah 1993)。
1 2 3 4 5 6 | fig, ax = plt.subplots(figsize=(8, 5.5)) for region, grp in conv.groupby('地域'): color = region_colors.get(region, '#888888') ax.scatter(grp['log初期水準'], grp['成長率'] * 100, color=color, s=60, alpha=0.85, label=region, zorder=3) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。df.groupby('列').apply(関数) — グループごとに関数を適用。時系列や地域別の集計でよく使います。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。x if cond else y は三項演算子。リスト内包表記と組み合わせると、forとifを1行で書けます。7 8 9 10 11 12 | # 回帰直線 x_line = np.linspace(conv['log初期水準'].min() - 0.05, conv['log初期水準'].max() + 0.05, 200) y_line = beta_res.params['const'] + beta_coef * x_line ax.plot(x_line, y_line * 100, color='#1565C0', linewidth=2.0, linestyle='--', label='OLS回帰直線', zorder=4) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。df[col](1列)と df[[col1, col2]](複数列)でカッコの数が違います。リストを渡していると覚えるとミスを減らせます。13 14 15 16 17 18 19 | # 都道府県名ラベル(上位・下位) extremes = pd.concat([conv.nsmallest(3, '成長率'), conv.nlargest(3, '成長率')]) for _, row in extremes.iterrows(): label = row['都道府県'].replace('県', '').replace('府', '').replace('都', '').replace('道', '') ax.annotate(label, xy=(row['log初期水準'], row['成長率'] * 100), xytext=(4, 2), textcoords='offset points', fontsize=8, color='#333333') |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。for _, row in df.iterrows() — DataFrameを1行ずつ取り出すループ。1点ずつ描画したいときに使用。s[:-n]「末尾n文字を除く」/s[n:]「先頭n文字を除く」。スライス [start:stop:step] はリスト・タプル・文字列共通の基本ワザです。20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | # β係数・p値テキスト sig_str = '***' if beta_pval < 0.001 else ('**' if beta_pval < 0.01 else ('*' if beta_pval < 0.05 else 'n.s.')) conv_str = '収束' if beta_coef < 0 else '発散' ax.text(0.05, 0.95, f'β = {beta_coef:.4f} {sig_str}\np = {beta_pval:.4f}\nR² = {beta_r2:.3f}\n→ {conv_str}', transform=ax.transAxes, fontsize=10, va='top', bbox=dict(boxstyle='round,pad=0.5', facecolor='#EFF3FF', alpha=0.9)) ax.set_xlabel(f'log(消費支出{start_yr}年)', fontsize=12) ax.set_ylabel(f'消費支出の年率成長率 ({start_yr}→{end_yr}年) [%]', fontsize=12) ax.set_title(f'β収束分析:初期水準と成長率の関係\n({start_yr}〜{end_yr}年、47都道府県)', fontsize=13, fontweight='bold') ax.legend(loc='upper right', fontsize=9, framealpha=0.9) ax.grid(True, alpha=0.3) ax.axhline(0, color='gray', linewidth=0.8, linestyle=':') plt.tight_layout() out_path = os.path.join(FIG_DIR, '2018_U3_fig1.png') plt.savefig(out_path, dpi=150, bbox_inches='tight') plt.close() print(f"\n保存: {out_path}") |
保存: html/figures/2018_U3_fig1.png
ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。fig.savefig(..., bbox_inches='tight') — 余白を自動で詰めて保存。plt.close() でメモリ解放。np.cumsum(arr) は累積和、np.linspace(a, b, n) は「aからbを等間隔でn個」。NumPyの定石です。| データ | 出典 | 使用変数 | 期間 |
|---|---|---|---|
| SSDSE-B 2026(都道府県別時系列) | 統計数理研究所 | 消費支出(二人以上の世帯)[円/月] | 2012〜2023年 |
| 統計量 | 2012年(初期) | 2023年(終期) |
|---|---|---|
| 平均(円/月) | 286,665 | 295,856 |
| 標準偏差(円/月) | 21,617 | 24,144 |
| 最小(円/月) | 227,981 | 223,423 |
| 最大(円/月) | 324,779 | 344,092 |
| 変動係数(CV) | 0.0754 | 0.0816 |
| 最大/最小比 | 1.42 | 1.54 |
Solow(1956)の資本蓄積方程式と限界生産力逓減の仮定から、所得水準が定常状態 k* より低いほど成長速度が速いことが証明される。この収束速度 λ は技術的に λ ≈ (1−α)(δ+n+g) で近似される(α: 資本所得分配率)。
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | 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) 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) print("columns:", df_b.columns.tolist()) print("年度range:", df_b['年度'].min(), '-', df_b['年度'].max()) print("都道府県数:", df_b['都道府県'].nunique()) |
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) — 列を整数に変換(年度などを数値比較するため)。f"...{x}..." はf-string。文字列の中に {変数} と書くだけで埋め込めて、{x:.2f} のように書式も指定できます。65 66 67 68 | # 消費支出列を特定 cons_col = '消費支出(二人以上の世帯)' print(f"使用列: {cons_col}") print(df_b[['都道府県', '年度', cons_col]].head()) |
columns: ['年度', '地域コード', '都道府県', '総人口', '総人口(男)', '総人口(女)', '日本人人口', '日本人人口(男)', '日本人人口(女)', '15歳未満人口', '15歳未満人口(男)', '15歳未満人口(女)', '15~64歳人口', '15~64歳人口(男)', '15~64歳人口(女)', '65歳以上人口', '65歳以上人口(男)', '65歳以上人口(女)', '出生数', '出生数(男)', '出生数(女)', '合計特殊出生率', '死亡数', '死亡数(男)', '死亡数(女)', '転入者数(日本人移動者)', '転入者数(日本人移動者)(男)', '転入者数(日本人移動者)(女)', '転出者数(日本人移動者)', '転出者数(日本人移動者)(男)', '転出者数(日本人移動者)(女)', '婚姻件数', '離婚件数', '年平均気温', '最高気温(日最高気温の月平均の最高値)', '最低気温(日最低気温の月平均の最低値)', '降水日数(年間)', '降水量(年間)', '着工建築物数', '着工建築物床面積', '旅館営業施設数(ホテルを含む)', '旅館営業施設客室数(ホテルを含む)', '標準価格(平均価格)(住宅地)', '標準価格(平均価格)(商業地)', '幼稚園数', '幼稚園教員数', '幼稚園在園者数', '小学校数', '小学校教員数', '小学校児童数', '中学校数', '中学校教員数', '中学校生徒数', '中学校卒業者数', '中学校卒業者のうち進学者数', '高等学校数', '高等学校教員数', '高等学校生徒数', '高等学校卒業者数', '高等学校卒業者のうち進学者数', '短期大学数', '大学数', '短期大学教員数', '大学教員数', '短期大学学生数', '大学学生数', '短期大学卒業者数', '短期大学卒業者のうち進学者数', '大学卒業者数', '大学卒業者のうち進学者数', '専修学校数', '各種学校数', '専修学校生徒数', '各種学校生徒数', '新規求職申込件数(一般)', '月間有効求職者数(一般)', '月間有効求人数(一般)', '充足数(一般)', '就職件数(一般)', '一般旅券発行件数', '延べ宿泊者数', '外国人延べ宿泊者数', '着工新設住宅戸数', '着工新設持家数', '着工新設貸家数', '着工新設分譲住宅数', '着工新設住宅床面積', '着工新設持家床面積', '着工新設分譲住宅床面積', '着工新設貸家床面積', 'ごみ総排出量(総量)', '1人1日当たりの排出量', 'ごみのリサイクル率', '一般病院数', '一般診療所数', '歯科診療所数', '保育所等数', '保育所等定員数', '保育所等利用待機児童数', '保育所等在所児数', '保育所等保育士数', '消費支出(二人以上の世帯)', '食料費(二人以上の世帯)', '住居費(二人以上の世帯)', '光熱・水道費(二人以上の世帯)', '家具・家事用品費(二人以上の世帯)', '被服及び履物費(二人以上の世帯)', '保健医療費(二人以上の世帯)', '交通・通信費(二人以上の世帯)', '教育費(二人以上の世帯)', '教養娯楽費(二人以上の世帯)', 'その他の消費支出(二人以上の世帯)'] 年度range: 2012 - 2023 都道府県数: 47 使用列: 消費支出(二人以上の世帯) 都道府県 年度 消費支出(二人以上の世帯) 0 北海道 2023 296888 1 北海道 2022 277737 2 北海道 2021 268396 3 北海道 2020 301683 4 北海道 2019 294682
df['A'] / df['B'] — pandasの列同士の四則演算は要素ごと(element-wise)。forループ不要なのが強み。69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | region_map_raw = { '北海道': '北海道・東北', '青森県': '北海道・東北', '岩手県': '北海道・東北', '宮城県': '北海道・東北', '秋田県': '北海道・東北', '山形県': '北海道・東北', '福島県': '北海道・東北', '茨城県': '関東', '栃木県': '関東', '群馬県': '関東', '埼玉県': '関東', '千葉県': '関東', '東京都': '関東', '神奈川県': '関東', '新潟県': '中部', '富山県': '中部', '石川県': '中部', '福井県': '中部', '山梨県': '中部', '長野県': '中部', '岐阜県': '中部', '静岡県': '中部', '愛知県': '中部', '三重県': '近畿', '滋賀県': '近畿', '京都府': '近畿', '大阪府': '近畿', '兵庫県': '近畿', '奈良県': '近畿', '和歌山県': '近畿', '鳥取県': '中国・四国', '島根県': '中国・四国', '岡山県': '中国・四国', '広島県': '中国・四国', '山口県': '中国・四国', '徳島県': '中国・四国', '香川県': '中国・四国', '愛媛県': '中国・四国', '高知県': '中国・四国', '福岡県': '九州・沖縄', '佐賀県': '九州・沖縄', '長崎県': '九州・沖縄', '熊本県': '九州・沖縄', '大分県': '九州・沖縄', '宮崎県': '九州・沖縄', '鹿児島県': '九州・沖縄', '沖縄県': '九州・沖縄' } region_colors = { '北海道・東北': '#4e9af1', '関東': '#e05c5c', '中部': '#f0a500', '近畿': '#5cb85c', '中国・四国': '#9b59b6', '九州・沖縄': '#f39c12' } df_b['地域'] = df_b['都道府県'].map(region_map_raw) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。df['A'] / df['B'] — pandasの列同士の四則演算は要素ごと(element-wise)。forループ不要なのが強み。94 95 96 97 98 | print(f"\n=== 消費支出 記述統計 ({start_yr}年・{end_yr}年) ===") for yr in [start_yr, end_yr]: s = df_b[df_b['年度'] == yr][cons_col] print(f"{yr}年: 平均={s.mean():.0f}, 標準偏差={s.std():.0f}, " f"最小={s.min():.0f}, 最大={s.max():.0f}, CV={s.std()/s.mean():.4f}") |
=== 消費支出 記述統計 (2012年・2023年) === 2012年: 平均=286665, 標準偏差=21617, 最小=227981, 最大=324779, CV=0.0754 2023年: 平均=295856, 標準偏差=24144, 最小=223423, 最大=344092, CV=0.0816
r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。都道府県iの初期消費水準(2012年)の対数と、2012〜2023年の年率成長率の関係をOLS回帰で推定する。β < 0ならば初期水準が低い(貧しい)都道府県ほど成長率が高い「追いつき効果」が存在する。
| パラメータ | 推定値 | 標準誤差 | t統計量 | p値 | 判定 |
|---|---|---|---|---|---|
| 定数項(α) | 0.4237 | — | — | — | — |
| β(log初期水準) | −0.0339 | — | — | 0.0073 ** | 有意(収束) |
| R² | 0.1493 | 観測数:47都道府県 | |||
格差の大きさを測る指標は複数あるが、単位に依存しない変動係数(CV = 標準偏差/平均)が最も頻繁に用いられる。CVは0〜∞の値をとり、値が大きいほど格差が大きい。
100 101 102 103 104 105 106 | fig, ax = plt.subplots(figsize=(8, 5)) years_arr = sigma['年度'].values cv_arr = sigma['CV'].values # COVID帯(2019〜2021)グレー塗り ax.axvspan(2019.5, 2021.5, color='#BDBDBD', alpha=0.3, label='COVID-19 影響期') |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。df[col](1列)と df[[col1, col2]](複数列)でカッコの数が違います。リストを渡していると覚えるとミスを減らせます。107 108 109 110 111 112 113 114 | # CV折れ線 ax.plot(years_arr, cv_arr, color='#1565C0', linewidth=2.2, marker='o', markersize=7, label='変動係数(CV)', zorder=3) # OLSトレンド線 y_trend = trend_res.params['const'] + trend_coef * years_arr ax.plot(years_arr, y_trend, color='#C62828', linewidth=1.8, linestyle='--', label=f'OLSトレンド (β={trend_coef:.5f})', zorder=4) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。s[:-n]「末尾n文字を除く」/s[n:]「先頭n文字を除く」。スライス [start:stop:step] はリスト・タプル・文字列共通の基本ワザです。115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | # 各年の値をアノテーション for yr, cv in zip(years_arr, cv_arr): ax.annotate(f'{cv:.4f}', xy=(yr, cv), xytext=(0, 8), textcoords='offset points', fontsize=7.5, ha='center', color='#1565C0') # 判定テキスト sig_str2 = '***' if trend_pval < 0.001 else ('**' if trend_pval < 0.01 else ('*' if trend_pval < 0.05 else 'n.s.')) sigma_str = 'σ収束(格差縮小)' if trend_coef < 0 else 'σ発散(格差拡大)' ax.text(0.05, 0.10, f'トレンド係数 = {trend_coef:.5f} {sig_str2}\np = {trend_pval:.4f}\n→ {sigma_str}', transform=ax.transAxes, fontsize=10, va='bottom', bbox=dict(boxstyle='round,pad=0.5', facecolor='#FFF9C4', alpha=0.9)) ax.set_xlabel('年度', fontsize=12) ax.set_ylabel('変動係数(CV = 標準偏差 / 平均)', fontsize=12) ax.set_title(f'σ収束分析:都道府県間消費格差の変動係数推移\n({start_yr}〜{end_yr}年、47都道府県)', fontsize=13, fontweight='bold') ax.legend(loc='upper right', fontsize=9, framealpha=0.9) ax.grid(True, alpha=0.3) ax.set_xticks(years_arr) plt.tight_layout() out_path = os.path.join(FIG_DIR, '2018_U3_fig2.png') plt.savefig(out_path, dpi=150, bbox_inches='tight') plt.close() print(f"保存: {out_path}") |
保存: html/figures/2018_U3_fig2.png
fig.savefig(..., bbox_inches='tight') — 余白を自動で詰めて保存。plt.close() でメモリ解放。np.cumsum(arr) は累積和、np.linspace(a, b, n) は「aからbを等間隔でn個」。NumPyの定石です。各年の47都道府県の消費支出の変動係数(CV = 標準偏差/平均)を計算し、その時系列を観察する。CVが時間とともに減少すればσ収束(格差縮小)、増加すればσ発散(格差拡大)となる。
| 年度 | 変動係数(CV) | 前年比 |
|---|---|---|
| 2012 | 0.0754 | — |
| 2013 | 0.0709 | ▼ 縮小 |
| 2014 | 0.0855 | ▲ 拡大 |
| 2015 | 0.0834 | ▼ 縮小 |
| 2016 | 0.0853 | ▲ 拡大 |
| 2017 | 0.0870 | ▲ 拡大 |
| 2018 | 0.0864 | ▼ 縮小 |
| 2019 | 0.0910 | ▲ 拡大 |
| 2020 | 0.0808 | ▼ 縮小(COVID影響) |
| 2021 | 0.0836 | ▲ 拡大 |
| 2022 | 0.0662 | ▼ 大幅縮小 |
| 2023 | 0.0816 | ▲ 拡大 |
| パラメータ | 推定値 | p値 | 判定 |
|---|---|---|---|
| トレンド係数(b) | +0.000057 | 0.9300 | 非有意(σ収束なし) |
β係数の符号は収束分析の核心である。成長回帰 g_i = α + β·log(y_i,0) + ε_i において:
143 144 145 146 147 148 | fig, ax = plt.subplots(figsize=(7.5, 6.5)) for region, grp in conv.groupby('地域'): color = region_colors.get(region, '#888888') ax.scatter(grp['start'] / 1000, grp['end'] / 1000, color=color, s=60, alpha=0.85, label=region, zorder=3) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。df.groupby('列').apply(関数) — グループごとに関数を適用。時系列や地域別の集計でよく使います。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。s[:-n]「末尾n文字を除く」/s[n:]「先頭n文字を除く」。スライス [start:stop:step] はリスト・タプル・文字列共通の基本ワザです。149 150 151 152 153 154 155 156 157 158 159 160 161 162 | # 45度線 all_vals = pd.concat([conv['start'], conv['end']]) / 1000 vmin, vmax = all_vals.min() * 0.97, all_vals.max() * 1.03 ax.plot([vmin, vmax], [vmin, vmax], color='gray', linewidth=1.5, linestyle='--', label='45度線(変化なし)', zorder=2) # 都道府県名ラベル(上位・下位 消費水準) top_prefs = conv.nlargest(4, 'end') bot_prefs = conv.nsmallest(4, 'end') for _, row in pd.concat([top_prefs, bot_prefs]).iterrows(): label = row['都道府県'].replace('県', '').replace('府', '').replace('都', '').replace('道', '') ax.annotate(label, xy=(row['start'] / 1000, row['end'] / 1000), xytext=(4, 2), textcoords='offset points', fontsize=7.5, color='#444') |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。for _, row in df.iterrows() — DataFrameを1行ずつ取り出すループ。1点ずつ描画したいときに使用。np.cumsum(arr) は累積和、np.linspace(a, b, n) は「aからbを等間隔でn個」。NumPyの定石です。163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 | # 45度線より上 = 改善(消費増加)の注釈 ax.text(0.05, 0.95, '▲ 45度線より上 = 消費増加(成長)', transform=ax.transAxes, fontsize=9, va='top', color='#2E7D32', bbox=dict(boxstyle='round,pad=0.4', facecolor='#E8F5E9', alpha=0.8)) ax.text(0.05, 0.87, '▼ 45度線より下 = 消費減少(後退)', transform=ax.transAxes, fontsize=9, va='top', color='#C62828', bbox=dict(boxstyle='round,pad=0.4', facecolor='#FFEBEE', alpha=0.8)) ax.set_xlabel(f'消費支出 {start_yr}年 [千円/月]', fontsize=12) ax.set_ylabel(f'消費支出 {end_yr}年 [千円/月]', fontsize=12) ax.set_title(f'都道府県別消費支出:{start_yr}年 vs {end_yr}年\n(地域色分け、45度線付き)', fontsize=13, fontweight='bold') ax.legend(loc='lower right', fontsize=8.5, framealpha=0.9) ax.grid(True, alpha=0.3) plt.tight_layout() out_path = os.path.join(FIG_DIR, '2018_U3_fig3.png') plt.savefig(out_path, dpi=150, bbox_inches='tight') plt.close() print(f"保存: {out_path}") |
保存: html/figures/2018_U3_fig3.png
fig.savefig(..., bbox_inches='tight') — 余白を自動で詰めて保存。plt.close() でメモリ解放。{値:.2f}(小数2桁)、{値:,}(3桁区切り)、{値:>10}(右寄せ10桁)など、覚えると出力が一気に整います。都道府県単位の収束仮説に加え、6地域(北海道・東北、関東、中部、近畿、中国・四国、九州・沖縄)の消費格差パターンを可視化する。
| 地域 | 2012年平均(千円) | 2023年平均(千円) | 変化率 | 相対水準(全国比) |
|---|---|---|---|---|
| 関東 | 307 | 322 | +5% | +12%(高消費) |
| 近畿 | 286 | 291 | +2% | +0%(全国並み) |
| 中部 | 292 | 304 | +4% | +3%(やや高) |
| 北海道・東北 | 272 | 283 | +4% | −5%(低消費) |
| 中国・四国 | 280 | 288 | +3% | −3%(やや低) |
| 九州・沖縄 | 270 | 280 | +4% | −5%(低消費) |
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 | start_yr = df_b['年度'].min() # 2012 end_yr = df_b['年度'].max() # 2023 T = end_yr - start_yr # 11年 start_df = (df_b[df_b['年度'] == start_yr] [['都道府県', cons_col]] .rename(columns={cons_col: 'start'})) end_df = (df_b[df_b['年度'] == end_yr] [['都道府県', cons_col]] .rename(columns={cons_col: 'end'})) conv = start_df.merge(end_df, on='都道府県').dropna() conv['成長率'] = (conv['end'] - conv['start']) / (conv['start'] * T) # 年率成長率 conv['log初期水準'] = np.log(conv['start']) conv['地域'] = conv['都道府県'].map(region_map_raw) X_beta = sm.add_constant(conv['log初期水準'].astype(float)) beta_res = sm.OLS(conv['成長率'].astype(float), X_beta).fit() beta_coef = beta_res.params['log初期水準'] beta_pval = beta_res.pvalues['log初期水準'] beta_r2 = beta_res.rsquared beta_n = int(beta_res.nobs) print(f"\n=== β収束 ===") print(f"β係数: {beta_coef:.4f}") print(f"p値: {beta_pval:.4f}") print(f"R²: {beta_r2:.4f}") print(f"観測数: {beta_n}") print(f"収束の判定: {'収束(β<0)' if beta_coef < 0 else '発散(β>0)'}") |
=== β収束 === β係数: -0.0339 p値: 0.0073 R²: 0.1493 観測数: 47 収束の判定: 収束(β<0)
sm.add_constant(X) — 切片項(定数1の列)を先頭に追加。statsmodelsで必須。sm.OLS(y, X).fit() — 最小二乗法でモデルを推定。model.params, model.pvalues, model.conf_int() で結果取得。.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | sigma = (df_b.groupby('年度')[cons_col] .apply(lambda x: x.std() / x.mean()) .reset_index()) sigma.columns = ['年度', 'CV'] sigma = sigma.sort_values('年度') trend_X = sm.add_constant(sigma['年度'].astype(float)) trend_res = sm.OLS(sigma['CV'].astype(float), trend_X).fit() trend_coef = trend_res.params['年度'] trend_pval = trend_res.pvalues['年度'] print(f"\n=== σ収束(変動係数) ===") print(sigma.to_string(index=False)) print(f"トレンド係数: {trend_coef:.6f}") print(f"p値: {trend_pval:.4f}") print(f"収束の判定: {'σ収束(負のトレンド)' if trend_coef < 0 else 'σ発散(正のトレンド)'}") |
=== σ収束(変動係数) === 年度 CV 2012 0.075410 2013 0.070919 2014 0.085476 2015 0.083449 2016 0.085346 2017 0.086992 2018 0.086389 2019 0.091014 2020 0.080821 2021 0.083591 2022 0.066246 2023 0.081607 トレンド係数: 0.000057 p値: 0.9300 収束の判定: σ発散(正のトレンド)
df.groupby('列').apply(関数) — グループごとに関数を適用。時系列や地域別の集計でよく使います。sort_values('列名', ascending=False) — 指定列で並べ替え(降順)。sm.add_constant(X) — 切片項(定数1の列)を先頭に追加。statsmodelsで必須。sm.OLS(y, X).fit() — 最小二乗法でモデルを推定。model.params, model.pvalues, model.conf_int() で結果取得。.apply(lambda x: ...) — その場限りの無名関数を適用。短いロジックなら def せずに書けます。[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 | df_b['地域'] = df_b['都道府県'].map(region_map_raw) region_ts = (df_b.dropna(subset=['地域']) .groupby(['年度', '地域'])[cons_col] .mean() .reset_index()) region_ts.columns = ['年度', '地域', '平均消費支出'] fig, ax = plt.subplots(figsize=(9, 5.5)) region_order = ['関東', '近畿', '中部', '北海道・東北', '中国・四国', '九州・沖縄'] for region in region_order: sub = region_ts[region_ts['地域'] == region].sort_values('年度') if sub.empty: continue color = region_colors[region] ax.plot(sub['年度'], sub['平均消費支出'] / 1000, color=color, linewidth=2.2, marker='o', markersize=5, label=region, zorder=3) # 最終年ラベル last = sub.iloc[-1] ax.annotate(region, xy=(last['年度'], last['平均消費支出'] / 1000), xytext=(4, 0), textcoords='offset points', fontsize=8, color=color, va='center', fontweight='bold') |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。df.groupby('列').apply(関数) — グループごとに関数を適用。時系列や地域別の集計でよく使います。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。sort_values('列名', ascending=False) — 指定列で並べ替え(降順)。np.cumsum(arr) は累積和、np.linspace(a, b, n) は「aからbを等間隔でn個」。NumPyの定石です。253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 | # COVID帯 ax.axvspan(2019.5, 2021.5, color='#BDBDBD', alpha=0.25, label='COVID-19 影響期') # 全国平均 nat_ts = df_b.groupby('年度')[cons_col].mean().reset_index() ax.plot(nat_ts['年度'], nat_ts[cons_col] / 1000, color='black', linewidth=1.5, linestyle=':', marker='s', markersize=4, label='全国平均', zorder=4) ax.set_xlabel('年度', fontsize=12) ax.set_ylabel('消費支出(月平均)[千円]', fontsize=12) ax.set_title('地域別消費支出の時系列推移\n(6地域別平均、全国平均を含む)', fontsize=13, fontweight='bold') ax.legend(loc='upper left', fontsize=8.5, framealpha=0.9, bbox_to_anchor=(0.0, 1.0)) ax.grid(True, alpha=0.3) all_years = sorted(df_b['年度'].unique()) ax.set_xticks(all_years) ax.set_xticklabels([str(y) for y in all_years], rotation=45, fontsize=9) plt.tight_layout() out_path = os.path.join(FIG_DIR, '2018_U3_fig4.png') plt.savefig(out_path, dpi=150, bbox_inches='tight') plt.close() print(f"保存: {out_path}") |
保存: html/figures/2018_U3_fig4.png
df.groupby('列').apply(関数) — グループごとに関数を適用。時系列や地域別の集計でよく使います。fig.savefig(..., bbox_inches='tight') — 余白を自動で詰めて保存。plt.close() でメモリ解放。{値:.2f}(小数2桁)、{値:,}(3桁区切り)、{値:>10}(右寄せ10桁)など、覚えると出力が一気に整います。278 279 280 281 282 283 | for i in range(1, 5): path = os.path.join(FIG_DIR, f'2018_U3_fig{i}.png') size = os.path.getsize(path) / 1024 print(f" fig{i}: {size:.1f} KB {'OK' if size > 10 else 'WARN'}") print("\nDONE: 2018_U3_suri") |
fig1: 125.0 KB OK fig2: 128.9 KB OK fig3: 130.8 KB OK fig4: 180.0 KB OK DONE: 2018_U3_suri
{値:.2f}(小数2桁)、{値:,}(3桁区切り)、{値:>10}(右寄せ10桁)など、覚えると出力が一気に整います。| 検定 | 指標 | 結果 | 解釈 |
|---|---|---|---|
| β収束 | β = −0.0339 | 有意(p = 0.007, **) | 追いつき効果が統計的に確認された |
| σ収束 | CVトレンド = +0.000057 | 非有意(p = 0.930) | 格差の縮小傾向は確認されなかった |
以下のスクリプトをダウンロードし、data/raw/SSDSE-B-2026.csv と同じディレクトリ構成で配置して実行すると全図・全結果を再現できます。
| データ | 出典 |
|---|---|
| SSDSE-B 2026(都道府県別時系列) | 統計数理研究所 社会・人口統計体系データセット(SSDSE) |
使用変数:消費支出(二人以上の世帯)[円/月]、47都道府県、2012〜2023年(12時点)。全て実公的データ(合成データ不使用)。
統計分析の解釈で初心者がやりがちな勘違いをまとめます。特に「相関と因果の混同」「p値の過信」は研究現場でもよく起きる落とし穴です。本文を読む前にも、読んだ後にも、目を通してみてください。
統計の基本用語を初心者向けに解説します。本文中で見慣れない言葉が出てきたら、ここに戻って確認してください。
統計手法について「何のためか」「結果をどう読むか」を初心者向けに解説します。
この研究をさらに発展させるための3つの方向性を示します。「今回わかったこと(X)」から「次に検証すべき仮説(Y)」を立て、「具体的に何をするか(Z)」まで考えてみましょう。
学んだだけでは身につきません。実際に手を動かすのが最強の学習方法です。本論文のスクリプトをベースに、以下のチャレンジに挑戦してみてください。難易度別に5つ用意しました。
本論文で学んだ手法は、研究の世界だけでなく、行政・企業・NPO の現場でも様々に活用されています。具体的なシーンを紹介します。
この論文を読んで初心者が抱きやすい疑問に、教育的観点から答えます。