このページの分析を自分で再現するには、以下の手順でデータを準備してください。コードの編集は不要です。
data/raw/ フォルダに入れます。html/figures/ に自動保存されます。
日本では都道府県間に大きな学力格差が存在することが知られている。本研究は「高校卒業後大学進学率」を学力の代理指標とし、SSDSE-B(都道府県データ)を用いて経済・教育・人口的要因との関係を統計的に検証した。
まず「都道府県ごとの学力の差」を統計的にとらえることが有効だと考えられる。 その理由は感覚や経験則だけでは、複雑な社会要因の中で「何が本当に効いているか」を見極めにくいからである。 本研究では公開データと統計手法を組み合わせ、この問いに定量的な答えを出すことを目指す。
SSDSE-B 相関分析 バックワードAIC選択 標準化係数
高校卒業者のうち大学・短大等へ進学した者の割合を「学力」の代理指標として用いる。
| 変数名 | SSDSE-Bコード | 定義・想定効果 |
|---|---|---|
| 教育費割合 | L322108 / L3221 × 100 | 家計の消費支出に占める教育費の割合(正) |
| 消費支出 | L3221 | 所得水準の代理指標(正) |
| 小学教員/児童比 | E2401 / E2501 | 教員あたり児童数が少ないほど手厚い指導(正) |
| 中学教員/生徒比 | E3401 / E3501 | 同上(中学)(正) |
| 住宅地価格 | C5401 | 地域の富裕度の指標(正) |
| 高齢化率 | A1303 / A1101 × 100 | 高齢化が進む地域は教育投資が少ない(負) |
| 宿泊者per capita | G7101 / A1101 | 都市・観光地の代理(?) |
| 年平均気温 | B4101 | 地理的・地域的差異(?) |
データ出典:SSDSE(社会・人口統計体系)B版 2026年版 — 統計センター
1 2 3 4 5 6 7 8 | df_raw = pd.read_csv( os.path.join(DATA_DIR, 'SSDSE-B-2026.csv'), encoding='cp932', header=None ) # 行0がコード行・行1がラベル行・行2以降がデータ df_raw.columns = df_raw.iloc[0] df_all = df_raw.iloc[2:].copy().reset_index(drop=True) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。pd.read_csv(...) でCSVを読み込みます。encoding='cp932' は日本語Windows由来の文字コード、header=1 は「2行目を列名として使う」。df['A'] / df['B'] — pandasの列同士の四則演算は要素ごと(element-wise)。forループ不要なのが強み。9 10 11 12 13 14 15 16 17 18 19 | # 都道府県コード(R\d{5}形式)に絞る mask_pref = df_all['Code'].str.match(r'^R\d{5}$', na=False) df_pref = df_all[mask_pref].copy() # 2022年度のみ抽出 df_pref['年度_str'] = df_pref['SSDSE-B-2026'].astype(str).str.strip() df = df_pref[df_pref['年度_str'] == '2022'].copy().reset_index(drop=True) print("=" * 60) print(f"■ 対象都道府県数: N={len(df)}(2022年度)") print("=" * 60) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。df['地域コード'].str.match(r'^R\d{5}', ...) — 正規表現で「R+数字5桁」の行(47都道府県)だけTrueにし、真偽値で行をフィルタ。.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。20 21 22 23 24 25 26 27 28 29 30 31 32 | # ── 必要列を数値変換 ───────────────────────────────────────────── NUM_COLS = [ 'E4602', 'E4601', # 大学進学者数・高校卒業者数 'L322108', 'L3221', # 教育費・消費支出 'E2401', 'E2501', # 小学教員数・児童数 'E3401', 'E3501', # 中学教員数・生徒数 'C5401', # 住宅地標準価格 'A1303', 'A1101', # 65歳以上人口・総人口 'G7101', # 延べ宿泊者数 'B4101', # 年平均気温 ] for c in NUM_COLS: df[c] = pd.to_numeric(df[c], errors='coerce') |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | # ── 目的変数 ───────────────────────────────────────────────────── df['大学進学率'] = df['E4602'] / df['E4601'] * 100 # ── 説明変数 ───────────────────────────────────────────────────── df['教育費割合'] = df['L322108'] / df['L3221'] * 100 df['消費支出'] = df['L3221'] df['小学教員児童比'] = df['E2401'] / df['E2501'] df['中学教員生徒比'] = df['E3401'] / df['E3501'] df['住宅地価格'] = df['C5401'] df['高齢化率'] = df['A1303'] / df['A1101'] * 100 df['宿泊者per capita'] = df['G7101'] / df['A1101'] df['年平均気温'] = df['B4101'] VAR_NAMES = [ '教育費割合', '消費支出', '小学教員児童比', '中学教員生徒比', '住宅地価格', '高齢化率', '宿泊者per capita', '年平均気温' ] VAR_LABELS = { '教育費割合': '教育費割合(%)', '消費支出': '消費支出(円/月)', '小学教員児童比': '小学教員/児童比', '中学教員生徒比': '中学教員/生徒比', '住宅地価格': '住宅地価格(千円/m²)', '高齢化率': '高齢化率(%)', '宿泊者per capita': '宿泊者per capita', '年平均気温': '年平均気温(℃)', } |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | # 都道府県名 pref_names = df['Prefecture'].values # 有効データ確認 y = df['大学進学率'].values X_df = df[VAR_NAMES].copy() valid_mask = ( np.isfinite(y) & X_df.apply(lambda col: np.isfinite(col)).all(axis=1).values ) df_valid = df[valid_mask].reset_index(drop=True) y = df_valid['大学進学率'].values X_df = df_valid[VAR_NAMES].copy() pref_names = df_valid['Prefecture'].values N = len(y) print(f"有効都道府県数(欠損除外後): N={N}") print() print(df_valid[['Prefecture', '大学進学率'] + VAR_NAMES].describe().round(3).to_string()) |
============================================================ ■ 対象都道府県数: N=47(2022年度) ============================================================ 有効都道府県数(欠損除外後): N=47 0 大学進学率 教育費割合 消費支出 小学教員児童比 中学教員生徒比 住宅地価格 高齢化率 宿泊者per capita 年平均気温 count 47.000 47.000 47.000 47.000 47.000 47.000 47.000 47.000 47.000 mean 56.623 3.631 289630.362 0.075 0.084 53372.340 31.350 3.265 16.066 std 7.006 1.284 19186.908 0.009 0.011 61991.622 3.269 1.553 2.289 min 46.199 1.955 245054.000 0.058 0.064 13200.000 22.810 0.563 10.200 25% 50.761 2.829 276834.500 0.071 0.078 25300.000 29.847 2.395 15.250 50% 56.768 3.439 287781.000 0.073 0.083 30800.000 31.421 2.951 16.400 75% 61.330 3.973 302255.000 0.080 0.091 55500.000 33.720 3.632 17.350 max 72.986 8.608 324793.000 0.094 0.119 389100.000 38.602 9.930 23.700
.describe() — 件数・平均・標準偏差・四分位・最大/最小を一括計算。データの素性チェックに必須。.apply(lambda x: ...) — その場限りの無名関数を適用。短いロジックなら def せずに書けます。x if cond else y は三項演算子。リスト内包表記と組み合わせると、forとifを1行で書けます。2022年度の47都道府県の大学進学率を降順に並べた横棒グラフ。上位10都道府県を赤で強調し、全国平均を点線で示す。
「学力」を直接測定する都道府県全体の共通指標は公開されていない。そこで「大学進学率」を学力の代理指標として用いる。代理指標はターゲット概念と正の相関を持つが、完全一致ではないため限界(バイアス)を意識することが重要。
82 83 84 85 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 | fig1, ax1 = plt.subplots(figsize=(10, 12)) sort_idx = np.argsort(y) y_sorted = y[sort_idx] names_sorted = pref_names[sort_idx] top10_threshold = np.sort(y)[-10] colors_bar = ['#C62828' if v >= top10_threshold else '#1565C0' for v in y_sorted] alphas_bar = [0.95 if v >= top10_threshold else 0.65 for v in y_sorted] bars = ax1.barh( np.arange(N), y_sorted, color=colors_bar, alpha=0.75, edgecolor='white', height=0.7 ) for bar, alpha in zip(bars, alphas_bar): bar.set_alpha(alpha) ax1.set_yticks(np.arange(N)) ax1.set_yticklabels(names_sorted, fontsize=9) ax1.set_xlabel('大学進学率(%)', fontsize=12) ax1.set_title('都道府県別 大学進学率(2022年度)\n(赤:上位10都道府県)', fontsize=13, fontweight='bold', pad=12) ax1.axvline(y.mean(), color='#FF8F00', linestyle='--', linewidth=1.5, label=f'全国平均 {y.mean():.1f}%') ax1.grid(axis='x', alpha=0.3) ax1.legend(fontsize=10) for i, (v, name) in enumerate(zip(y_sorted, names_sorted)): ax1.text(v + 0.3, i, f'{v:.1f}%', va='center', fontsize=7.5, color='#C62828' if v >= top10_threshold else '#555') plt.tight_layout() fig1.savefig(os.path.join(FIG_DIR, '2023_H5_2_fig1_bar.png'), bbox_inches='tight', dpi=150) plt.close(fig1) print("\n図1保存: 2023_H5_2_fig1_bar.png") |
図1保存: 2023_H5_2_fig1_bar.png
fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。8つの説明変数候補と大学進学率のピアソン相関係数を算出し、無相関検定(帰無仮説:相関係数 = 0)を実施した。
| 変数 | 相関係数 r | p値 | 有意水準 | 方向 |
|---|---|---|---|---|
| 住宅地価格 | 0.672 | <0.001 | *** | 正 |
| 中学教員/生徒比 | −0.592 | <0.001 | *** | 負 |
| 高齢化率 | −0.588 | <0.001 | *** | 負 |
| 小学教員/児童比 | −0.524 | <0.001 | *** | 負 |
| 教育費割合 | 0.464 | 0.001 | ** | 正 |
| 消費支出 | 0.446 | 0.002 | ** | 正 |
| 宿泊者per capita | −0.143 | 0.337 | n.s. | — |
| 年平均気温 | 0.106 | 0.478 | n.s. | — |
「教員/生徒比が高いと進学率が低い」は一見「クラスが大きいと成績が悪い」に見えるが、実際には第三の変数(過疎・高齢化・経済力)が両者に影響している交絡(confounding)の可能性がある。回帰分析で複数変数を同時に制御することでより正確な推定が可能となる。
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 146 147 148 149 150 151 152 153 154 155 156 | fig2, ax2 = plt.subplots(figsize=(9, 5)) corr_list = [corr_vals[n] for n in VAR_NAMES] pval_list = [pval_vals[n] for n in VAR_NAMES] colors2 = ['#C62828' if p < 0.05 else '#9E9E9E' for p in pval_list] y_pos2 = np.arange(len(VAR_NAMES)) ax2.barh(y_pos2, corr_list, color=colors2, alpha=0.75, edgecolor='white', height=0.6) ax2.axvline(0, color='gray', linestyle='--', linewidth=1.0) ax2.set_yticks(y_pos2) ax2.set_yticklabels(VAR_NAMES, fontsize=10) ax2.set_xlabel('ピアソン相関係数', fontsize=11) ax2.set_title('各説明変数と大学進学率の相関係数\n(無相関検定: 赤=p<0.05)', fontsize=12, fontweight='bold') ax2.invert_yaxis() ax2.grid(axis='x', alpha=0.3) from matplotlib.patches import Patch ax2.legend( handles=[ Patch(color='#C62828', alpha=0.75, label='有意(p<0.05)'), Patch(color='#9E9E9E', alpha=0.75, label='非有意'), ], fontsize=9 ) for i, (r, p) in enumerate(zip(corr_list, pval_list)): sig = '***' if p < 0.001 else '**' if p < 0.01 else '*' if p < 0.05 else '' offset = 0.01 if r >= 0 else -0.01 ha = 'left' if r >= 0 else 'right' ax2.text(r + offset, i, f' {r:.3f}{sig}', va='center', ha=ha, fontsize=8.5) plt.tight_layout() fig2.savefig(os.path.join(FIG_DIR, '2023_H5_2_fig2_corr.png'), bbox_inches='tight', dpi=150) plt.close(fig2) print("図2保存: 2023_H5_2_fig2_corr.png") |
図2保存: 2023_H5_2_fig2_corr.png
import pandas as pd など — 必要なライブラリをまとめて呼び出します。as pd は短い別名(alias)。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。x if cond else y は三項演算子。リスト内包表記と組み合わせると、forとifを1行で書けます。8変数全投入モデルを出発点とし、変数を1つ除外したときにAICが最も改善する変数を繰り返し除外する。AICが改善しなくなった時点で選択を終了する。
| 変数 | 標準化係数 β | 非標準化係数 | p値 | 有意水準 |
|---|---|---|---|---|
| 住宅地価格(千円/m²) | +0.563 | +0.0001 | <0.001 | *** |
| 中学教員/生徒比 | −0.711 | −434 | 0.011 | * |
| 小学教員/児童比 | +0.475 | +371 | 0.097 | † |
R² = 0.545, Adj. R² = 0.513, AIC = 286.4, N = 47都道府県
AIC基準のバックワード選択は、p値基準と異なりモデル全体のバランス(適合度とパラメータ数のトレードオフ)を評価する。少数変数で良く当てはまるモデルを選ぶ。
説明変数の単位が異なる(円・比率・℃など)場合、非標準化回帰係数の大小を直接比較することはできない。すべての変数を平均0・標準偏差1に標準化してから回帰すると、β係数の絶対値がそのまま「効果の大きさ」を示し、変数間の比較が可能になる。
158 159 160 161 162 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 193 194 195 196 197 | fig3, ax3 = plt.subplots(figsize=(9, max(4, len(selected_cols) * 0.9))) colors3 = ['#C62828' if p < 0.01 else '#FF8F00' if p < 0.05 else '#9E9E9E' for p in beta_pvals] y_pos3 = np.arange(len(selected_cols)) ax3.barh(y_pos3, beta_coefs, color=colors3, alpha=0.75, edgecolor='white', height=0.6) ax3.errorbar(beta_coefs, y_pos3, xerr=1.96 * beta_ses, fmt='none', color='#333', capsize=4, linewidth=1.2) ax3.axvline(0, color='gray', linestyle='--', linewidth=1.0) ax3.set_yticks(y_pos3) ax3.set_yticklabels(selected_cols, fontsize=10) ax3.set_xlabel('標準化係数 β(±1.96SE)', fontsize=11) ax3.set_title( f'バックワードAIC選択後の重回帰 — 標準化係数\n' f'R²={final_model.rsquared:.3f} Adj.R²={final_model.rsquared_adj:.3f} N={N}都道府県', fontsize=12, fontweight='bold' ) ax3.invert_yaxis() ax3.grid(axis='x', alpha=0.3) ax3.legend( handles=[ Patch(color='#C62828', alpha=0.75, label='p<0.01'), Patch(color='#FF8F00', alpha=0.75, label='p<0.05'), Patch(color='#9E9E9E', alpha=0.75, label='n.s.'), ], fontsize=9 ) for i, (b, p) in enumerate(zip(beta_coefs, beta_pvals)): sig = '***' if p < 0.001 else '**' if p < 0.01 else '*' if p < 0.05 else '' offset = 0.01 if b >= 0 else -0.01 ha = 'left' if b >= 0 else 'right' ax3.text(b + offset, i, f' {b:.3f}{sig}', va='center', ha=ha, fontsize=8.5) plt.tight_layout() fig3.savefig(os.path.join(FIG_DIR, '2023_H5_2_fig3_beta.png'), bbox_inches='tight', dpi=150) plt.close(fig3) print("図3保存: 2023_H5_2_fig3_beta.png") |
図3保存: 2023_H5_2_fig3_beta.png
fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。df[col](1列)と df[[col1, col2]](複数列)でカッコの数が違います。リストを渡していると覚えるとミスを減らせます。家計の消費支出に占める教育費の割合(教育費割合)と大学進学率の散布図。教育費割合は最終モデルでは除外されたが、単純相関ではr=0.464(p=0.001)の有意な正相関が存在する。
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 | import pandas as pd import numpy as np import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt import statsmodels.api as sm from scipy import stats import warnings warnings.filterwarnings('ignore') plt.rcParams['font.family'] = 'Hiragino Sans' plt.rcParams['axes.unicode_minus'] = False plt.rcParams['figure.dpi'] = 150 import os FIG_DIR = 'html/figures' DATA_DIR = 'data/raw' 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} のように書式も指定できます。217 218 219 220 221 222 223 224 225 226 227 228 229 | print("\n" + "=" * 60) print("■ Step1. ピアソン相関係数(目的変数:大学進学率)") print("=" * 60) print(f"\n {'変数':<20} {'r':>8} {'p値':>10} {'有意':>6}") print(" " + "-" * 48) corr_vals = {} pval_vals = {} for name in VAR_NAMES: r, p = stats.pearsonr(X_df[name].values, y) corr_vals[name] = r pval_vals[name] = p sig = '***' if p < 0.001 else '**' if p < 0.01 else '*' if p < 0.05 else 'n.s.' print(f" {name:<20} {r:>8.4f} {p:>10.4f} {sig:>6}") |
============================================================ ■ Step1. ピアソン相関係数(目的変数:大学進学率) ============================================================ 変数 r p値 有意 ------------------------------------------------ 教育費割合 0.4644 0.0010 ** 消費支出 0.4464 0.0017 ** 小学教員児童比 -0.5243 0.0002 *** 中学教員生徒比 -0.5919 0.0000 *** 住宅地価格 0.6717 0.0000 *** 高齢化率 -0.5879 0.0000 *** 宿泊者per capita -0.1432 0.3369 n.s. 年平均気温 0.1061 0.4776 n.s.
stats.pearsonr(x, y) — Pearson相関係数 r と p値を同時に返します。.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。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 | def backward_aic(X_df, y): """バックワードAIC変数選択。AICが改善しなくなるまで変数を除外する。""" cols = list(X_df.columns) step = 0 print("\n" + "=" * 60) print("■ Step2. バックワードAIC変数選択") print("=" * 60) while len(cols) > 1: full_model = sm.OLS(y, sm.add_constant(X_df[cols])).fit() full_aic = full_model.aic aic_drop = {} for c in cols: sub = [x for x in cols if x != c] m = sm.OLS(y, sm.add_constant(X_df[sub])).fit() aic_drop[c] = m.aic best_drop = min(aic_drop, key=aic_drop.get) if aic_drop[best_drop] < full_aic: step += 1 print(f" Step{step}: '{best_drop}' を除外" f"(AIC: {full_aic:.3f} → {aic_drop[best_drop]:.3f})") cols.remove(best_drop) else: break return cols selected_cols = backward_aic(X_df, y) print(f"\n 最終選択変数: {selected_cols}") |
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行でリストを作れます。257 258 259 260 261 262 263 264 | # ── 最終モデルの推定 ────────────────────────────────────────────── X_final = sm.add_constant(X_df[selected_cols]) final_model = sm.OLS(y, X_final).fit() print(f"\n R² = {final_model.rsquared:.4f}") print(f" Adj. R² = {final_model.rsquared_adj:.4f}") print(f" AIC = {final_model.aic:.3f}") print() print(final_model.summary2()) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。sm.add_constant(X) — 切片項(定数1の列)を先頭に追加。statsmodelsで必須。sm.OLS(y, X).fit() — 最小二乗法でモデルを推定。model.params, model.pvalues, model.conf_int() で結果取得。r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 | # ── 標準化係数(βeta) ────────────────────────────────────────── y_std = (y - y.mean()) / y.std() X_sel_std = X_df[selected_cols].copy() for c in selected_cols: X_sel_std[c] = (X_sel_std[c] - X_sel_std[c].mean()) / X_sel_std[c].std() std_model = sm.OLS(y_std, sm.add_constant(X_sel_std)).fit() beta_coefs = std_model.params[1:].values # 定数項除く beta_ses = std_model.bse[1:].values beta_pvals = std_model.pvalues[1:].values print("\n標準化係数(β):") for name, b, p in zip(selected_cols, beta_coefs, beta_pvals): sig = '***' if p < 0.001 else '**' if p < 0.01 else '*' if p < 0.05 else 'n.s.' print(f" {name:<20}: β={b:.4f} p={p:.4f} {sig}") |
============================================================
■ Step2. バックワードAIC変数選択
============================================================
Step1: '教育費割合' を除外(AIC: 293.823 → 291.823)
Step2: '年平均気温' を除外(AIC: 291.823 → 290.002)
Step3: '高齢化率' を除外(AIC: 290.002 → 288.554)
Step4: '宿泊者per capita' を除外(AIC: 288.554 → 287.116)
Step5: '消費支出' を除外(AIC: 287.116 → 286.406)
最終選択変数: ['小学教員児童比', '中学教員生徒比', '住宅地価格']
R² = 0.5445
Adj. R² = 0.5127
AIC = 286.406
Results: Ordinary least squares
=================================================================
Model: OLS Adj. R-squared: 0.513
Dependent Variable: y AIC: 286.4061
Date: 2026-05-18 11:24 BIC: 293.8067
No. Observations: 47 Log-Likelihood: -139.20
Df Model: 3 F-statistic: 17.13
Df Residuals: 43 Prob (F-statistic): 1.82e-07
R-squared: 0.545 Scale: 23.917
-----------------------------------------------------------------
Coef. Std.Err. t P>|t| [0.025 0.975]
-----------------------------------------------------------------
const 62.0325 8.3498 7.4292 0.0000 45.1934 78.8716
小学教員児童比 370.6683 218.2791 1.6981 0.0967 -69.5333 810.8700
中学教員生徒比 -433.9107 163.7779 -2.6494 0.0112 -764.2003 -103.6212
住宅地価格 0.0001 0.0000 4.2480 0.0001 0.0000 0.0001
-----------------------------------------------------------------
Omnibus: 0.133 Durbin-Watson: 1.299
Prob(Omnibus): 0.936 Jarque-Bera (JB): 0.038
Skew: 0.060 Prob(JB): 0.981
Kurtosis: 2.928 Condition No.: 30238466
=================================================================
Notes:
[1] Standard Errors assume that the covariance matrix of the
errors is correctly specified.
[2] The condition number is large, 3.02e+07. This might indicate
that there are strong multicollinearity or other numerical
problems.
標準化係数(β):
小学教員児童比 : β=0.4747 p=0.0967 n.s.
中学教員生徒比 : β=-0.7113 p=0.0112 *
住宅地価格 : β=0.5632 p=0.0001 ***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行で書けます。280 281 282 283 284 285 | fig4, ax4 = plt.subplots(figsize=(11, 7)) x4 = X_df['教育費割合'].values r4, p4 = stats.pearsonr(x4, y) ax4.scatter(x4, y, color='#1565C0', s=50, alpha=0.7, zorder=3, edgecolors='white', linewidth=0.5) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。stats.pearsonr(x, y) — Pearson相関係数 r と p値を同時に返します。s[:-n]「末尾n文字を除く」/s[n:]「先頭n文字を除く」。スライス [start:stop:step] はリスト・タプル・文字列共通の基本ワザです。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 | # 回帰直線 z4 = np.polyfit(x4, y, 1) xs4 = np.linspace(x4.min() - 0.05, x4.max() + 0.05, 200) ax4.plot(xs4, np.poly1d(z4)(xs4), color='#C62828', linewidth=1.8, alpha=0.85, label=f'回帰直線 r={r4:.3f}(p={p4:.4f})') # 都道府県ラベル for xi, yi, name in zip(x4, y, pref_names): # 短縮ラベル label = name.replace('都', '').replace('道', '').replace('府', '').replace('県', '') ax4.text(xi + 0.01, yi + 0.15, label, fontsize=7, color='#333', ha='left', va='bottom', alpha=0.85) ax4.set_xlabel('教育費割合(消費支出に占める割合 %)', fontsize=12) ax4.set_ylabel('大学進学率(%)', fontsize=12) ax4.set_title('教育費割合と大学進学率の関係(2022年度・47都道府県)', fontsize=13, fontweight='bold') ax4.legend(fontsize=10) ax4.grid(True, alpha=0.3) plt.tight_layout() fig4.savefig(os.path.join(FIG_DIR, '2023_H5_2_fig4_scatter.png'), bbox_inches='tight', dpi=150) plt.close(fig4) print("図4保存: 2023_H5_2_fig4_scatter.png") print("\n全図の生成完了(4枚)") print(" fig1_bar.png : 都道府県別大学進学率(横棒グラフ)") print(" fig2_corr.png : 各変数との相関係数棒グラフ") print(" fig3_beta.png : 最終OLSモデル標準化係数") print(" fig4_scatter.png: 教育費割合 vs 大学進学率 散布図") |
図4保存: 2023_H5_2_fig4_scatter.png 全図の生成完了(4枚) fig1_bar.png : 都道府県別大学進学率(横棒グラフ) fig2_corr.png : 各変数との相関係数棒グラフ fig3_beta.png : 最終OLSモデル標準化係数 fig4_scatter.png: 教育費割合 vs 大学進学率 散布図
np.cumsum(arr) は累積和、np.linspace(a, b, n) は「aからbを等間隔でn個」。NumPyの定石です。SSDSE-B(47都道府県、2022年度)を用いた重回帰分析(バックワードAIC選択)の結果:
| データ | 出典 |
|---|---|
| SSDSE-B 都道府県データ(2022年度) | 統計センター SSDSE(社会・人口統計体系)B版 |
| 高校卒業後進学者数・卒業者数 | E4602 / E4601(文部科学省学校基本調査) |
| 家計消費支出・教育費 | L3221 / L322108(総務省家計調査) |
| 住宅地標準価格・教員数・生徒数 | C5401 / E2401 / E2501 / E3401 / E3501 |
分析スクリプトは実データ(SSDSE-B-2026.csv)のみ使用。np.random等の合成データは一切使用していません。
統計分析の解釈で初心者がやりがちな勘違いをまとめます。特に「相関と因果の混同」「p値の過信」は研究現場でもよく起きる落とし穴です。本文を読む前にも、読んだ後にも、目を通してみてください。
統計の基本用語を初心者向けに解説します。本文中で見慣れない言葉が出てきたら、ここに戻って確認してください。
統計手法について「何のためか」「結果をどう読むか」を初心者向けに解説します。
この研究をさらに発展させるための3つの方向性を示します。「今回わかったこと(X)」から「次に検証すべき仮説(Y)」を立て、「具体的に何をするか(Z)」まで考えてみましょう。
学んだだけでは身につきません。実際に手を動かすのが最強の学習方法です。本論文のスクリプトをベースに、以下のチャレンジに挑戦してみてください。難易度別に5つ用意しました。
本論文で学んだ手法は、研究の世界だけでなく、行政・企業・NPO の現場でも様々に活用されています。具体的なシーンを紹介します。
この論文を読んで初心者が抱きやすい疑問に、教育的観点から答えます。