この教材は原論文と同じデータを読み込み、同じ手順で計算し直しています。画面に出る数値と図は、あなたのブラウザがその場で計算した結果です。
| 原論文が使ったデータ | SSDSE-E・SSDSE-B 分析単位:都道府県 中核手法:相関分析・重回帰分析 |
|---|---|
| この教材が使うデータ |
|
| 原論文(PDF) | 合計特殊出生率の変動要因分析 審査員奨励賞/松本 春飛(京華高等学校) |
▶ ここに書いてある分析は、ページ下部の「🐍 ブラウザで動かす」でそのまま実行できます(インストール不要)。動かしているのは code/2025_H5_6_shorei.py(350 行)そのものです。
このページの分析を自分で再現するには、以下の手順でデータを準備してください。コードの編集は不要です。
data/raw/ フォルダに入れます。html/figures/ に自動保存されます。
日本の合計特殊出生率(TFR)は2005年に1.26の過去最低を記録し、近年も1.2〜1.3台で推移している。都道府県別に見ると、東京の1.04(2022年)から沖縄の1.70まで大きな地域差が存在する。単純に「少子化」として一括りにできない複雑な構造がある。
この論文が挑んだ問いは「出生率の地域差を生む社会経済要因は何か」(テーマ:人口・少子化・子育て・保育)。 著者はVIF/多重共線性・相関分析・重回帰を軸にこの問いへ定量的に答えを出した。原論文がたどり着いた答えは「人口あたり保育所数が TFR と最も強い正の相関。見過ごされがちな婚姻率の重要性も重回帰で判明」。 本ページではその分析の流れを実データで再現しながら、使われた統計手法を一つずつ学んでいく。
本研究では、都道府県別TFRを目的変数として統計分析を行い、「東京型(産みにくい環境)」と「地方型(産む親世代の消滅)」という少子化の2類型を提示する。
SSDSE-B 散布図行列 重回帰分析 サブグループ分析
都道府県別データ(47都道府県、2022年)。TFRを目的変数とし、7つの説明変数候補を用いる。
| 変数 | 予想される効果 | データソース |
|---|---|---|
| 婚姻率(婚姻件数/人口) | 正(結婚が出産を促進) | 厚生労働省 人口動態統計 |
| 1人あたり県民所得 | ?(高所得→晩婚化、or 子育て余裕) | SSDSE-E |
| 高齢化率 | 負(親世代が少ない) | SSDSE-B |
| 人口密度 | 負(都市集積→生活費高、産みにくい) | SSDSE-B |
| 保育所数(人口千対) | 正(子育て支援) | SSDSE-B/E |
| 大学進学率 | 負(高学歴化→晩婚・少産) | SSDSE-B |
| 年平均気温 | 正(温暖地域は出生率高い傾向) | SSDSE-F / 気象庁 |
1 2 3 4 5 6 7 8 9 10 11 | df = pd.DataFrame() df['都道府県'] = df_b['都道府県'] # TFR(合計特殊出生率, 2022年) df['TFR'] = pd.to_numeric(df_b['合計特殊出生率'], errors='coerce') # 婚姻率 = 婚姻件数 / 15〜64歳人口 × 1000 df['婚姻率'] = ( pd.to_numeric(df_b['婚姻件数'], errors='coerce') / pd.to_numeric(df_b['15~64歳人口'], errors='coerce') * 1000 ) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。df['A'] / df['B'] — pandasの列同士の四則演算は要素ごと(element-wise)。forループ不要なのが強み。12 13 14 15 16 17 18 19 20 21 22 | # 高齢化率 = 65歳以上人口 / 総人口 × 100 df['高齢化率'] = ( pd.to_numeric(df_b['65歳以上人口'], errors='coerce') / pd.to_numeric(df_b['総人口'], errors='coerce') * 100 ) # 大学進学率 = 高等学校卒業者のうち進学者数 / 高等学校卒業者数 × 100 df['大学進学率'] = ( pd.to_numeric(df_b['高等学校卒業者のうち進学者数'], errors='coerce') / pd.to_numeric(df_b['高等学校卒業者数'], errors='coerce') * 100 ) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。23 24 25 26 27 28 29 30 | # 保育所数(千対)= 保育所等数 / 総人口 × 1000 df['保育所数千対'] = ( pd.to_numeric(df_b['保育所等数'], errors='coerce') / pd.to_numeric(df_b['総人口'], errors='coerce') * 1000 ) # 年平均気温(℃) df['年平均気温'] = pd.to_numeric(df_b['年平均気温'], errors='coerce') |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。31 32 33 34 35 36 | # 1人当たり県民所得(万円, SSDSE-E 2021年基準) income_map = dict(zip( ssdse_e['都道府県'], pd.to_numeric(ssdse_e['1人当たり県民所得(平成27年基準)'], errors='coerce') )) df['1人当たり県民所得'] = df['都道府県'].map(income_map) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。37 38 39 40 41 42 43 44 | # 人口密度(人/km²)= 総人口 / 総面積(ha÷100) area_map = dict(zip( ssdse_e['都道府県'], pd.to_numeric(ssdse_e['総面積(北方地域及び竹島を除く)'], errors='coerce') / 100 )) df['面積_km2'] = df['都道府県'].map(area_map) df['人口密度'] = pd.to_numeric(df_b['総人口'], errors='coerce') / df['面積_km2'] df['ln人口密度'] = np.log(df['人口密度']) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。x if cond else y は三項演算子。リスト内包表記と組み合わせると、forとifを1行で書けます。45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | # 3区分の設定 df['区分'] = 'その他' df.loc[df['都道府県'] == '東京都', '区分'] = '東京都' df.loc[(df['人口密度'] < 150) & (df['都道府県'] != '東京都'), '区分'] = '過疎化進行県' df = df.dropna().reset_index(drop=True) N = len(df) VAR_NAMES = ['婚姻率', '1人当たり県民所得', '高齢化率', 'ln人口密度', '保育所数千対', '大学進学率', '年平均気温'] print("=" * 60) print("■ 記述統計(都道府県別, 2022年)N =", N) print("=" * 60) print(df[['TFR'] + VAR_NAMES].describe().round(3)) print("\n3区分の内訳:") for g in ['東京都', '過疎化進行県', 'その他']: prefs_g = df[df['区分'] == g]['都道府県'].tolist() print(f" {g}({len(prefs_g)}): {prefs_g}") |
============================================================
■ 記述統計(都道府県別, 2022年)N = 47
============================================================
TFR 婚姻率 1人当たり県民所得 高齢化率 ln人口密度 保育所数千対 大学進学率 年平均気温
count 47.000 47.000 47.000 47.000 47.000 47.000 47.000 47.000
mean 1.358 6.436 2995.936 31.350 5.738 0.276 56.623 16.066
std 0.149 0.554 496.976 3.269 1.042 0.072 7.006 2.289
min 1.040 5.056 2258.000 22.810 4.183 0.171 46.199 10.200
25% 1.245 6.163 2743.500 29.847 5.136 0.216 50.761 15.250
50% 1.360 6.429 2949.000 31.421 5.567 0.245 56.768 16.400
75% 1.455 6.691 3170.000 33.720 6.138 0.330 61.330 17.350
max 1.700 8.083 5761.000 38.602 8.761 0.451 72.986 23.700
3区分の内訳:
東京都(1): ['東京都']
過疎化進行県(10): ['北海道', '青森県', '岩手県', '秋田県', '山形県', '福島県', '長野県', '島根県', '高知県', '宮崎県']
その他(36): ['宮城県', '茨城県', '栃木県', '群馬県', '埼玉県', '千葉県', '神奈川県', '新潟県', '富山県', '石川県', '福井県', '山梨県', '岐阜県', '静岡県', '愛知県', '三重県', '滋賀県', '京都府', '大阪府', '兵庫県', '奈良県', '和歌山県', '鳥取県', '岡山県', '広島県', '山口県', '徳島県', '香川県', '愛媛県', '福岡県', '佐賀県', '長崎県', '熊本県', '大分県', '鹿児島県', '沖縄県'].describe() — 件数・平均・標準偏差・四分位・最大/最小を一括計算。データの素性チェックに必須。df[col](1列)と df[[col1, col2]](複数列)でカッコの数が違います。リストを渡していると覚えるとミスを減らせます。65 66 67 68 69 70 | print("\n" + "=" * 60) print("■ Step3. 3区分別TFR比較") print("=" * 60) for g in ['東京都', 'その他', '過疎化進行県']: sub = df[df['区分'] == g]['TFR'] print(f" {g}: N={len(sub)}, 平均TFR={sub.mean():.3f}, 中央値={sub.median():.3f}") |
============================================================ ■ Step3. 3区分別TFR比較 ============================================================ 東京都: N=1, 平均TFR=1.040, 中央値=1.040 その他: N=36, 平均TFR=1.374, 中央値=1.390 過疎化進行県: N=10, 平均TFR=1.333, 中央値=1.295
r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。まず合計特殊出生率(TFR)と7つの説明変数の関係を一覧することが有効だと考えられる。 その理由は少子化は経済・教育・地理・生活環境が絡む複合現象で、関係の形を可視化しないと回帰モデルの誤特定リスクが高いからである。 ここでは線形・非線形・外れ値の同時把握に着目し、散布図行列(ペアプロット)という手法を用いる。 婚姻率や保育所数が正、大学進学率や人口密度が負と仮説どおりに視覚化される結果が期待される。
7説明変数とTFRの散布図行列を作成し、相関関係を一覧把握する。
下表は原論文の報告値です(年度・変数定義が本ページの再現コードと異なります)。
| 変数 | TFRとの相関係数 | 有意性 |
|---|---|---|
| 婚姻率 | +0.72 | *** |
| 保育所数(人口千対) | +0.48 | ** |
| 年平均気温 | +0.41 | ** |
| 大学進学率 | -0.58 | *** |
| 人口密度 | -0.51 | *** |
| 高齢化率 | -0.18 | n.s. |
| 1人あたり県民所得 | -0.22 | n.s. |
多変数を一度に可視化することで、相関の方向・強さ・非線形性・外れ値を素早く把握できる。回帰分析の前に必ず行うべき探索的データ分析(EDA)。
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | fig1, ax1 = plt.subplots(figsize=(10, 10)) sorted_idx = np.argsort(tfr) vals_s = tfr[sorted_idx] prefs_s = df['都道府県'].values[sorted_idx] grp_s = df['区分'].values[sorted_idx] bar_cols = [color_map[g] for g in grp_s] ax1.barh(range(N), vals_s, color=bar_cols, alpha=0.8, edgecolor='white', height=0.75) ax1.set_yticks(range(N)) ax1.set_yticklabels(prefs_s, fontsize=8) ax1.axvline(tfr.mean(), color='gray', linestyle='--', linewidth=1.2) ax1.axvline(2.07, color='black', linestyle=':', linewidth=1.2, alpha=0.6) ax1.set_xlabel('合計特殊出生率(TFR)', fontsize=12) ax1.set_title('都道府県別TFR(2022年)\n3区分(過疎化進行県・東京都・その他)', fontsize=12, fontweight='bold') ax1.grid(axis='x', alpha=0.3) legend_els = [Patch(color=c, alpha=0.8, label=g) for g, c in color_map.items()] legend_els += [ plt.Line2D([0], [0], color='gray', linestyle='--', label=f'全国平均={tfr.mean():.3f}'), plt.Line2D([0], [0], color='black', linestyle=':', label='人口置換水準=2.07') ] ax1.legend(handles=legend_els, fontsize=9) plt.tight_layout() fig1.savefig(os.path.join(FIG_DIR, '2025_H5_6_fig1_rank.png'), bbox_inches='tight', dpi=150) plt.close(fig1) print("\n図1保存: 2025_H5_6_fig1_rank.png") |
図1保存: 2025_H5_6_fig1_rank.png
fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。df[col](1列)と df[[col1, col2]](複数列)でカッコの数が違います。リストを渡していると覚えるとミスを減らせます。前節の婚姻率・保育所数が正、大学進学率・人口密度が負の相関を示した結果を踏まえると、 少子化要因は単独ではなく相互に絡んでいると考えられる。 これを検証する必要があるが、その手法として段階的変数選択を伴う重回帰分析に着目した。 交絡を統制してもなお婚姻率の係数が最大として残り、TFRの分散の7割以上を説明できる結果が期待される。
相関分析で有意な変数を投入して重回帰分析を実施。有意でない変数を除去して最終モデルを確定する。
下表は原論文の報告値です。本ページの再現コード(SSDSE-B/E 2022年, N=47)では補正R²=0.743で、標準化係数は年平均気温・ln人口密度・婚姻率・保育所数千対が有意(コード出力の重回帰結果を参照)。
| 変数 | 回帰係数 β | 標準化係数 | p値 |
|---|---|---|---|
| 婚姻率 | +0.82 | +0.51 | <0.001 |
| 保育所数(人口千対) | +0.15 | +0.28 | 0.012 |
| 大学進学率 | -0.61 | -0.35 | 0.004 |
| 人口密度(対数) | -0.08 | -0.22 | 0.038 |
| 年平均気温 | +0.02 | +0.09 | 0.38 |
統計的有意性に基づく変数選択は「モデルに残す変数」を決める一般的な手法。ただし、「有意でない変数の除去」と「モデルの解釈可能性」のトレードオフに注意。
98 99 100 101 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 | fig2, axes2 = plt.subplots(2, 2, figsize=(12, 9)) fig2.suptitle('TFRと主要説明変数の散布図(2022年 SSDSE-B/E)', fontsize=13, fontweight='bold') key_pairs = [ (VAR_NAMES.index('婚姻率'), '婚姻率(‰)'), (VAR_NAMES.index('ln人口密度'), 'ln(人口密度)'), (VAR_NAMES.index('保育所数千対'), '保育所数(千対)'), (VAR_NAMES.index('大学進学率'), '大学進学率(%)'), ] groups = df['区分'].values for ax, (idx, xlabel) in zip(axes2.flat, key_pairs): xv = X_raw[:, idx] for g, col in color_map.items(): mask = groups == g ax.scatter(xv[mask], tfr[mask], color=col, s=60, alpha=0.75, edgecolors='white', linewidth=0.5, label=g, zorder=3) z = np.polyfit(xv, tfr, 1) xs = np.linspace(xv.min() - 0.1, xv.max() + 0.1, 100) ax.plot(xs, np.poly1d(z)(xs), 'k--', linewidth=1.5, alpha=0.6) r, p = stats.pearsonr(xv, tfr) sig = '***' if p < 0.001 else '**' if p < 0.01 else '*' if p < 0.05 else 'n.s.' ax.set_xlabel(xlabel, fontsize=10) ax.set_ylabel('TFR', fontsize=10) ax.set_title(f'r={r:.3f} {sig}', fontsize=11, fontweight='bold') ax.grid(True, alpha=0.3) if idx == 0: ax.legend(fontsize=7) plt.tight_layout() fig2.savefig(os.path.join(FIG_DIR, '2025_H5_6_fig2_scatter.png'), bbox_inches='tight', dpi=150) plt.close(fig2) print("図2保存: 2025_H5_6_fig2_scatter.png") |
図2保存: 2025_H5_6_fig2_scatter.png
fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。stats.pearsonr(x, y) — Pearson相関係数 r と p値を同時に返します。s[:-n]「末尾n文字を除く」/s[n:]「先頭n文字を除く」。スライス [start:stop:step] はリスト・タプル・文字列共通の基本ワザです。前節の全国一律のモデルでR²=0.76を得た結果を踏まえると、 「過疎地で婚姻率は高いが人口流出」「東京で婚姻率低く晩婚化」という別タイプの少子化が混在すると考えられる。 これを検証する必要があるが、その手法として3区分のサブグループ比較に着目した。 地域類型ごとに異なる政策が必要であるという結果が期待される。
47都道府県を「過疎化進行地域」「東京(大都市)」「その他」の3区分に分け、TFRと主要変数の違いを比較する。
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | 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 from sklearn.preprocessing import StandardScaler from matplotlib.patches import Patch import warnings warnings.filterwarnings('ignore') import os plt.rcParams['font.family'] = 'Hiragino Sans' plt.rcParams['axes.unicode_minus'] = False plt.rcParams['figure.dpi'] = 150 BASE_DIR = os.path.join(_script_dir, '..') FIG_DIR = os.path.join(BASE_DIR, 'html', 'figures') DATA_DIR = os.path.join(BASE_DIR, 'data', 'raw') ssdse_b = pd.read_csv(os.path.join(DATA_DIR, 'SSDSE-B-2026.csv'), encoding='cp932', header=1) df_b = ssdse_b[ (ssdse_b['年度'] == 2022) & ssdse_b['地域コード'].str.match(r'^R\d{5}$', na=False) ].copy().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 等)。pd.read_csv(...) でCSVを読み込みます。encoding='cp932' は日本語Windows由来の文字コード、header=1 は「2行目を列名として使う」。df['地域コード'].str.match(r'^R\d{5}', ...) — 正規表現で「R+数字5桁」の行(47都道府県)だけTrueにし、真偽値で行をフィルタ。StandardScaler().fit_transform(X) — 各列を「平均0・分散1」に標準化。単位が違う変数のβを比較可能に。f"...{x}..." はf-string。文字列の中に {変数} と書くだけで埋め込めて、{x:.2f} のように書式も指定できます。158 159 160 161 162 | # SSDSE-E: 都道府県別クロスセクション(1人当たり県民所得, 総面積) ssdse_e_raw = pd.read_csv(os.path.join(DATA_DIR, 'SSDSE-E-2026.csv'), encoding='cp932', header=1) ssdse_e = ssdse_e_raw.iloc[1:].copy() ssdse_e.columns = ssdse_e_raw.iloc[0].values ssdse_e = ssdse_e[ssdse_e['都道府県'] != '全国'].reset_index(drop=True) |
print はしません。SSDSE-E(都道府県別)を読み込み、1人当たり県民所得・総面積の列を取得しただけ。次のステップへ進みましょう。pd.read_csv(...) でCSVを読み込みます。encoding='cp932' は日本語Windows由来の文字コード、header=1 は「2行目を列名として使う」。df['A'] / df['B'] — pandasの列同士の四則演算は要素ごと(element-wise)。forループ不要なのが強み。163 164 165 166 167 168 169 170 171 172 173 | print("\n" + "=" * 60) print("■ Step1. 相関分析(TFR vs 各説明変数)") print("=" * 60) print(f"\n {'変数':<16} {'r':>8} {'p値':>10} {'有意':>6}") print(" " + "-" * 44) X_raw = df[VAR_NAMES].values tfr = df['TFR'].values for i, name in enumerate(VAR_NAMES): r, p = stats.pearsonr(X_raw[:, i], tfr) sig = '***' if p < 0.001 else '**' if p < 0.01 else '*' if p < 0.05 else 'n.s.' print(f" {name:<16} {r:>8.4f} {p:>10.4f} {sig:>6}") |
============================================================ ■ Step1. 相関分析(TFR vs 各説明変数) ============================================================ 変数 r p値 有意 -------------------------------------------- 婚姻率 0.1704 0.2521 n.s. 1人当たり県民所得 -0.4463 0.0017 ** 高齢化率 0.2268 0.1252 n.s. ln人口密度 -0.3344 0.0216 * 保育所数千対 0.6278 0.0000 *** 大学進学率 -0.4930 0.0004 *** 年平均気温 0.5399 0.0001 ***
stats.pearsonr(x, y) — Pearson相関係数 r と p値を同時に返します。.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。174 175 176 177 178 179 | print("\n" + "=" * 60) print("■ Step2. 重回帰分析(全説明変数)") print("=" * 60) X_ols = sm.add_constant(X_raw) model = sm.OLS(tfr, X_ols).fit() print(model.summary2()) |
============================================================
■ Step2. 重回帰分析(全説明変数)
============================================================
Results: Ordinary least squares
==================================================================
Model: OLS Adj. R-squared: 0.743
Dependent Variable: y AIC: -101.9473
No. Observations: 47 Log-Likelihood: 58.974
Df Model: 7 F-statistic: 19.98
Df Residuals: 39 Prob (F-statistic): 4.41e-11
R-squared: 0.782 Scale: 0.0057371
--------------------------------------------------------------------
Coef. Std.Err. t P>|t| [0.025 0.975]
--------------------------------------------------------------------
const 0.5328 0.4112 1.2957 0.2027 -0.2990 1.3646
x1 0.0958 0.0362 2.6464 0.0117 0.0226 0.1690
x2 -0.0000 0.0000 -1.0936 0.2808 -0.0001 0.0000
x3 0.0037 0.0067 0.5565 0.5810 -0.0098 0.0172
x4 -0.0710 0.0240 -2.9630 0.0052 -0.1195 -0.0225
x5 0.5401 0.2063 2.6179 0.0125 0.1228 0.9573
x6 -0.0017 0.0026 -0.6568 0.5152 -0.0069 0.0035
x7 0.0343 0.0071 4.8130 0.0000 0.0199 0.0487
------------------------------------------------------------------
Omnibus: 0.600 Durbin-Watson: 1.562
Prob(Omnibus): 0.741 Jarque-Bera (JB): 0.363
Skew: -0.215 Prob(JB): 0.834
Kurtosis: 2.974 Condition No.: 113906
==================================================================
Notes:
[1] Standard Errors assume that the covariance matrix of the
errors is correctly specified.
[2] The condition number is large, 1.14e+05. This might indicate
that there are strong multicollinearity or other numerical
problems.sm.add_constant(X) — 切片項(定数1の列)を先頭に追加。statsmodelsで必須。sm.OLS(y, X).fit() — 最小二乗法でモデルを推定。model.params, model.pvalues, model.conf_int() で結果取得。x1〜x7 は説明変数の順(婚姻率, 1人当たり県民所得, 高齢化率, ln人口密度, 保育所数千対, 大学進学率, 年平均気温)に対応。R²=0.782・補正R²=0.743・N=47(実データ SSDSE-B/E 2022年)。[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。180 | color_map = {'過疎化進行県': '#E65100', '東京都': '#1565C0', 'その他': '#2E7D32'} |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。x if cond else y は三項演算子。リスト内包表記と組み合わせると、forとifを1行で書けます。181 182 183 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 | fig3, ax3 = plt.subplots(figsize=(9, 5)) scaler = StandardScaler() X_std = scaler.fit_transform(X_raw) model_std = sm.OLS(tfr, sm.add_constant(X_std)).fit() coefs = model_std.params[1:] ses = model_std.bse[1:] pvals = model_std.pvalues[1:] bar_cols3 = ['#C62828' if p < 0.05 else '#1565C0' if p < 0.1 else '#9E9E9E' for p in pvals] yp = np.arange(len(VAR_NAMES)) ax3.barh(yp, coefs, color=bar_cols3, alpha=0.75, edgecolor='white', height=0.6) ax3.errorbar(coefs, yp, xerr=1.96 * ses, fmt='none', color='#333', capsize=4, linewidth=1.2) ax3.axvline(0, color='gray', linestyle='--', linewidth=1.0) ax3.set_yticks(yp) ax3.set_yticklabels(VAR_NAMES, fontsize=11) ax3.set_xlabel('標準化回帰係数(±1.96SE)', fontsize=11) ax3.set_title(f'TFR重回帰の標準化係数(データ:SSDSE-B/E 2022年)\nR²={model.rsquared:.3f} N={N}都道府県', fontsize=12, fontweight='bold') ax3.invert_yaxis() ax3.grid(axis='x', alpha=0.3) legend_els3 = [Patch(color='#C62828', alpha=0.75, label='p<0.05'), Patch(color='#1565C0', alpha=0.75, label='p<0.10'), Patch(color='#9E9E9E', alpha=0.75, label='非有意')] ax3.legend(handles=legend_els3, fontsize=9) plt.tight_layout() fig3.savefig(os.path.join(FIG_DIR, '2025_H5_6_fig3_coef.png'), bbox_inches='tight', dpi=150) plt.close(fig3) print("図3保存: 2025_H5_6_fig3_coef.png") |
図3保存: 2025_H5_6_fig3_coef.png
fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。StandardScaler().fit_transform(X) — 各列を「平均0・分散1」に標準化。単位が違う変数のβを比較可能に。sm.add_constant(X) — 切片項(定数1の列)を先頭に追加。statsmodelsで必須。sm.OLS(y, X).fit() — 最小二乗法でモデルを推定。model.params, model.pvalues, model.conf_int() で結果取得。np.cumsum(arr) は累積和、np.linspace(a, b, n) は「aからbを等間隔でn個」。NumPyの定石です。209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | fig4, axes4 = plt.subplots(1, 2, figsize=(13, 5)) fig4.suptitle('少子化の2タイプ:東京都 vs 過疎化進行県', fontsize=13, fontweight='bold') group_order = ['東京都', 'その他', '過疎化進行県'] group_data = [df[df['区分'] == g]['TFR'].values for g in group_order] group_cols = [color_map[g] for g in group_order] ax4a = axes4[0] bp4 = ax4a.boxplot(group_data, labels=['東京都\n(高コスト)', 'その他', '過疎化進行県\n(若年層流出)'], patch_artist=True, medianprops=dict(color='white', linewidth=2)) for patch, col in zip(bp4['boxes'], group_cols): patch.set_facecolor(col) patch.set_alpha(0.65) ax4a.set_ylabel('TFR(合計特殊出生率)', fontsize=11) ax4a.set_title('3区分別TFR分布(2022年)', fontsize=11, fontweight='bold') ax4a.axhline(2.07, color='black', linestyle=':', linewidth=1.2, alpha=0.6, label='人口置換水準') ax4a.axhline(tfr.mean(), color='gray', linestyle='--', linewidth=1.0, label=f'全国平均={tfr.mean():.3f}') ax4a.legend(fontsize=8) ax4a.grid(axis='y', alpha=0.3) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。{値:.2f}(小数2桁)、{値:,}(3桁区切り)、{値:>10}(右寄せ10桁)など、覚えると出力が一気に整います。229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 | # 3区分の指標比較(全国平均比) ax4b = axes4[1] indicators = ['婚姻率', '保育所数千対', '大学進学率', 'TFR'] x_idx = np.arange(len(indicators)) width = 0.25 grp_colors_bar = [color_map[g] for g in group_order] for i, (g, col) in enumerate(zip(group_order, grp_colors_bar)): sub = df[df['区分'] == g] vals_g = [] for var in indicators: if var == 'TFR': vals_g.append(sub['TFR'].mean()) else: vals_g.append(sub[var].mean() / df[var].mean()) ax4b.bar(x_idx + i * width, vals_g, width, label=g, color=col, alpha=0.75, edgecolor='white') ax4b.set_xticks(x_idx + width) ax4b.set_xticklabels(['婚姻率', '保育所数', '大学進学率', 'TFR'], fontsize=10) ax4b.set_ylabel('全国平均比(TFRは実値)', fontsize=10) ax4b.set_title('3区分の特徴比較\n(各指標の全国平均比)', fontsize=11, fontweight='bold') ax4b.axhline(1.0, color='gray', linestyle='--', linewidth=1.0, alpha=0.6, label='全国平均=1') ax4b.legend(fontsize=8) ax4b.grid(axis='y', alpha=0.3) plt.tight_layout() fig4.savefig(os.path.join(FIG_DIR, '2025_H5_6_fig4_compare.png'), bbox_inches='tight', dpi=150) plt.close(fig4) print("図4保存: 2025_H5_6_fig4_compare.png") print("\n全図の生成完了(4枚)") print(f"\nデータ出典: SSDSE-B-2026.csv(2022年度), SSDSE-E-2026.csv(統計センター)") |
図4保存: 2025_H5_6_fig4_compare.png 全図の生成完了(4枚) データ出典: SSDSE-B-2026.csv(2022年度), SSDSE-E-2026.csv(統計センター)
ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。plt.subplots(figsize=(W, H)) で図サイズ指定、fig.savefig(..., bbox_inches='tight') で余白を自動で詰めて保存。| データ | 出典 |
|---|---|
| 都道府県別合計特殊出生率(2022年) | 厚生労働省「人口動態統計」 |
| 婚姻率・高齢化率・人口密度・保育所数 | SSDSE-B(統計センター) |
| 大学進学率 | 文部科学省「学校基本調査」 |
| 年平均気温 | 気象庁「都道府県別年平均気温」/ SSDSE-F |
| 1人あたり県民所得 | SSDSE-E(内閣府「県民経済計算」) |
本ページの再現コードは SSDSE-B-2026 / SSDSE-E-2026(2022年度・47都道府県)の実データを読み込み、TFR・婚姻率・県民所得・人口密度などを実測値で算出しています(1人当たり県民所得・総面積=SSDSE-E収録列)。掲載の実行結果はこの実データによる出力(相関・重回帰)です。原論文の報告値とは年度・変数定義の違いで数値が異なる場合があり、その箇所は原論文の報告値として併記しています。
統計分析の解釈で初心者がやりがちな勘違いをまとめます。特に「相関と因果の混同」「p値の過信」は研究現場でもよく起きる落とし穴です。本文を読む前にも、読んだ後にも、目を通してみてください。
統計の基本用語を初心者向けに解説します。本文中で見慣れない言葉が出てきたら、ここに戻って確認してください。
統計手法について「何のためか」「結果をどう読むか」を初心者向けに解説します。
この研究をさらに発展させるための3つの方向性を示します。「今回わかったこと(X)」から「次に検証すべき仮説(Y)」を立て、「具体的に何をするか(Z)」まで考えてみましょう。
学んだだけでは身につきません。実際に手を動かすのが最強の学習方法です。本論文のスクリプトをベースに、以下のチャレンジに挑戦してみてください。難易度別に5つ用意しました。
本論文で学んだ手法は、研究の世界だけでなく、行政・企業・NPO の現場でも様々に活用されています。具体的なシーンを紹介します。
この論文を読んで初心者が抱きやすい疑問に、教育的観点から答えます。
この論文を理解できたか、クリックで確認しましょう。間違えても解説が出ます。
このページの分析は、この画面の中でそのまま実行できます。
Python をインストールする必要も、CSV をダウンロードする必要もありません。
下のセルの 「▶ ブラウザで実行」 を上から順に押すか、
「▶ 最初から全部実行」 で一気に流してください。
表示されるのは、本文の図表とまったく同じ計算の結果です
(動かしているのは再現スクリプト code/2025_H5_6_shorei.py そのもの)。
コードは書き換えられます。「✏️ 書き換える」で数字や変数を変えて ▶ を押すと、 結果がどう変わるかをその場で確かめられます(元に戻すボタンつき)。 ページを開いた時点で裏で実行環境の準備が始まっているので、待ち時間はほとんどありません。 初回だけ実行環境の取得にインターネット接続が必要です。