このページの分析を自分で再現するには、以下の手順でデータを準備してください。コードの編集は不要です。
data/raw/ フォルダに入れます。html/figures/ に自動保存されます。
日本では高度経済成長以降、地域間の所得格差・消費格差が政策課題として議論されてきた。地方交付税制度などの財政移転が格差を圧縮する一方、人口動態や産業構造の違いが格差を広げる要因ともなっている。本研究は、47都道府県の消費支出データ(2012-2022年)を用いて、都道府県間の消費格差がどのように変化してきたか(時系列分析)、また格差の大小を規定する要因は何か(重回帰分析)を統計的に分析する。
まず「都道府県別消費格差の動態分析ジニ係数の時系列変化と規定要因」を統計的にとらえることが有効だと考えられる。 その理由は感覚や経験則だけでは、複雑な社会要因の中で「何が本当に効いているか」を見極めにくいからである。 本研究では公開データと統計手法を組み合わせ、この問いに定量的な答えを出すことを目指す。
ジニ係数 変動係数 重回帰分析(OLS) 時系列分析
統計数理研究所が提供する SSDSE-B(都道府県データ)2026年版を使用。2012〜2022年の11年間、47都道府県(計517観測)を対象とする。
| 変数 | 元データ列名 | 変換・定義 | 役割 |
|---|---|---|---|
| 消費支出 | 消費支出(二人以上の世帯) | 円/月のまま使用 | 目的変数 |
| 有効求人倍率 | 月間有効求人数・求職者数 | 求人数÷求職者数(倍) | 説明変数(雇用) |
| 高齢化率 | 65歳以上人口・総人口 | 65歳以上÷総人口×100(%) | 説明変数(人口) |
| 大学進学率 | 高校卒業者・進学者数 | 高校卒進学者÷高校卒業者×100(%) | 説明変数(教育) |
| 都市集積度 | 転入者数・総人口 | 転入者数÷総人口×100(%) | 説明変数(都市化) |
| 変数 | 最小値 | 平均 | 最大値 | 標準偏差 |
|---|---|---|---|---|
| 消費支出(万円/月) | 24.5 | 29.0 | 32.5 | 1.8 |
| 有効求人倍率(倍) | 0.84 | 1.27 | 1.79 | 0.22 |
| 高齢化率(%) | 20.9 | 29.4 | 35.9 | 3.3 |
| 大学進学率(%) | 38.4 | 52.2 | 74.1 | 8.3 |
| 都市集積度(転入率%) | 0.9 | 1.6 | 3.2 | 0.5 |
まず2022年時点の47都道府県の消費支出水準を確認する。地域ブロック別に色分けし、全国平均(29.0万円/月)を破線で示す。
1 2 3 4 5 6 7 8 9 | import os import numpy as np import pandas as pd import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt import matplotlib.patches as mpatches import statsmodels.api as sm from scipy import stats |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。import pandas as pd など — 必要なライブラリをまとめて呼び出します。as pd は短い別名(alias)。matplotlib.use('Agg') — グラフを画面表示せずファイルに保存するためのおまじない。f"...{x}..." はf-string。文字列の中に {変数} と書くだけで埋め込めて、{x:.2f} のように書式も指定できます。10 11 12 13 14 15 16 17 18 | # ── フォント設定 ────────────────────────────────────────────────────────────── 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 はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。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ループ不要なのが強み。19 20 21 22 23 24 25 26 | # ── データ読み込み ──────────────────────────────────────────────────────────── 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("=== 利用可能な列 ===") print(df_b.columns.tolist()) print() |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。pd.read_csv(...) でCSVを読み込みます。encoding='cp932' は日本語Windows由来の文字コード、header=1 は「2行目を列名として使う」。df['地域コード'].str.match(r'^R\d{5}', ...) — 正規表現で「R+数字5桁」の行(47都道府県)だけTrueにし、真偽値で行をフィルタ。.astype(int) — 列を整数に変換(年度などを数値比較するため)。.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。27 28 29 30 31 32 33 34 | # ── 分析変数を構築 ──────────────────────────────────────────────────────────── cons_col = '消費支出(二人以上の世帯)' # 有効求人倍率 = 月間有効求人数 / 月間有効求職者数 df_b['有効求人倍率'] = df_b['月間有効求人数(一般)'] / df_b['月間有効求職者数(一般)'] # 高齢化率 = 65歳以上人口 / 総人口 × 100 df_b['高齢化率'] = df_b['65歳以上人口'] / df_b['総人口'] * 100 |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。35 36 37 38 39 40 | # 大学進学率 = 高等学校卒業者のうち進学者数 / 高等学校卒業者数 × 100 df_b['大学進学率'] = df_b['高等学校卒業者のうち進学者数'] / df_b['高等学校卒業者数'] * 100 # 人口密度の代理変数 = 着工建築物数 / 総人口 × 10000(相対都市化指標) # ※SSDSE-Bに面積データがないため、転入者数/総人口を都市集積度の代理とする df_b['都市集積度'] = df_b['転入者数(日本人移動者)'] / df_b['総人口'] * 100 |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。41 42 43 44 45 46 47 | # 消費支出を万円単位に df_b['消費支出万'] = df_b[cons_col] / 10000 print("=== データサマリー(主要変数) ===") X_cols = ['有効求人倍率', '高齢化率', '大学進学率', '都市集積度'] print(df_b[['年度', '都道府県', cons_col] + X_cols].describe()) print() |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。.describe() — 件数・平均・標準偏差・四分位・最大/最小を一括計算。データの素性チェックに必須。x if cond else y は三項演算子。リスト内包表記と組み合わせると、forとifを1行で書けます。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 | # ── 地域ブロック定義 ────────────────────────────────────────────────────────── # 都道府県名は「〇〇県」「〇〇府」「〇〇都」「北海道」形式 region_map_raw = { '北海道': '北海道・東北', '青森': '北海道・東北', '岩手': '北海道・東北', '宮城': '北海道・東北', '秋田': '北海道・東北', '山形': '北海道・東北', '福島': '北海道・東北', '茨城': '関東', '栃木': '関東', '群馬': '関東', '埼玉': '関東', '千葉': '関東', '東京': '関東', '神奈川': '関東', '新潟': '中部', '富山': '中部', '石川': '中部', '福井': '中部', '山梨': '中部', '長野': '中部', '岐阜': '中部', '静岡': '中部', '愛知': '中部', '三重': '近畿', '滋賀': '近畿', '京都': '近畿', '大阪': '近畿', '兵庫': '近畿', '奈良': '近畿', '和歌山': '近畿', '鳥取': '中国・四国', '島根': '中国・四国', '岡山': '中国・四国', '広島': '中国・四国', '山口': '中国・四国', '徳島': '中国・四国', '香川': '中国・四国', '愛媛': '中国・四国', '高知': '中国・四国', '福岡': '九州・沖縄', '佐賀': '九州・沖縄', '長崎': '九州・沖縄', '熊本': '九州・沖縄', '大分': '九州・沖縄', '宮崎': '九州・沖縄', '鹿児島': '九州・沖縄', '沖縄': '九州・沖縄' } region_colors = { '北海道・東北': '#4e9af1', '関東': '#e05c5c', '中部': '#f0a500', '近畿': '#5cb85c', '中国・四国': '#9b59b6', '九州・沖縄': '#f39c12', } def get_region(pref_name): """都道府県名(〇〇県など)から地域ブロックを返す""" short = pref_name.replace('県', '').replace('府', '').replace('都', '').replace('道', '') return region_map_raw.get(short, '関東') def short_name(pref_name): """都道府県名から短縮形(県・府・都・道を除去)""" return pref_name.replace('県', '').replace('府', '').replace('都', '').replace('道', '') df_b['地域ブロック'] = df_b['都道府県'].apply(get_region) df_b['都道府県短'] = df_b['都道府県'].apply(short_name) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。df[col](1列)と df[[col1, col2]](複数列)でカッコの数が違います。リストを渡していると覚えるとミスを減らせます。85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | # ── ジニ係数・変動係数の計算 ────────────────────────────────────────────────── def gini(arr): v = np.sort(arr) n = len(v) idx = np.arange(1, n + 1) return (2 * np.sum(idx * v) / (n * np.sum(v))) - (n + 1) / n gini_ts = df_b.groupby('年度')[cons_col].apply(lambda x: gini(x.dropna().values)) cv_ts = df_b.groupby('年度')[cons_col].apply(lambda x: x.std() / x.mean()) print("=== ジニ係数の時系列 ===") for yr, g in gini_ts.items(): print(f" {yr}: Gini={g:.4f}, CV={cv_ts[yr]:.4f}") print() |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。df.groupby('列').apply(関数) — グループごとに関数を適用。時系列や地域別の集計でよく使います。gini(arr) — Gini係数(0=完全平等、1=完全不平等)を計算。ソート → 累積和 → 公式という単純実装。.apply(lambda x: ...) — その場限りの無名関数を適用。短いロジックなら def せずに書けます。s[:-n]「末尾n文字を除く」/s[n:]「先頭n文字を除く」。スライス [start:stop:step] はリスト・タプル・文字列共通の基本ワザです。102 103 104 105 106 107 108 109 110 111 112 113 114 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 142 143 144 145 | # 最新年を特定(2022年まで=SSDSE-Bの完全年次) latest_year = 2022 df_latest = df_b[df_b['年度'] == latest_year].copy() print("=== 図1: 都道府県別消費支出ランキング ===") df1 = df_latest[['都道府県', '都道府県短', '地域ブロック', cons_col]].dropna().sort_values(cons_col) nat_avg_latest = df1[cons_col].mean() fig1, ax1 = plt.subplots(figsize=(11, 14)) colors_bar = [region_colors.get(r, '#999999') for r in df1['地域ブロック']] bars1 = ax1.barh(df1['都道府県短'], df1[cons_col] / 10000, color=colors_bar, alpha=0.85, edgecolor='white', linewidth=0.4) ax1.axvline(nat_avg_latest / 10000, color='black', linewidth=2.0, linestyle='--', label=f'全国平均({nat_avg_latest / 10000:.1f}万円)') for bar, val in zip(bars1, df1[cons_col]): ax1.text(bar.get_width() + 0.1, bar.get_y() + bar.get_height() / 2, f'{val / 10000:.1f}', va='center', fontsize=8) region_patches1 = [mpatches.Patch(color=c, label=r, alpha=0.85) for r, c in region_colors.items()] nat_line1 = plt.Line2D([0], [0], color='black', linewidth=2.0, linestyle='--', label=f'全国平均({nat_avg_latest / 10000:.1f}万円)') ax1.legend(handles=region_patches1 + [nat_line1], fontsize=9, loc='lower right', framealpha=0.9) ax1.set_title(f'図1 都道府県別消費支出ランキング({latest_year}年・地域色分け)', fontsize=13, fontweight='bold', pad=12) ax1.set_xlabel('消費支出(万円/月・二人以上世帯)', fontsize=11) ax1.set_xlim(0, df1[cons_col].max() / 10000 * 1.15) ax1.grid(axis='x', alpha=0.3) plt.tight_layout() fig1_path = os.path.join(FIG_DIR, '2018_H2_fig1.png') fig1.savefig(fig1_path, bbox_inches='tight') plt.close(fig1) print(f"saved: {fig1_path}") print(f" 最高: {df1.iloc[-1]['都道府県']} ({df1.iloc[-1][cons_col]/10000:.1f}万円)") print(f" 最低: {df1.iloc[0]['都道府県']} ({df1.iloc[0][cons_col]/10000:.1f}万円)") print(f" 全国平均: {nat_avg_latest/10000:.1f}万円") print() |
=== 利用可能な列 ===
['年度', '地域コード', '都道府県', '総人口', '総人口(男)', '総人口(女)', '日本人人口', '日本人人口(男)', '日本人人口(女)', '15歳未満人口', '15歳未満人口(男)', '15歳未満人口(女)', '15~64歳人口', '15~64歳人口(男)', '15~64歳人口(女)', '65歳以上人口', '65歳以上人口(男)', '65歳以上人口(女)', '出生数', '出生数(男)', '出生数(女)', '合計特殊出生率', '死亡数', '死亡数(男)', '死亡数(女)', '転入者数(日本人移動者)', '転入者数(日本人移動者)(男)', '転入者数(日本人移動者)(女)', '転出者数(日本人移動者)', '転出者数(日本人移動者)(男)', '転出者数(日本人移動者)(女)', '婚姻件数', '離婚件数', '年平均気温', '最高気温(日最高気温の月平均の最高値)', '最低気温(日最低気温の月平均の最低値)', '降水日数(年間)', '降水量(年間)', '着工建築物数', '着工建築物床面積', '旅館営業施設数(ホテルを含む)', '旅館営業施設客室数(ホテルを含む)', '標準価格(平均価格)(住宅地)', '標準価格(平均価格)(商業地)', '幼稚園数', '幼稚園教員数', '幼稚園在園者数', '小学校数', '小学校教員数', '小学校児童数', '中学校数', '中学校教員数', '中学校生徒数', '中学校卒業者数', '中学校卒業者のうち進学者数', '高等学校数', '高等学校教員数', '高等学校生徒数', '高等学校卒業者数', '高等学校卒業者のうち進学者数', '短期大学数', '大学数', '短期大学教員数', '大学教員数', '短期大学学生数', '大学学生数', '短期大学卒業者数', '短期大学卒業者のうち進学者数', '大学卒業者数', '大学卒業者のうち進学者数', '専修学校数', '各種学校数', '専修学校生徒数', '各種学校生徒数', '新規求職申込件数(一般)', '月間有効求職者数(一般)', '月間有効求人数(一般)', '充足数(一般)', '就職件数(一般)', '一般旅券発行件数', '延べ宿泊者数', '外国人延べ宿泊者数', '着工新設住宅戸数', '着工新設持家数', '着工新設貸家数', '着工新設分譲住宅数', '着工新設住宅床面積', '着工新設持家床面積', '着工新設分譲住宅床面積', '着工新設貸家床面積', 'ごみ総排出量(総量)', '1人1日当たりの排出量', 'ごみのリサイクル率', '一般病院数', '一般診療所数', '歯科診療所数', '保育所等数', '保育所等定員数', '保育所等利用待機児童数', '保育所等在所児数', '保育所等保育士数', '消費支出(二人以上の世帯)', '食料費(二人以上の世帯)', '住居費(二人以上の世帯)', '光熱・水道費(二人以上の世帯)', '家具・家事用品費(二人以上の世帯)', '被服及び履物費(二人以上の世帯)', '保健医療費(二人以上の世帯)', '交通・通信費(二人以上の世帯)', '教育費(二人以上の世帯)', '教養娯楽費(二人以上の世帯)', 'その他の消費支出(二人以上の世帯)']
=== データサマリー(主要変数) ===
年度 消費支出(二人以上の世帯) ... 大学進学率 都市集積度
count 564.000000 564.000000 ... 564.000000 564.000000
mean 2017.500000 287335.789007 ... 52.591527 1.554213
std 3.455117 23591.177050 ... 7.088938 0.384527
min 2012.000000 210593.000000 ... 37.668100 0.860518
25% 2014.750000 272849.750000 ... 46.692715 1.284842
50% 2017.500000 287054.000000 ... 52.023913 1.519534
75% 2020.250000 304501.750000 ... 57.280621 1.723869
max 2023.000000 355065.000000 ... 74.123750 3.152612
[8 rows x 6 columns]
=== ジニ係数の時系列 ===
2012: Gini=0.0422, CV=0.0754
2013: Gini=0.0396, CV=0.0709
2014: Gini=0.0476, CV=0.0855
2015: Gini=0.0463, CV=0.0834
2016: Gini=0.0482, CV=0.0853
2017: Gini=0.0486, CV=0.0870
2018: Gini=0.0475, CV=0.0864
2019: Gini=0.0493, CV=0.0910
2020: Gini=0.0448, CV=0.0808
2021: Gini=0.0464, CV=0.0836
2022: Gini=0.0371, CV=0.0662
2023: Gini=0.0444, CV=0.0816
=== 図1: 都道府県別消費支出ランキング ===
saved: html/figures/2018_H2_fig1.png
最高: 埼玉県 (32.5万円)
最低: 愛媛県 (24.5万円)
全国平均: 29.0万円fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。sort_values('列名', ascending=False) — 指定列で並べ替え(降順)。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。np.cumsum(arr) は累積和、np.linspace(a, b, n) は「aからbを等間隔でn個」。NumPyの定石です。47都道府県の消費支出を用いてジニ係数と変動係数(CV)を各年計算し、2012〜2022年の格差変化を追う。
| 年度 | ジニ係数 | 変動係数 | 特記事項 |
|---|---|---|---|
| 2012 | 0.0422 | 0.0754 | 基準年 |
| 2013 | 0.0396 | 0.0709 | アベノミクス初期・格差縮小 |
| 2014 | 0.0476 | 0.0855 | 消費税8%引き上げ(4月) |
| 2016 | 0.0482 | 0.0853 | 格差拡大傾向 |
| 2019 | 0.0493 | 0.0910 | ピーク(消費税10%引き上げ直前) |
| 2020 | 0.0448 | 0.0808 | COVID-19・一時的縮小 |
| 2021 | 0.0464 | 0.0836 | COVID継続期間 |
| 2022 | 0.0371 | 0.0662 | ジニ係数最小(格差縮小) |
ジニ係数は0(完全平等)から1(完全不平等)の値をとる格差指標。都道府県間消費格差の文脈では0.04前後という小さい値が得られるが、これは47都道府県という集計データのため内部分散が消えているから。
変動係数(CV = 標準偏差/平均)とジニ係数は両方とも格差を測る指標だが、異なる性質を持つ。CVは正規分布を仮定した指標で計算が簡単。ジニ係数はローレンツ曲線に基づく幾何的指標で、分布形状に敏感。
147 148 149 150 151 152 153 154 | print("=== 図2: 消費格差の時系列 ===") years_plot = [y for y in sorted(gini_ts.index) if 2012 <= y <= 2022] g_vals = [gini_ts[y] for y in years_plot] cv_vals = [cv_ts[y] for y in years_plot] fig2, ax2a = plt.subplots(figsize=(11, 6)) ax2b = ax2a.twinx() |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。df['A'] / df['B'] — pandasの列同士の四則演算は要素ごと(element-wise)。forループ不要なのが強み。155 156 157 158 159 160 161 162 | # COVID-19期間ハイライト(2020-2021) ax2a.axvspan(2019.5, 2021.5, alpha=0.13, color='gray', label='COVID-19期間(2020-2021)') line_g, = ax2a.plot(years_plot, g_vals, color='#1565C0', linewidth=2.5, marker='o', markersize=7, label='ジニ係数(左軸)', zorder=4) line_cv, = ax2b.plot(years_plot, cv_vals, color='#E65100', linewidth=2.5, marker='s', markersize=7, linestyle='--', label='変動係数 CV(右軸)', zorder=4) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。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 | # 値ラベル for x, g, cv in zip(years_plot, g_vals, cv_vals): ax2a.annotate(f'{g:.4f}', (x, g), textcoords='offset points', xytext=(0, 8), ha='center', fontsize=8, color='#1565C0') ax2b.annotate(f'{cv:.4f}', (x, cv), textcoords='offset points', xytext=(0, -14), ha='center', fontsize=8, color='#E65100') ax2a.set_ylabel('ジニ係数', fontsize=11, color='#1565C0') ax2b.set_ylabel('変動係数(CV)', fontsize=11, color='#E65100') ax2a.set_xlabel('年度', fontsize=11) ax2a.tick_params(axis='y', labelcolor='#1565C0') ax2b.tick_params(axis='y', labelcolor='#E65100') ax2a.set_xticks(years_plot) ax2a.set_xlim(2011.5, 2022.5) covid_patch = mpatches.Patch(color='gray', alpha=0.3, label='COVID-19期間(2020-2021)') ax2a.legend(handles=[line_g, line_cv, covid_patch], fontsize=10, loc='upper left', framealpha=0.9) ax2a.set_title('図2 都道府県間消費格差の時系列変化(2012-2022年)', fontsize=13, fontweight='bold', pad=12) ax2a.grid(axis='y', alpha=0.3) plt.tight_layout() fig2_path = os.path.join(FIG_DIR, '2018_H2_fig2.png') fig2.savefig(fig2_path, bbox_inches='tight') plt.close(fig2) print(f"saved: {fig2_path}") print(f" ジニ係数最大: {max(g_vals):.4f} ({years_plot[g_vals.index(max(g_vals))]}年)") print(f" ジニ係数最小: {min(g_vals):.4f} ({years_plot[g_vals.index(min(g_vals))]}年)") print() |
=== 図2: 消費格差の時系列 === saved: html/figures/2018_H2_fig2.png ジニ係数最大: 0.0493 (2019年) ジニ係数最小: 0.0371 (2022年)
[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。雇用の強さを示す有効求人倍率と消費支出の関係を、47都道府県の断面データ(2022年)で散布図を描く。各県を地域ブロック別に色分けし、OLS回帰直線を重ね合わせる。
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 | print("=== 図3: 有効求人倍率 vs 消費支出 散布図 ===") df3 = df_latest[['都道府県', '都道府県短', '地域ブロック', cons_col, '有効求人倍率']].dropna().copy() fig3, ax3 = plt.subplots(figsize=(12, 9)) for _, row in df3.iterrows(): region = row['地域ブロック'] color = region_colors.get(region, '#999999') ax3.scatter(row['有効求人倍率'], row[cons_col] / 10000, color=color, s=65, alpha=0.85, zorder=3) ax3.annotate(row['都道府県短'], xy=(row['有効求人倍率'], row[cons_col] / 10000), xytext=(3, 3), textcoords='offset points', fontsize=7.5, color='#333333') |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。for _, row in df.iterrows() — DataFrameを1行ずつ取り出すループ。1点ずつ描画したいときに使用。.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。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 | # 回帰直線 x_vals = df3['有効求人倍率'].values y_vals = df3[cons_col].values / 10000 slope3, intercept3, r3, p3, se3 = stats.linregress(x_vals, y_vals) x_line3 = np.linspace(x_vals.min(), x_vals.max(), 100) ax3.plot(x_line3, intercept3 + slope3 * x_line3, color='crimson', linewidth=2.2, zorder=4, label=f'回帰直線 (r={r3:.3f}, p={p3:.4f})') region_patches3 = [mpatches.Patch(color=c, label=r, alpha=0.85) for r, c in region_colors.items()] reg_line3 = plt.Line2D([0], [0], color='crimson', linewidth=2.2, label=f'回帰直線 r={r3:.3f}, p={p3:.4f}') ax3.legend(handles=region_patches3 + [reg_line3], fontsize=9, loc='upper left', framealpha=0.9) ax3.set_title(f'図3 有効求人倍率 vs 消費支出 都道府県別散布図({latest_year}年)', fontsize=13, fontweight='bold', pad=12) ax3.set_xlabel('有効求人倍率(倍)', fontsize=11) ax3.set_ylabel('消費支出(万円/月・二人以上世帯)', fontsize=11) ax3.grid(alpha=0.3) plt.tight_layout() fig3_path = os.path.join(FIG_DIR, '2018_H2_fig3.png') fig3.savefig(fig3_path, bbox_inches='tight') plt.close(fig3) print(f"saved: {fig3_path}") print(f" 有効求人倍率 vs 消費支出: r={r3:.3f}, p={p3:.6f}") print() |
=== 図3: 有効求人倍率 vs 消費支出 散布図 === saved: html/figures/2018_H2_fig3.png 有効求人倍率 vs 消費支出: r=-0.020, p=0.894803
stats.linregress(x, y) — 単回帰の傾き・切片・r値・p値・標準誤差を返します。使わない値は _ で受け取り。[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。4つの説明変数を用いたOLS重回帰分析を2022年断面(N=47)で実施する。変数間の係数の大きさを比較するため、全変数を標準化(平均0、標準偏差1)した上で回帰する(標準化偏回帰係数 β)。
| 説明変数 | 標準化偏回帰係数 β | p値 | 有意性 | 解釈 |
|---|---|---|---|---|
| 大学進学率 | 0.364 | 0.035 | * | 教育水準が高い県ほど消費支出が高い |
| 有効求人倍率 | 0.210 | 0.174 | n.s. | 正の効果だが有意でない(±0.5の95%CI) |
| 都市集積度 | 0.144 | 0.463 | n.s. | 転入率が高いほど消費高、有意でない |
| 高齢化率 | −0.129 | 0.525 | n.s. | 負の効果だが有意でない |
| モデル全体 F(4,42)=3.519 | 0.015 | * | モデル全体は有意 | |
| Adj.R²=0.180、N=47都道府県 | ||||
ジニ係数の時系列(図2)を見ると、2020年(COVID-19感染拡大元年)に格差が縮小し、2022年にはさらに縮小(Gini=0.0371:2012-2022年最小値)。
今回分析した都道府県間ジニ係数(≈0.04-0.05)は所得ジニ係数(個人レベル、≈0.3-0.4)と比べて極めて小さい。この「圧縮」の一因が地方交付税制度。財政力の弱い地方に国税を再分配することで、地域間の公共サービス格差を縮小し、消費格差も間接的に縮小させる。
237 238 239 240 241 242 243 244 245 246 247 248 249 250 | print("=== 重回帰分析(OLS) ===") df_reg = df_latest[['都道府県', cons_col] + X_cols].dropna().copy() y_reg = df_reg[cons_col].values X_reg = df_reg[X_cols].values # 標準化 y_std = (y_reg - y_reg.mean()) / y_reg.std() X_std = (X_reg - X_reg.mean(axis=0)) / X_reg.std(axis=0) X_sm = sm.add_constant(X_std) model = sm.OLS(y_std, X_sm).fit() print(model.summary()) print() |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。sm.add_constant(X) — 切片項(定数1の列)を先頭に追加。statsmodelsで必須。sm.OLS(y, X).fit() — 最小二乗法でモデルを推定。model.params, model.pvalues, model.conf_int() で結果取得。[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。251 252 253 254 255 256 257 258 259 260 261 262 | # 標準化偏回帰係数・95%CI・p値 coef_names = X_cols coefs = model.params[1:] ci_raw = model.conf_int() ci = pd.DataFrame(ci_raw[1:], index=coef_names) pvals = model.pvalues[1:] print("標準化偏回帰係数:") for nm, coef, pv in zip(coef_names, coefs, pvals): star = '***' if pv < 0.001 else ('**' if pv < 0.01 else ('*' if pv < 0.05 else 'n.s.')) print(f" {nm}: β={coef:.4f}, p={pv:.4f} {star}") print() |
=== 重回帰分析(OLS) ===
OLS Regression Results
==============================================================================
Dep. Variable: y R-squared: 0.251
Model: OLS Adj. R-squared: 0.180
Method: Least Squares F-statistic: 3.519
Date: Mon, 18 May 2026 Prob (F-statistic): 0.0145
Time: 11:23:20 Log-Likelihood: -59.897
No. Observations: 47 AIC: 129.8
Df Residuals: 42 BIC: 139.0
Df Model: 4
Covariance Type: nonrobust
==============================================================================
coef std err t P>|t| [0.025 0.975]
------------------------------------------------------------------------------
const -7.355e-16 0.134 -5.51e-15 1.000 -0.269 0.269
x1 0.2097 0.151 1.385 0.173 -0.096 0.515
x2 -0.1294 0.202 -0.641 0.525 -0.537 0.278
x3 0.3642 0.167 2.176 0.035 0.026 0.702
x4 0.1441 0.195 0.741 0.463 -0.248 0.537
==============================================================================
Omnibus: 1.845 Durbin-Watson: 1.832
Prob(Omnibus): 0.397 Jarque-Bera (JB): 1.288
Skew: -0.403 Prob(JB): 0.525
Kurtosis: 3.089 Cond. No. 2.94
==============================================================================
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
標準化偏回帰係数:
有効求人倍率: β=0.2097, p=0.1735 n.s.
高齢化率: β=-0.1294, p=0.5251 n.s.
大学進学率: β=0.3642, p=0.0353 *
都市集積度: β=0.1441, p=0.4630 n.s.r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。263 264 265 266 267 268 269 270 271 272 273 274 275 | print("=== 図4: 標準化偏回帰係数 ===") var_labels4 = { '有効求人倍率': '有効求人倍率\n(倍)', '高齢化率': '高齢化率\n(65歳以上%)', '大学進学率': '大学進学率\n(高卒→大学%)', '都市集積度': '都市集積度\n(転入率%)', } coef_arr = np.array(coefs) ci_lo = ci.iloc[:, 0].values ci_hi = ci.iloc[:, 1].values pvals_arr = np.array(pvals) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 | # 係数の大きい順にソート sort_idx = np.argsort(np.abs(coef_arr))[::-1] sorted_names = [coef_names[i] for i in sort_idx] sorted_labels = [var_labels4.get(coef_names[i], coef_names[i]) for i in sort_idx] sorted_coefs = coef_arr[sort_idx] sorted_cilo = ci_lo[sort_idx] sorted_cihi = ci_hi[sort_idx] sorted_pvals = pvals_arr[sort_idx] y_pos4 = np.arange(len(sorted_names)) fig4, ax4 = plt.subplots(figsize=(10, 6)) bar_colors4 = ['#1565C0' if c >= 0 else '#C62828' for c in sorted_coefs] bars4 = ax4.barh(y_pos4, sorted_coefs, color=bar_colors4, alpha=0.80, height=0.55, edgecolor='white') |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。x if cond else y は三項演算子。リスト内包表記と組み合わせると、forとifを1行で書けます。292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 | # 95% CI エラーバー xerr_lo = sorted_coefs - sorted_cilo xerr_hi = sorted_cihi - sorted_coefs ax4.errorbar(sorted_coefs, y_pos4, xerr=[xerr_lo, xerr_hi], fmt='none', color='black', capsize=5, linewidth=1.5, zorder=5) # 有意マーク for i, (coef, pv) in enumerate(zip(sorted_coefs, sorted_pvals)): star = '***' if pv < 0.001 else ('**' if pv < 0.01 else ('*' if pv < 0.05 else 'n.s.')) offset = 0.01 if coef >= 0 else -0.01 ha = 'left' if coef >= 0 else 'right' ax4.text(sorted_cihi[i] + 0.02, i, star, va='center', ha='left', fontsize=10, color='#C62828' if pv < 0.05 else '#999999', fontweight='bold') ax4.set_yticks(y_pos4) ax4.set_yticklabels(sorted_labels, fontsize=10) ax4.axvline(0, color='black', linewidth=1.0, linestyle='--') r2_adj = model.rsquared_adj ax4.set_title(f'図4 重回帰分析:標準化偏回帰係数と95%信頼区間(2022年断面)\n' f'Adj.R²={r2_adj:.3f} N=47都道府県', fontsize=12, fontweight='bold', pad=12) ax4.set_xlabel('標準化偏回帰係数(β)', fontsize=11) ax4.grid(axis='x', alpha=0.3) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。df[col](1列)と df[[col1, col2]](複数列)でカッコの数が違います。リストを渡していると覚えるとミスを減らせます。317 318 319 320 321 322 323 324 325 326 | # 凡例 pos_patch = mpatches.Patch(color='#1565C0', alpha=0.8, label='正の効果') neg_patch = mpatches.Patch(color='#C62828', alpha=0.8, label='負の効果') ax4.legend(handles=[pos_patch, neg_patch], fontsize=9, loc='lower right') plt.tight_layout() fig4_path = os.path.join(FIG_DIR, '2018_H2_fig4.png') fig4.savefig(fig4_path, bbox_inches='tight') plt.close(fig4) print(f"saved: {fig4_path}") |
=== 図4: 標準化偏回帰係数 === saved: html/figures/2018_H2_fig4.png
s[:-n]「末尾n文字を除く」/s[n:]「先頭n文字を除く」。スライス [start:stop:step] はリスト・タプル・文字列共通の基本ワザです。327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 | print() print("=== 結果サマリー ===") print(f"分析期間: 2012-2022年, 47都道府県") print() print(f"[消費格差(ジニ係数)推移]") for yr, g in zip(years_plot, g_vals): print(f" {yr}: {g:.4f}") print() print(f"[消費支出ランキング上位5 ({latest_year}年)]") top5_df = df1.tail(5) for _, rr in top5_df.iloc[::-1].iterrows(): print(f" {rr['都道府県']}: {rr[cons_col]/10000:.1f}万円") print() print(f"[消費支出ランキング下位5 ({latest_year}年)]") bot5_df = df1.head(5) for _, rr in bot5_df.iterrows(): print(f" {rr['都道府県']}: {rr[cons_col]/10000:.1f}万円") print() print(f"[重回帰結果 Adj.R²={r2_adj:.3f}]") for nm, coef, pv in zip(coef_names, coefs, pvals): star = '***' if pv < 0.001 else ('**' if pv < 0.01 else ('*' if pv < 0.05 else 'n.s.')) print(f" {nm}: β={coef:.4f}, p={pv:.4f} {star}") print() print("DONE: 2018_H2_yushu") |
=== 結果サマリー === 分析期間: 2012-2022年, 47都道府県 [消費格差(ジニ係数)推移] 2012: 0.0422 2013: 0.0396 2014: 0.0476 2015: 0.0463 2016: 0.0482 2017: 0.0486 2018: 0.0475 2019: 0.0493 2020: 0.0448 2021: 0.0464 2022: 0.0371 [消費支出ランキング上位5 (2022年)] 埼玉県: 32.5万円 東京都: 32.2万円 滋賀県: 31.9万円 愛知県: 31.9万円 富山県: 31.7万円 [消費支出ランキング下位5 (2022年)] 愛媛県: 24.5万円 青森県: 25.0万円 沖縄県: 25.2万円 和歌山県: 26.4万円 大阪府: 26.5万円 [重回帰結果 Adj.R²=0.180] 有効求人倍率: β=0.2097, p=0.1735 n.s. 高齢化率: β=-0.1294, p=0.5251 n.s. 大学進学率: β=0.3642, p=0.0353 * 都市集積度: β=0.1441, p=0.4630 n.s. DONE: 2018_H2_yushu
for _, row in df.iterrows() — DataFrameを1行ずつ取り出すループ。1点ずつ描画したいときに使用。x if cond else y は三項演算子。リスト内包表記と組み合わせると、forとifを1行で書けます。| データ | 出典 |
|---|---|
| SSDSE-B 都道府県データ(2012-2022年) | 統計数理研究所 SSDSE(社会・人口統計体系)2026年版 |
| 消費支出(二人以上の世帯) | 総務省 家計調査(都道府県庁所在地・政令指定都市平均) |
| 月間有効求人数・求職者数 | 厚生労働省 職業安定業務統計 |
| 高等学校卒業者・進学者数 | 文部科学省 学校基本調査 |
本教育用コードは SSDSE-B-2026.csv の実データのみを使用。合成データ(np.random.seed等)は一切使用していない。
統計分析の解釈で初心者がやりがちな勘違いをまとめます。特に「相関と因果の混同」「p値の過信」は研究現場でもよく起きる落とし穴です。本文を読む前にも、読んだ後にも、目を通してみてください。
統計の基本用語を初心者向けに解説します。本文中で見慣れない言葉が出てきたら、ここに戻って確認してください。
統計手法について「何のためか」「結果をどう読むか」を初心者向けに解説します。
この研究をさらに発展させるための3つの方向性を示します。「今回わかったこと(X)」から「次に検証すべき仮説(Y)」を立て、「具体的に何をするか(Z)」まで考えてみましょう。
学んだだけでは身につきません。実際に手を動かすのが最強の学習方法です。本論文のスクリプトをベースに、以下のチャレンジに挑戦してみてください。難易度別に5つ用意しました。
本論文で学んだ手法は、研究の世界だけでなく、行政・企業・NPO の現場でも様々に活用されています。具体的なシーンを紹介します。
この論文を読んで初心者が抱きやすい疑問に、教育的観点から答えます。