このページの分析を自分で再現するには、以下の手順でデータを準備してください。コードの編集は不要です。
data/raw/ フォルダに入れます。html/figures/ に自動保存されます。
日本の多くの地方では、人口減少と高齢化が同時に進行する「過疎化」が深刻な問題となっている。本研究は、福祉施設の充実が過疎化対策に有効であるという仮説を、都道府県データの重回帰分析で検証した。
まず「福祉支援を通じた過疎化対策の提案」を統計的にとらえることが有効だと考えられる。 その理由は感覚や経験則だけでは、複雑な社会要因の中で「何が本当に効いているか」を見極めにくいからである。 本研究では公開データと統計手法を組み合わせ、この問いに定量的な答えを出すことを目指す。
SSDSE-B 重回帰分析 VIF 政策提言
| 区分 | 変数名 | 出典 |
|---|---|---|
| 目的変数 | 人口増加率(5年間) | 国勢調査 |
| 福祉施設の利用者割合(人口比) | SSDSE-E | |
| 説明変数 | 正規雇用者割合 | SSDSE-B |
| 失業者割合 | SSDSE-B | |
| 学校数(人口比) | SSDSE-B | |
| 一般病院・診療所数 | SSDSE-B | |
| 1人あたり所得 | SSDSE-E | |
| 標準地価(住宅地) | 国土交通省 | |
| 都道府県道延長 | 国土交通省 | |
| 1世帯あたり自家用車所有台数 | 自動車検査登録情報協会 | |
| 保育所等数 | SSDSE-E | |
| 児童・老人・社会福祉施設数 | SSDSE-B |
1 2 3 4 5 6 7 8 | print("=" * 65) print("■ データ読み込み(SSDSE-B-2026、実データ)") print("=" * 65) df_b = pd.read_csv(DATA_PATH, encoding='cp932', header=1) # 都道府県行のみ抽出(地域コード R + 5桁数字) df_b = df_b[df_b['地域コード'].str.match(r'^R\d{5}$', na=False)].copy() |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。pd.read_csv(...) でCSVを読み込みます。encoding='cp932' は日本語Windows由来の文字コード、header=1 は「2行目を列名として使う」。df['地域コード'].str.match(r'^R\d{5}', ...) — 正規表現で「R+数字5桁」の行(47都道府県)だけTrueにし、真偽値で行をフィルタ。df['A'] / df['B'] — pandasの列同士の四則演算は要素ごと(element-wise)。forループ不要なのが強み。9 10 11 12 13 14 15 16 17 18 19 20 21 | # 列名を変数コードへマッピング(実列名を使用) COL = { 'A1101': '総人口', 'A1302': '15~64歳人口', 'F3102': '月間有効求職者数(一般)', 'F3105': '就職件数(一般)', 'E2101': '小学校数', 'I510120': '一般病院数', 'L3221': '消費支出(二人以上の世帯)', 'C5401': '標準価格(平均価格)(住宅地)', 'J2503': '保育所等数', 'J2506': '保育所等在所児数', } |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。22 23 24 25 26 27 28 29 30 | # 必要列を数値化 for code, colname in COL.items(): df_b[colname] = pd.to_numeric(df_b[colname], errors='coerce') # ── 2022 年断面 ────────────────────────────────────────────── df22 = df_b[df_b['年度'] == 2022].copy().reset_index(drop=True) df21 = df_b[df_b['年度'] == 2021][['都道府県', '総人口']].rename( columns={'総人口': '総人口_2021'} ) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。31 32 33 34 35 36 37 38 | # ── 目的変数①:人口増加率(2021→2022)────────────────────── df22 = df22.merge(df21, on='都道府県', how='left') df22['pop_growth'] = ( (df22['総人口'] - df22['総人口_2021']) / df22['総人口_2021'] * 100 ) # ── 目的変数②:保育所利用率(在所児数 / 総人口 × 1000)─────── df22['welfare_rate'] = df22['保育所等在所児数'] / df22['総人口'] * 1000 |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。39 40 41 42 43 44 | # ── 説明変数 ───────────────────────────────────────────────── # 正規雇用率 proxy: 就職件数 / 月間有効求職者数 × 100 df22['empl_rate'] = df22['就職件数(一般)'] / df22['月間有効求職者数(一般)'] * 100 # 失業率 proxy: 月間有効求職者数 / 15-64歳人口 × 100 df22['unemp_rate'] = df22['月間有効求職者数(一般)'] / df22['15~64歳人口'] * 100 |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。x if cond else y は三項演算子。リスト内包表記と組み合わせると、forとifを1行で書けます。45 46 47 48 49 50 51 52 | # 学校数(人口比): 小学校数 / 総人口 × 10000 df22['school_per10k'] = df22['小学校数'] / df22['総人口'] * 10000 # 病院数(人口比): 一般病院数 / 総人口 × 10000 df22['hospital_per10k'] = df22['一般病院数'] / df22['総人口'] * 10000 # 一人当たり消費支出(直接使用) df22['consumption'] = df22['消費支出(二人以上の世帯)'] |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。df[col](1列)と df[[col1, col2]](複数列)でカッコの数が違います。リストを渡していると覚えるとミスを減らせます。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 | # 標準地価(住宅地) df22['land_price'] = df22['標準価格(平均価格)(住宅地)'] # 保育所数(人口比): 保育所等数 / 総人口 × 10000 df22['daycare_per10k'] = df22['保育所等数'] / df22['総人口'] * 10000 # ── 分析データフレーム構築 ──────────────────────────────────── EXPLAIN_VARS = [ 'empl_rate', 'unemp_rate', 'school_per10k', 'hospital_per10k', 'consumption', 'land_price', 'daycare_per10k', ] EXPLAIN_LABELS = { 'empl_rate': '正規雇用率(就職/求職)', 'unemp_rate': '失業率 proxy(求職/15-64歳)', 'school_per10k': '小学校数(1万人比)', 'hospital_per10k': '一般病院数(1万人比)', 'consumption': '一人当たり消費支出', 'land_price': '標準地価(住宅地)', 'daycare_per10k': '保育所数(1万人比)', } TARGET_VARS = ['pop_growth', 'welfare_rate'] all_vars = TARGET_VARS + EXPLAIN_VARS df_ana = df22[['都道府県'] + all_vars].dropna().reset_index(drop=True) print(f"分析対象: {len(df_ana)} 都道府県(欠損除去後)") print(f"年度: 2022年(人口増加率は 2021→2022 年変化率)") print() print("記述統計(目的変数):") print(df_ana[TARGET_VARS].rename( columns={'pop_growth': '人口増加率(%)', 'welfare_rate': '保育所利用率(‰)'} ).describe().round(4)) |
=================================================================
■ データ読み込み(SSDSE-B-2026、実データ)
=================================================================
分析対象: 47 都道府県(欠損除去後)
年度: 2022年(人口増加率は 2021→2022 年変化率)
記述統計(目的変数):
人口増加率(%) 保育所利用率(‰)
count 47.0000 47.0000
mean -0.7173 23.2798
std 0.4089 5.0314
min -1.5873 15.4206
25% -1.0660 19.1692
50% -0.7265 22.8666
75% -0.4331 26.0339
max 0.1999 38.8522.describe() — 件数・平均・標準偏差・四分位・最大/最小を一括計算。データの素性チェックに必須。s[:-n]「末尾n文字を除く」/s[n:]「先頭n文字を除く」。スライス [start:stop:step] はリスト・タプル・文字列共通の基本ワザです。重回帰分析の前に、説明変数間の相関(多重共線性)を確認する。相関が高い変数が複数あると回帰係数の推定が不安定になる。VIF(分散拡大係数)はその程度を数値化する指標。
VIFは各説明変数を他の説明変数で回帰したときのR²から計算される。statsmodelsでは variance_inflation_factor 関数が利用可能。
86 87 88 89 90 91 92 93 94 95 96 97 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 | print("図1: 相関ヒートマップを作成中...") all_corr_vars = TARGET_VARS + EXPLAIN_VARS all_corr_labels = { 'pop_growth': '人口増加率', 'welfare_rate': '保育所利用率', **EXPLAIN_LABELS, } corr_labels_list = [all_corr_labels[v] for v in all_corr_vars] corr_mat = df_ana[all_corr_vars].corr() fig1, ax1 = plt.subplots(figsize=(12, 10)) im = ax1.imshow(corr_mat.values, cmap='RdBu_r', vmin=-1, vmax=1) plt.colorbar(im, ax=ax1, shrink=0.8, label='相関係数') ax1.set_xticks(range(len(all_corr_vars))) ax1.set_yticks(range(len(all_corr_vars))) ax1.set_xticklabels(corr_labels_list, fontsize=8, rotation=35, ha='right') ax1.set_yticklabels(corr_labels_list, fontsize=8) for i in range(len(all_corr_vars)): for j in range(len(all_corr_vars)): v = corr_mat.values[i, j] col = 'white' if abs(v) > 0.6 else 'black' ax1.text(j, i, f'{v:.2f}', ha='center', va='center', fontsize=7.5, color=col, fontweight='bold') # 高相関ペアに枠 high_corr_pairs = [ (i, j) for i in range(len(all_corr_vars)) for j in range(i + 1, len(all_corr_vars)) if abs(corr_mat.values[i, j]) > 0.7 ] for (i, j) in high_corr_pairs: ax1.add_patch(plt.Rectangle((j - 0.5, i - 0.5), 1, 1, fill=False, edgecolor='gold', lw=2.5)) ax1.add_patch(plt.Rectangle((i - 0.5, j - 0.5), 1, 1, fill=False, edgecolor='gold', lw=2.5)) ax1.set_title('全変数間の相関係数ヒートマップ\n(目的変数・説明変数、金枠:|r|>0.7)', fontsize=12, fontweight='bold', pad=15) plt.tight_layout() fig1.savefig(os.path.join(FIG_DIR, '2024_H2_fig1_corr.png'), bbox_inches='tight', dpi=150) plt.close(fig1) print(" -> 2024_H2_fig1_corr.png 保存完了") |
図1: 相関ヒートマップを作成中... -> 2024_H2_fig1_corr.png 保存完了
fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。df[col](1列)と df[[col1, col2]](複数列)でカッコの数が違います。リストを渡していると覚えるとミスを減らせます。5年間の人口増加率を目的変数とした重回帰分析。標準地価・正規雇用者割合・保育所数が有意な正の影響を示した(R²=0.603)。
| 変数 | 係数の方向 | 解釈 | 有意性 |
|---|---|---|---|
| 標準地価(住宅地) | 正(+) | 地価が高い地域は経済活力があり人口が増える | * |
| 正規雇用者割合 | 正(+) | 安定した雇用が定住を促す | * |
| 保育所数 | 正(+) | 子育て環境の充実が人口維持に貢献 | * |
| 失業者割合 | 負(−) | 雇用不安は人口流出を招く | n.s. |
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 154 155 156 157 158 159 160 161 162 163 164 165 166 | print("図2: 標準化回帰係数比較を作成中...") coefs1 = [reg1.params[zv] for zv in Z_VARS] coefs2 = [reg2.params[zv] for zv in Z_VARS] pvals1 = [reg1.pvalues[zv] for zv in Z_VARS] pvals2 = [reg2.pvalues[zv] for zv in Z_VARS] x_pos = np.arange(len(Z_VARS)) width = 0.38 fig2, ax2 = plt.subplots(figsize=(13, 6)) bars1 = ax2.bar(x_pos - width / 2, coefs1, width, label=f'人口増加率モデル(R²={reg1.rsquared:.3f})', color='#1565C0', alpha=0.82, edgecolor='white') bars2 = ax2.bar(x_pos + width / 2, coefs2, width, label=f'保育所利用率モデル(R²={reg2.rsquared:.3f})', color='#43A047', alpha=0.82, edgecolor='white') # 有意な係数に *印 for bar, p in zip(bars1, pvals1): if p < 0.05: ax2.text(bar.get_x() + bar.get_width() / 2, bar.get_height() + (0.01 if bar.get_height() >= 0 else -0.03), '*', ha='center', va='bottom', fontsize=11, color='#1565C0', fontweight='bold') for bar, p in zip(bars2, pvals2): if p < 0.05: ax2.text(bar.get_x() + bar.get_width() / 2, bar.get_height() + (0.01 if bar.get_height() >= 0 else -0.03), '*', ha='center', va='bottom', fontsize=11, color='#43A047', fontweight='bold') ax2.set_xticks(x_pos) ax2.set_xticklabels(short_labels, fontsize=9, rotation=25, ha='right') ax2.axhline(0, color='black', linewidth=1.0) ax2.set_ylabel('標準化回帰係数(β)', fontsize=11) ax2.set_title('両回帰モデルの標準化回帰係数比較\n(*: p<0.05)', fontsize=12, fontweight='bold') ax2.legend(fontsize=10) ax2.grid(axis='y', alpha=0.3) plt.tight_layout() fig2.savefig(os.path.join(FIG_DIR, '2024_H2_fig2_coef.png'), bbox_inches='tight', dpi=150) plt.close(fig2) print(" -> 2024_H2_fig2_coef.png 保存完了") |
図2: 標準化回帰係数比較を作成中... -> 2024_H2_fig2_coef.png 保存完了
fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。s[:-n]「末尾n文字を除く」/s[n:]「先頭n文字を除く」。スライス [start:stop:step] はリスト・タプル・文字列共通の基本ワザです。福祉施設の利用者割合(人口比)を目的変数とした分析。保育所数・医療施設数が強い正の影響を示した(R²=0.766)。
| 変数 | 人口増加率モデル | 福祉利用者割合モデル |
|---|---|---|
| 保育所等数 | 正(有意) | 正(有意・最大) |
| 標準地価 | 正(有意) | n.s. |
| 一般病院・診療所数 | n.s. | 正(有意) |
| 失業者割合 | 負 | n.s. |
本研究は「人口増加率」と「福祉施設利用者割合」という2つの目的変数で別々の重回帰分析を行った。これにより「保育所増設が人口増加と福祉利用者増加の両方に効果がある」という一貫した結論が導かれた。
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | import os import warnings import numpy as np import pandas as pd import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt import statsmodels.api as sm from statsmodels.stats.outliers_influence import variance_inflation_factor from scipy import stats warnings.filterwarnings('ignore') plt.rcParams['font.family'] = 'Hiragino Sans' plt.rcParams['axes.unicode_minus'] = False plt.rcParams['figure.dpi'] = 150 FIG_DIR = 'html/figures' DATA_PATH = '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} のように書式も指定できます。188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | print("\n" + "=" * 65) print("■ VIF(分散拡大係数)の確認") print("=" * 65) # 説明変数の標準化(z-score) df_std = df_ana[EXPLAIN_VARS].copy() for v in EXPLAIN_VARS: mu, sig = df_std[v].mean(), df_std[v].std(ddof=1) df_std[v + '_z'] = (df_std[v] - mu) / sig Z_VARS = [v + '_z' for v in EXPLAIN_VARS] X_vif = df_std[Z_VARS].values vif_vals = {} for i, zv in enumerate(Z_VARS): vif = variance_inflation_factor(X_vif, i) vif_vals[zv] = vif label = EXPLAIN_LABELS.get(zv.replace('_z', ''), zv) flag = ' ★多重共線性の疑い' if vif > 5 else '' print(f" {label:<28} VIF = {vif:5.2f}{flag}") |
================================================================= ■ VIF(分散拡大係数)の確認 ================================================================= 正規雇用率(就職/求職) VIF = 3.49 失業率 proxy(求職/15-64歳) VIF = 2.85 小学校数(1万人比) VIF = 4.27 一般病院数(1万人比) VIF = 2.40 一人当たり消費支出 VIF = 1.96 標準地価(住宅地) VIF = 2.22 保育所数(1万人比) VIF = 2.35
.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | print("\n" + "=" * 65) print("■ 重回帰分析①:人口増加率(2021→2022)") print("=" * 65) X_reg = sm.add_constant(df_std[Z_VARS]) y1 = df_ana['pop_growth'] reg1 = sm.OLS(y1, X_reg).fit(cov_type='HC1') print(reg1.summary()) print() print(f" R² = {reg1.rsquared:.4f}") print(f" 調整済み R² = {reg1.rsquared_adj:.4f}") print(f" F 統計量 = {reg1.fvalue:.3f} (p = {reg1.f_pvalue:.4f})") print() print(" 標準化回帰係数:") for zv in Z_VARS: b = reg1.params[zv] p = reg1.pvalues[zv] sig = '***' if p < 0.001 else '**' if p < 0.01 else '*' if p < 0.05 else ' ' label = EXPLAIN_LABELS.get(zv.replace('_z', ''), zv) print(f" {label:<28} β = {b:+.4f} p = {p:.4f} {sig}") |
=================================================================
■ 重回帰分析①:人口増加率(2021→2022)
=================================================================
OLS Regression Results
==============================================================================
Dep. Variable: pop_growth R-squared: 0.791
Model: OLS Adj. R-squared: 0.753
Method: Least Squares F-statistic: 60.22
Date: Mon, 18 May 2026 Prob (F-statistic): 6.27e-19
Time: 11:24:35 Log-Likelihood: 12.632
No. Observations: 47 AIC: -9.264
Df Residuals: 39 BIC: 5.537
Df Model: 7
Covariance Type: HC1
=====================================================================================
coef std err z P>|z| [0.025 0.975]
-------------------------------------------------------------------------------------
const -0.7173 0.030 -24.220 0.000 -0.775 -0.659
empl_rate_z -0.3027 0.058 -5.236 0.000 -0.416 -0.189
unemp_rate_z -0.0698 0.047 -1.488 0.137 -0.162 0.022
school_per10k_z -0.1744 0.054 -3.237 0.001 -0.280 -0.069
hospital_per10k_z 0.0580 0.049 1.193 0.233 -0.037 0.153
consumption_z 0.0565 0.033 1.699 0.089 -0.009 0.122
land_price_z -0.0134 0.029 -0.459 0.646 -0.070 0.044
daycare_per10k_z 0.2085 0.040 5.161 0.000 0.129 0.288
==============================================================================
Omnibus: 0.716 Durbin-Watson: 1.717
Prob(Omnibus): 0.699 Jarque-Bera (JB): 0.622
Skew: 0.269 Prob(JB): 0.733
Kurtosis: 2.832 Cond. No. 5.15
==============================================================================
Notes:
[1] Standard Errors are heteroscedasticity robust (HC1)
R² = 0.7910
調整済み R² = 0.7535
F 統計量 = 60.217 (p = 0.0000)
標準化回帰係数:
正規雇用率(就職/求職) β = -0.3027 p = 0.0000 ***
失業率 proxy(求職/15-64歳) β = -0.0698 p = 0.1367
小学校数(1万人比) β = -0.1744 p = 0.0012 **
一般病院数(1万人比) β = +0.0580 p = 0.2328
一人当たり消費支出 β = +0.0565 p = 0.0893
標準地価(住宅地) β = -0.0134 p = 0.6461
保育所数(1万人比) β = +0.2085 p = 0.0000 ***sm.add_constant(X) — 切片項(定数1の列)を先頭に追加。statsmodelsで必須。sm.OLS(y, X).fit() — 最小二乗法でモデルを推定。model.params, model.pvalues, model.conf_int() で結果取得。[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 | print("\n" + "=" * 65) print("■ 重回帰分析②:保育所利用率(在所児数 / 総人口 × 1000)") print("=" * 65) y2 = df_ana['welfare_rate'] reg2 = sm.OLS(y2, X_reg).fit(cov_type='HC1') print(reg2.summary()) print() print(f" R² = {reg2.rsquared:.4f}") print(f" 調整済み R² = {reg2.rsquared_adj:.4f}") print(f" F 統計量 = {reg2.fvalue:.3f} (p = {reg2.f_pvalue:.4f})") print() print(" 標準化回帰係数:") for zv in Z_VARS: b = reg2.params[zv] p = reg2.pvalues[zv] sig = '***' if p < 0.001 else '**' if p < 0.01 else '*' if p < 0.05 else ' ' label = EXPLAIN_LABELS.get(zv.replace('_z', ''), zv) print(f" {label:<28} β = {b:+.4f} p = {p:.4f} {sig}") |
=================================================================
■ 重回帰分析②:保育所利用率(在所児数 / 総人口 × 1000)
=================================================================
OLS Regression Results
==============================================================================
Dep. Variable: welfare_rate R-squared: 0.771
Model: OLS Adj. R-squared: 0.729
Method: Least Squares F-statistic: 22.29
Date: Mon, 18 May 2026 Prob (F-statistic): 8.66e-12
Time: 11:24:35 Log-Likelihood: -107.52
No. Observations: 47 AIC: 231.0
Df Residuals: 39 BIC: 245.8
Df Model: 7
Covariance Type: HC1
=====================================================================================
coef std err z P>|z| [0.025 0.975]
-------------------------------------------------------------------------------------
const 23.2798 0.382 60.988 0.000 22.532 24.028
empl_rate_z -0.0116 0.809 -0.014 0.989 -1.598 1.574
unemp_rate_z -0.7527 0.698 -1.078 0.281 -2.121 0.616
school_per10k_z -1.7396 0.853 -2.039 0.041 -3.412 -0.067
hospital_per10k_z 0.7036 0.529 1.330 0.183 -0.333 1.740
consumption_z -0.1448 0.587 -0.247 0.805 -1.295 1.005
land_price_z -0.9634 0.463 -2.083 0.037 -1.870 -0.057
daycare_per10k_z 5.1821 0.833 6.221 0.000 3.550 6.815
==============================================================================
Omnibus: 0.003 Durbin-Watson: 1.166
Prob(Omnibus): 0.998 Jarque-Bera (JB): 0.117
Skew: -0.019 Prob(JB): 0.943
Kurtosis: 2.758 Cond. No. 5.15
==============================================================================
Notes:
[1] Standard Errors are heteroscedasticity robust (HC1)
R² = 0.7707
調整済み R² = 0.7295
F 統計量 = 22.285 (p = 0.0000)
標準化回帰係数:
正規雇用率(就職/求職) β = -0.0116 p = 0.9886
失業率 proxy(求職/15-64歳) β = -0.7527 p = 0.2811
小学校数(1万人比) β = -1.7396 p = 0.0415 *
一般病院数(1万人比) β = +0.7036 p = 0.1834
一人当たり消費支出 β = -0.1448 p = 0.8051
標準地価(住宅地) β = -0.9634 p = 0.0373 *
保育所数(1万人比) β = +5.1821 p = 0.0000 ***sm.OLS(y, X).fit() — 最小二乗法でモデルを推定。model.params, model.pvalues, model.conf_int() で結果取得。r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。248 249 250 251 252 253 | print("\n" + "=" * 65) print("■ 図の生成(4枚)") print("=" * 65) short_labels = [EXPLAIN_LABELS[v] for v in EXPLAIN_VARS] vif_labels = [EXPLAIN_LABELS.get(zv.replace('_z', ''), zv) for zv in Z_VARS] |
================================================================= ■ 図の生成(4枚) =================================================================
x if cond else y は三項演算子。リスト内包表記と組み合わせると、forとifを1行で書けます。254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 | print("図3: VIF棒グラフを作成中...") vif_values_list = [vif_vals[zv] for zv in Z_VARS] bar_colors_vif = [ '#E53935' if v > 5 else '#FF8F00' if v > 3 else '#43A047' for v in vif_values_list ] fig3, ax3 = plt.subplots(figsize=(9, 5)) ax3.barh(range(len(Z_VARS)), vif_values_list, color=bar_colors_vif, alpha=0.85, edgecolor='white') ax3.axvline(5, color='#E53935', linestyle='--', linewidth=2, label='VIF=5(警戒線)') ax3.axvline(3, color='#FF8F00', linestyle='--', linewidth=1.5, label='VIF=3(注意線)') ax3.set_yticks(range(len(Z_VARS))) ax3.set_yticklabels(vif_labels, fontsize=9) ax3.set_xlabel('VIF(分散拡大係数)', fontsize=11) ax3.set_title('VIF(多重共線性確認)\n緑: VIF≤3 / 橙: VIF>3 / 赤: VIF>5', fontsize=11, fontweight='bold') ax3.legend(fontsize=9) ax3.grid(axis='x', alpha=0.3) ax3.invert_yaxis() for i, v in enumerate(vif_values_list): ax3.text(v + 0.05, i, f'{v:.2f}', va='center', fontsize=8.5) plt.tight_layout() fig3.savefig(os.path.join(FIG_DIR, '2024_H2_fig3_vif.png'), bbox_inches='tight', dpi=150) plt.close(fig3) print(" -> 2024_H2_fig3_vif.png 保存完了") |
図3: VIF棒グラフを作成中... -> 2024_H2_fig3_vif.png 保存完了
fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。np.cumsum(arr) は累積和、np.linspace(a, b, n) は「aからbを等間隔でn個」。NumPyの定石です。281 282 283 284 285 286 287 288 289 290 291 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 317 | print("図4: 散布図(保育所数 vs 保育所利用率)を作成中...") x4 = df_ana['daycare_per10k'] y4 = df_ana['welfare_rate'] slope, intercept, r_val, p_val, se = stats.linregress(x4, y4) x_line = np.linspace(x4.min(), x4.max(), 100) y_line = intercept + slope * x_line fig4, ax4 = plt.subplots(figsize=(8, 6)) ax4.scatter(x4, y4, color='#1565C0', alpha=0.75, s=70, edgecolors='white', linewidth=0.5, zorder=3) ax4.plot(x_line, y_line, color='#E53935', linewidth=2, label=f'回帰直線 (r={r_val:.3f}, p={p_val:.4f})') # 都道府県名ラベル(上位・下位) for i, row in df_ana.iterrows(): if row['daycare_per10k'] > x4.quantile(0.85) or row['daycare_per10k'] < x4.quantile(0.15): ax4.annotate(row['都道府県'], (row['daycare_per10k'], row['welfare_rate']), fontsize=7.5, xytext=(4, 3), textcoords='offset points', alpha=0.8) ax4.set_xlabel('保育所等数(1万人比)', fontsize=12) ax4.set_ylabel('保育所等利用率(在所児数 / 総人口 × 1000)', fontsize=12) ax4.set_title('保育所数と保育所利用率の関係\n(2022年、47都道府県)', fontsize=12, fontweight='bold') ax4.legend(fontsize=10) ax4.grid(True, alpha=0.3) plt.tight_layout() fig4.savefig(os.path.join(FIG_DIR, '2024_H2_fig4_scatter.png'), bbox_inches='tight', dpi=150) plt.close(fig4) print(" -> 2024_H2_fig4_scatter.png 保存完了") print("\n" + "=" * 65) print("全図の生成完了(4枚)") print("=" * 65) print(f"\n保存先: {os.path.abspath(FIG_DIR)}") print(" 2024_H2_fig1_corr.png - 全変数の相関ヒートマップ") print(" 2024_H2_fig2_coef.png - 両回帰モデルの標準化係数比較") print(" 2024_H2_fig3_vif.png - VIF棒グラフ") print(" 2024_H2_fig4_scatter.png - 保育所数 vs 保育所利用率") |
図4: 散布図(保育所数 vs 保育所利用率)を作成中... -> 2024_H2_fig4_scatter.png 保存完了 ================================================================= 全図の生成完了(4枚) ================================================================= 保存先: /Users/shimpei/Dropbox/Works_Researches/2026 統計・データ解析コンペ/html/figures 2024_H2_fig1_corr.png - 全変数の相関ヒートマップ 2024_H2_fig2_coef.png - 両回帰モデルの標準化係数比較 2024_H2_fig3_vif.png - VIF棒グラフ 2024_H2_fig4_scatter.png - 保育所数 vs 保育所利用率
fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。stats.linregress(x, y) — 単回帰の傾き・切片・r値・p値・標準誤差を返します。使わない値は _ で受け取り。for _, row in df.iterrows() — DataFrameを1行ずつ取り出すループ。1点ずつ描画したいときに使用。{値:.2f}(小数2桁)、{値:,}(3桁区切り)、{値:>10}(右寄せ10桁)など、覚えると出力が一気に整います。| データ | 出典 |
|---|---|
| SSDSE-B(都道府県別社会経済指標) | 統計数理研究所 SSDSE |
| SSDSE-E(都道府県別経済指標) | 統計数理研究所 SSDSE |
| 都道府県道延長・地価 | 国土交通省 |
| 自家用車所有台数 | 自動車検査登録情報協会 |
本教育用コードは合成データを使用(np.random.seed(42))。実際の分析はSSDSEの実データによる。
統計分析の解釈で初心者がやりがちな勘違いをまとめます。特に「相関と因果の混同」「p値の過信」は研究現場でもよく起きる落とし穴です。本文を読む前にも、読んだ後にも、目を通してみてください。
統計の基本用語を初心者向けに解説します。本文中で見慣れない言葉が出てきたら、ここに戻って確認してください。
統計手法について「何のためか」「結果をどう読むか」を初心者向けに解説します。
この研究をさらに発展させるための3つの方向性を示します。「今回わかったこと(X)」から「次に検証すべき仮説(Y)」を立て、「具体的に何をするか(Z)」まで考えてみましょう。
学んだだけでは身につきません。実際に手を動かすのが最強の学習方法です。本論文のスクリプトをベースに、以下のチャレンジに挑戦してみてください。難易度別に5つ用意しました。
本論文で学んだ手法は、研究の世界だけでなく、行政・企業・NPO の現場でも様々に活用されています。具体的なシーンを紹介します。
この論文を読んで初心者が抱きやすい疑問に、教育的観点から答えます。