このページの分析を自分で再現するには、以下の手順でデータを準備してください。コードの編集は不要です。
data/raw/ フォルダに入れます。html/figures/ に自動保存されます。
日本の合計特殊出生率(TFR)は1975年に人口置換水準(2.07)を下回って以来、長期低落傾向が続いている。政府は保育所の整備拡充を少子化対策の柱の一つとして推進してきたが、保育所整備が本当にTFR向上に寄与するのかは実証分析が必要な問いである。
まず「子育て支援と合計特殊出生率保育所整備の効果検証」を統計的にとらえることが有効だと考えられる。 その理由は感覚や経験則だけでは、複雑な社会要因の中で「何が本当に効いているか」を見極めにくいからである。 本研究では公開データと統計手法を組み合わせ、この問いに定量的な答えを出すことを目指す。
本研究は、都道府県パネルデータを用いて保育所整備の進展とTFRの関係を時系列・横断的に分析し、子育て支援政策の効果を検証する。
SSDSE-B 時系列分析 OLS回帰 相関分析 待機児童
分析には統計数理研究所が公開するSSDSE(社会・人口統計体系データセット)-B(都道府県データ)2026年版を使用した。2012〜2022年の11年間、47都道府県のパネルデータを構築する。
| 変数名 | 計算式 | SSDSE-B列 | 単位 |
|---|---|---|---|
| TFR(合計特殊出生率) | 直接使用 | A4103 | — |
| 保育所密度 | 保育所等数 / 総人口 × 10,000 | J2503, A1101 | 施設数/万人 |
| 保育士密度 | 保育所等保育士数 / 総人口 × 10,000 | J2526, A1101 | 人/万人 |
| 待機児童率 | 待機児童数 / 定員数 × 100(≥0にclip) | J250502, J2505 | % |
| 女性就業代理 | 15〜64歳人口(女)/ 15〜64歳人口 × 100 | A130202, A1302 | % |
| 高齢化率 | 65歳以上人口 / 総人口 × 100 | A1303, A1101 | % |
TFR(Total Fertility Rate)は「1人の女性が一生の間に産む子どもの数の平均」を表す指標。年齢別出生率(ASFR)の合計として計算される。
人口置換水準2.07は「親世代1組(2人)が平均2.07人の子どもを産まないと人口が維持できない」ことを意味する。2.0でなく2.07なのは乳幼児死亡率と男女出生比率を考慮しているため。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | df_raw = pd.read_csv(DATA_B, encoding='cp932', header=0) # 1行目がコード名(英数字)、2行目(index=0)が日本語ラベル → スキップ df_all = df_raw.iloc[1:].copy() df_all.columns = df_raw.columns # 英数字コード名を保持 # 数値変換対象列 NUM_COLS = [ 'A1101', # 総人口 'A1302', # 15〜64歳人口 'A130202', # 15〜64歳人口(女) 'A1303', # 65歳以上人口 'A4103', # 合計特殊出生率 'J2503', # 保育所等数 'J2505', # 保育所等定員数 'J250502', # 保育所等利用待機児童数 'J2506', # 保育所等在所児数 'J2526', # 保育所等保育士数 ] for c in NUM_COLS: df_all[c] = pd.to_numeric(df_all[c], errors='coerce') |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。pd.read_csv(...) でCSVを読み込みます。encoding='cp932' は日本語Windows由来の文字コード、header=1 は「2行目を列名として使う」。df['A'] / df['B'] — pandasの列同士の四則演算は要素ごと(element-wise)。forループ不要なのが強み。21 22 23 24 25 26 27 28 29 | # 年度列・都道府県列の列名 COL_YEAR = 'SSDSE-B-2026' COL_PREF = 'Prefecture' print("=" * 60) print("■ SSDSE-B-2026 読み込み完了") print(f" 利用可能年度: {sorted(df_all[COL_YEAR].unique())}") print(f" 都道府県数(2022年): {len(df_all[df_all[COL_YEAR]=='2022'])}") print("=" * 60) |
============================================================ ■ SSDSE-B-2026 読み込み完了 利用可能年度: ['2012', '2013', '2014', '2015', '2016', '2017', '2018', '2019', '2020', '2021', '2022', '2023'] 都道府県数(2022年): 47 ============================================================
.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | df22 = df_all[df_all[COL_YEAR] == '2022'].copy().reset_index(drop=True) pop22 = df22['A1101'].clip(lower=1) w1564 = df22['A130202'] # 15〜64歳人口(女) pop1564 = df22['A1302'].clip(lower=1) pop65p = df22['A1303'] df22['保育所密度'] = df22['J2503'] / pop22 * 10000 df22['保育士密度'] = df22['J2526'] / pop22 * 10000 df22['待機児童率'] = (df22['J250502'] / df22['J2505'] * 100).clip(lower=0) df22['女性就業代理'] = w1564 / pop1564 * 100 df22['高齢化率'] = pop65p / pop22 * 100 df22['TFR'] = df22['A4103'] # 欠損値除外 FEATURE_COLS = ['保育所密度', '保育士密度', '待機児童率', '女性就業代理', '高齢化率', 'TFR'] df22_clean = df22.dropna(subset=FEATURE_COLS).copy().reset_index(drop=True) print(f"\n■ 2022年 都道府県データ(欠損除外後): N={len(df22_clean)}") print(df22_clean[['Prefecture'] + FEATURE_COLS].to_string(index=False)) |
■ 2022年 都道府県データ(欠損除外後): N=47
Prefecture 保育所密度 保育士密度 待機児童率 女性就業代理 高齢化率 TFR
北海道 2.138132 19.188716 0.024924 50.376197 32.801556 1.12
青森県 3.920266 22.300664 0.000000 49.773756 34.800664 1.24
岩手県 3.310754 28.340390 0.111150 48.765432 34.546994 1.21
宮城県 2.258772 27.482456 0.168835 49.303008 28.903509 1.09
秋田県 2.881720 25.634409 0.027341 49.380165 38.602151 1.18
山形県 2.901057 30.970221 0.000000 48.939929 34.774256 1.32
福島県 2.184358 26.625698 0.063575 47.864945 32.737430 1.27
茨城県 2.214789 24.003521 0.012464 47.794562 30.422535 1.27
栃木県 2.299633 28.182294 0.031099 47.903657 29.963332 1.24
群馬県 2.446419 18.421328 0.002082 48.194946 30.789336 1.32
埼玉県 2.063514 28.260870 0.225503 48.516618 27.354505 1.17
千葉県 2.120970 24.352059 0.208582 48.551106 27.976380 1.18
東京都 2.575153 41.277247 0.093058 49.242017 22.809517 1.04
神奈川県 2.223787 29.774697 0.129302 48.456098 25.812392 1.17
新潟県 3.316303 32.726428 0.000000 48.870293 33.534603 1.27
富山県 2.920354 19.341200 0.000000 48.245614 32.940020 1.46
石川県 3.094812 22.593918 0.000000 49.074074 30.232558 1.38
福井県 3.837981 21.567065 0.000000 48.591549 31.208499 1.50
山梨県 2.905237 24.064838 0.000000 48.478261 31.421446 1.40
長野県 2.782178 33.980198 0.014706 48.804252 32.524752 1.43
岐阜県 2.132580 22.872559 0.000000 49.414941 31.038027 1.36
静岡県 1.934673 17.303183 0.031225 48.401163 30.737018 1.33
愛知県 2.110740 27.546364 0.027701 48.206569 25.617078 1.35
三重県 2.445465 27.916188 0.139967 48.908730 30.482204 1.40
滋賀県 2.391767 22.824698 0.318239 48.994083 26.827537 1.43
京都府 2.007843 24.898039 0.029189 50.396825 29.607843 1.18
大阪府 1.816215 19.288317 0.073792 50.439334 27.693008 1.22
兵庫県 2.060348 15.894113 0.285601 50.872739 29.766753 1.31
奈良県 1.707504 20.796325 0.283068 51.564626 32.388974 1.25
和歌山県 2.126246 22.768549 0.128233 50.707071 33.997785 1.39
鳥取県 3.437500 34.852941 0.000000 49.664430 33.088235 1.60
島根県 4.513678 52.537994 0.000000 48.857143 34.802432 1.57
岡山県 2.448980 27.744361 0.159438 49.858890 30.827068 1.39
広島県 2.528986 29.097826 0.011155 49.309912 29.927536 1.40
山口県 2.292460 24.287890 0.052968 49.715909 35.186596 1.47
徳島県 3.025568 31.022727 0.000000 50.000000 34.943182 1.42
香川県 2.387580 24.807281 0.075454 49.425287 32.334047 1.45
愛媛県 2.381317 28.055130 0.089350 50.139665 33.920368 1.39
高知県 3.624260 44.215976 0.015464 50.000000 36.094675 1.36
福岡県 2.081704 34.894449 0.082065 50.846333 28.322909 1.33
佐賀県 3.283396 30.973783 0.030753 50.450450 31.335830 1.53
長崎県 3.819174 34.154326 0.000000 50.869565 33.904910 1.57
熊本県 3.626310 36.763679 0.015775 50.689290 32.130384 1.52
大分県 3.035230 23.992773 0.000000 50.000000 33.965673 1.49
宮崎県 3.982890 34.306084 0.000000 50.796460 33.460076 1.63
鹿児島県 3.736404 27.594370 0.358362 51.193317 33.461292 1.54
沖縄県 4.298365 59.795640 0.730534 49.660633 23.433243 1.70[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。まず全国レベルで、TFRと保育所密度の長期推移を2軸グラフで確認する。2012〜2022年の11年間、保育所は着実に増加しているが、TFRはどのような動きをしているだろうか。
| 年 | 全国平均TFR | 保育所密度(施設/万人) | 待機児童率(%) |
|---|---|---|---|
| 2012 | 1.460 | 1.781 | 1.155 |
| 2015 | 1.530 | 1.907 | 0.985 |
| 2018 | 1.503 | 2.205 | 0.733 |
| 2019 | 1.455 | 2.271 | 0.602 |
| 2020 | 1.421 | 2.336 | 0.435 |
| 2021 | 1.400 | 2.390 | 0.194 |
| 2022 | 1.358 | 2.430 | 0.100 |
注:2015年以降の保育所密度急増は「子ども・子育て支援新制度」(2015年4月施行)による認定こども園・小規模保育事業者の参入拡大が一因。
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 85 86 87 88 89 | fig1, ax1a = plt.subplots(figsize=(10, 5)) ax1b = ax1a.twinx() years_arr = df_ts['year'].values tfr_arr = df_ts['TFR'].values den_arr = df_ts['保育所密度'].values line1, = ax1a.plot(years_arr, tfr_arr, color='#C62828', marker='o', linewidth=2.2, markersize=6, label='合計特殊出生率(左軸)', zorder=3) ax1a.axhline(2.07, color='#C62828', linestyle='--', linewidth=1.2, alpha=0.6, label='人口置換水準 (2.07)') ax1a.set_ylabel('合計特殊出生率(TFR)', fontsize=11, color='#C62828') ax1a.tick_params(axis='y', labelcolor='#C62828') ax1a.set_ylim(1.1, 2.2) line2, = ax1b.plot(years_arr, den_arr, color='#1565C0', marker='s', linewidth=2.2, markersize=6, label='保育所密度(右軸)', zorder=3) ax1b.set_ylabel('保育所密度(施設数/万人)', fontsize=11, color='#1565C0') ax1b.tick_params(axis='y', labelcolor='#1565C0') ax1b.set_ylim(1.4, 2.8) ax1a.set_xlabel('年', fontsize=11) ax1a.set_xticks(years_arr) ax1a.set_xticklabels([str(y) for y in years_arr], rotation=45, fontsize=9) ax1a.grid(True, alpha=0.3, zorder=1) lines_all = [line1, line2, plt.Line2D([0], [0], color='#C62828', linestyle='--', linewidth=1.2, alpha=0.6, label='人口置換水準 (2.07)')] ax1a.legend(lines_all + [line2], ['合計特殊出生率(左軸)', '人口置換水準 (2.07)', '保育所密度(右軸)'], fontsize=9, loc='upper right') ax1a.set_title('TFRと保育所密度の時系列推移(全国47都道府県平均)\n2012〜2022年(SSDSE-B 実データ)', fontsize=12, fontweight='bold') fig1.tight_layout() fig1.savefig(os.path.join(FIG_DIR, '2022_H5_7_fig1_timeseries.png'), bbox_inches='tight', dpi=150) plt.close(fig1) print("\n図1保存: 2022_H5_7_fig1_timeseries.png") |
図1保存: 2022_H5_7_fig1_timeseries.png
fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。df[col](1列)と df[[col1, col2]](複数列)でカッコの数が違います。リストを渡していると覚えるとミスを減らせます。時系列分析の限界を補うため、2022年の47都道府県の横断データで保育所密度とTFRの関係を直接確認する。各点が1つの都道府県を表す散布図に回帰直線を重ねる。
| 都道府県 | 保育所密度(施設/万人) | TFR(2022) | 特徴 |
|---|---|---|---|
| 沖縄県 | 4.30 | 1.70 | 高密度・高TFR |
| 島根県 | 4.51 | 1.57 | 高密度・高TFR |
| 宮崎県 | 3.98 | 1.63 | 高密度・高TFR |
| 東京都 | 2.58 | 1.04 | 中密度・低TFR(都市部) |
| 大阪府 | 1.82 | 1.22 | 低密度・低TFR(大都市) |
| 静岡県 | 1.93 | 1.33 | 低密度・低TFR |
「保育所が多い自治体ほど子育てしやすい」を比較するには、単純な保育所数では人口規模の大きな都道府県が有利になる。人口10,000人あたりの施設数に変換(標準化)することで公平な比較が可能になる。
この「分母を共通スケールにする」操作は統計学の基本であり、感染症の「人口10万人あたりの罹患率」や犯罪の「人口あたりの発生率」などにも使われる。
91 92 93 94 95 96 97 | fig2, ax2 = plt.subplots(figsize=(11, 8)) x2 = df22_clean['保育所密度'].values y2 = df22_clean['TFR'].values prefs = df22_clean['Prefecture'].values ax2.scatter(x2, y2, color='#1565C0', s=60, alpha=0.7, zorder=3) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。s[:-n]「末尾n文字を除く」/s[n:]「先頭n文字を除く」。スライス [start:stop:step] はリスト・タプル・文字列共通の基本ワザです。98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | # 都道府県ラベル(略称表示) pref_short = { '北海道': '北海', '青森県': '青森', '岩手県': '岩手', '宮城県': '宮城', '秋田県': '秋田', '山形県': '山形', '福島県': '福島', '茨城県': '茨城', '栃木県': '栃木', '群馬県': '群馬', '埼玉県': '埼玉', '千葉県': '千葉', '東京都': '東京', '神奈川県': '神奈', '新潟県': '新潟', '富山県': '富山', '石川県': '石川', '福井県': '福井', '山梨県': '山梨', '長野県': '長野', '岐阜県': '岐阜', '静岡県': '静岡', '愛知県': '愛知', '三重県': '三重', '滋賀県': '滋賀', '京都府': '京都', '大阪府': '大阪', '兵庫県': '兵庫', '奈良県': '奈良', '和歌山県': '和歌', '鳥取県': '鳥取', '島根県': '島根', '岡山県': '岡山', '広島県': '広島', '山口県': '山口', '徳島県': '徳島', '香川県': '香川', '愛媛県': '愛媛', '高知県': '高知', '福岡県': '福岡', '佐賀県': '佐賀', '長崎県': '長崎', '熊本県': '熊本', '大分県': '大分', '宮崎県': '宮崎', '鹿児島県': '鹿児', '沖縄県': '沖縄', } for xi, yi, pn in zip(x2, y2, prefs): label = pref_short.get(pn, pn[:2]) ax2.annotate(label, (xi, yi), textcoords='offset points', xytext=(4, 3), fontsize=7.5, color='#333') |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。np.cumsum(arr) は累積和、np.linspace(a, b, n) は「aからbを等間隔でn個」。NumPyの定石です。117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | # 回帰直線 z2 = np.polyfit(x2, y2, 1) xs2 = np.linspace(x2.min(), x2.max(), 100) ax2.plot(xs2, np.poly1d(z2)(xs2), color='#C62828', linewidth=1.8, alpha=0.7, label='回帰直線') r2, p2 = stats.pearsonr(x2, y2) ax2.set_xlabel('保育所密度(施設数/万人)', fontsize=12) ax2.set_ylabel('合計特殊出生率(TFR)', fontsize=12) ax2.set_title(f'保育所密度 vs 合計特殊出生率(2022年、47都道府県)\nr = {r2:.3f} p = {p2:.4f}', fontsize=13, fontweight='bold') ax2.axhline(2.07, color='#888', linestyle=':', linewidth=1.0, label='人口置換水準 (2.07)') ax2.legend(fontsize=9) ax2.grid(True, alpha=0.3) fig2.tight_layout() fig2.savefig(os.path.join(FIG_DIR, '2022_H5_7_fig2_scatter.png'), bbox_inches='tight', dpi=150) plt.close(fig2) print("図2保存: 2022_H5_7_fig2_scatter.png") |
図2保存: 2022_H5_7_fig2_scatter.png
ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。stats.pearsonr(x, y) — Pearson相関係数 r と p値を同時に返します。{値:.2f}(小数2桁)、{値:,}(3桁区切り)、{値:>10}(右寄せ10桁)など、覚えると出力が一気に整います。散布図分析で示された「保育所密度とTFRの正の関連」は、他の変数(高齢化率・女性就業代理など)を制御した上でも成立するだろうか。OLS重回帰によって複数の変数を同時に制御し、保育所密度の独立した効果を推定する。
| 変数 | 回帰係数 | 標準誤差 | t値 | p値 | 有意 |
|---|---|---|---|---|---|
| 定数項 | −0.091 | 0.918 | −0.099 | 0.922 | n.s. |
| 保育所密度 | +0.128 | 0.036 | 3.537 | 0.001 | ** |
| 保育士密度 | −0.001 | 0.003 | −0.168 | 0.867 | n.s. |
| 待機児童率 | +0.115 | 0.162 | 0.710 | 0.482 | n.s. |
| 女性就業代理 | +0.021 | 0.020 | 1.076 | 0.288 | n.s. |
| 高齢化率 | +0.002 | 0.007 | 0.228 | 0.821 | n.s. |
R² = 0.429、調整済みR² = 0.359、N = 47都道府県(2022年)。** p<0.01。
| 変数 | 相関係数 r | p値 | 有意 |
|---|---|---|---|
| 保育所密度 | +0.628 | 0.000 | *** |
| 保育士密度 | +0.382 | 0.008 | ** |
| 待機児童率 | +0.105 | 0.481 | n.s. |
| 女性就業代理 | +0.242 | 0.101 | n.s. |
| 高齢化率 | +0.227 | 0.125 | n.s. |
待機児童率 = 待機児童数 / 定員数 × 100 は保育所の逼迫度を示す指標だが、いくつかの限界がある。
(1)「隠れ待機児童」の問題:保育所申請を諦めた人(潜在需要)は統計に現れない。(2)定義変更:2017年の「新定義」では育休中の保護者などを待機児童から除外したため、時系列が断絶している。(3)地域差:定員充足率は都市部と農村部で大きく異なる。
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | fig3, ax3 = plt.subplots(figsize=(9, 5)) coefs3 = np.asarray(ols_model.params[1:]) ses3 = np.asarray(ols_model.bse[1:]) pvals3 = np.asarray(ols_model.pvalues[1:]) y_pos3 = np.arange(len(X_vars)) colors3 = ['#C62828' if p < 0.01 else '#FF8F00' if p < 0.05 else '#9E9E9E' for p in pvals3] ax3.barh(y_pos3, coefs3, color=colors3, alpha=0.75, edgecolor='white', height=0.6) ax3.errorbar(coefs3, y_pos3, xerr=1.96 * ses3, fmt='none', color='#333', capsize=5, linewidth=1.5) ax3.axvline(0, color='gray', linestyle='--', linewidth=1.0) ax3.set_yticks(y_pos3) ax3.set_yticklabels(X_vars, fontsize=11) ax3.set_xlabel('OLS回帰係数(±1.96 SE)', fontsize=11) ax3.set_title(f'TFRの決定要因(OLS重回帰係数プロット)\n' f'R² = {ols_model.rsquared:.3f} N = {len(df22_clean)}都道府県(2022年)', fontsize=12, fontweight='bold') ax3.invert_yaxis() ax3.grid(axis='x', alpha=0.3) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。np.cumsum(arr) は累積和、np.linspace(a, b, n) は「aからbを等間隔でn個」。NumPyの定石です。157 158 159 160 161 162 163 164 | # 有意水準の凡例 from matplotlib.patches import Patch legend_patches = [ Patch(facecolor='#C62828', alpha=0.75, label='p < 0.01'), Patch(facecolor='#FF8F00', alpha=0.75, label='p < 0.05'), Patch(facecolor='#9E9E9E', alpha=0.75, label='n.s.'), ] ax3.legend(handles=legend_patches, fontsize=9, loc='lower right') |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。import pandas as pd など — 必要なライブラリをまとめて呼び出します。as pd は短い別名(alias)。{値:.2f}(小数2桁)、{値:,}(3桁区切り)、{値:>10}(右寄せ10桁)など、覚えると出力が一気に整います。165 166 167 168 169 170 171 172 173 174 175 | # 係数と有意水準を注釈 for i, (c, p) in enumerate(zip(coefs3, pvals3)): sig = '***' if p < 0.001 else '**' if p < 0.01 else '*' if p < 0.05 else '' offset = 0.003 if c >= 0 else -0.003 ha = 'left' if c >= 0 else 'right' ax3.text(c + offset, i, f' {c:.4f}{sig}', va='center', ha=ha, fontsize=8.5) fig3.tight_layout() fig3.savefig(os.path.join(FIG_DIR, '2022_H5_7_fig3_coef.png'), bbox_inches='tight', dpi=150) plt.close(fig3) print("図3保存: 2022_H5_7_fig3_coef.png") |
図3保存: 2022_H5_7_fig3_coef.png
plt.subplots(figsize=(W, H)) で図サイズ指定、fig.savefig(..., bbox_inches='tight') で余白を自動で詰めて保存。保育所の「量的整備」が進む一方で、需要と供給のアンバランスは地域によって大きく異なる。2022年時点での都道府県別待機児童率を可視化することで、保育政策の残余課題を探る。
| グループ | 特徴 | 代表的都道府県 |
|---|---|---|
| 高待機圧力(0.5%超) | 保育需要が供給を上回る | 沖縄(0.73%) |
| 中程度(0.1〜0.5%) | 一定の待機が存在 | 鹿児島・兵庫・奈良 |
| 低水準(0.1%未満) | 実質的な待機解消 | 東北・北陸の多くの県 |
「保育所を増やすとTFRが上がるか」は政策評価の問いであり、因果推論が必要である。OLS回帰は相関を示すが因果とは限らない。
処置効果推定の課題:(1)逆因果(TFRが低い都市部で保育所を増設)、(2)地域固定効果(地方性という交絡)、(3)時間ラグ(保育所整備の効果は数年後に現れる可能性)。
より厳密な推定には差分の差分法(DiD)や操作変数法(IV)が有効だが、本研究の横断データでは困難である。この点が今後の課題となる。
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | 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 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_B = '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} のように書式も指定できます。195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 | YEARS = list(range(2012, 2023)) ts_records = [] for yr in YEARS: ydf = df_all[df_all[COL_YEAR] == str(yr)].copy() pop_total = ydf['A1101'].sum() nurseries = ydf['J2503'].sum() nursery_staff = ydf['J2526'].sum() waiting = ydf['J250502'].sum() capacity = ydf['J2505'].sum() avg_tfr = ydf['A4103'].mean() density_nursery = nurseries / pop_total * 10000 # 保育所密度 density_staff = nursery_staff / pop_total * 10000 # 保育士密度 waiting_rate = max(waiting / capacity * 100, 0) # 待機児童率(≥0) ts_records.append({ 'year': yr, 'TFR': avg_tfr, '保育所密度': density_nursery, '保育士密度': density_staff, '待機児童率': waiting_rate, }) df_ts = pd.DataFrame(ts_records) print("\n■ 時系列データ(全国平均):") print(df_ts.to_string(index=False)) |
■ 時系列データ(全国平均): year TFR 保育所密度 保育士密度 待機児童率 2012 1.460426 1.780718 24.856845 1.155214 2013 1.478936 1.773275 25.130362 1.057160 2014 1.471915 1.807007 25.790330 0.971926 2015 1.529574 1.906766 24.842018 0.985077 2016 1.520638 1.949797 24.851784 0.977507 2017 1.512553 2.021746 25.099748 1.040996 2018 1.503404 2.205242 26.382034 0.732534 2019 1.454894 2.270712 27.074553 0.601590 2020 1.421277 2.336497 26.909829 0.435217 2021 1.399787 2.389960 26.738406 0.193985 2022 1.358298 2.429530 28.087334 0.100266
.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 | print("\n" + "=" * 60) print("■ OLS重回帰: TFR の決定要因分析(2022年)") print("=" * 60) X_vars = ['保育所密度', '保育士密度', '待機児童率', '女性就業代理', '高齢化率'] y_reg = df22_clean['TFR'].values X_reg = df22_clean[X_vars].values X_reg_c = sm.add_constant(X_reg) ols_model = sm.OLS(y_reg, X_reg_c).fit() print(ols_model.summary()) # 標準化係数(効果量の比較) X_std = (X_reg - X_reg.mean(axis=0)) / X_reg.std(axis=0) y_std = (y_reg - y_reg.mean()) / y_reg.std() ols_std = sm.OLS(y_std, sm.add_constant(X_std)).fit() beta_std = ols_std.params[1:] # 定数項除く print("\n■ 標準化偏回帰係数(β):") for name, b in zip(X_vars, beta_std): print(f" {name:<12}: β = {b:+.4f}") |
============================================================
■ OLS重回帰: TFR の決定要因分析(2022年)
============================================================
OLS Regression Results
==============================================================================
Dep. Variable: y R-squared: 0.429
Model: OLS Adj. R-squared: 0.359
Method: Least Squares F-statistic: 6.158
Date: Mon, 18 May 2026 Prob (F-statistic): 0.000242
Time: 11:24:11 Log-Likelihood: 36.343
No. Observations: 47 AIC: -60.69
Df Residuals: 41 BIC: -49.58
Df Model: 5
Covariance Type: nonrobust
==============================================================================
coef std err t P>|t| [0.025 0.975]
------------------------------------------------------------------------------
const -0.0907 0.918 -0.099 0.922 -1.945 1.763
x1 0.1279 0.036 3.537 0.001 0.055 0.201
x2 -0.0005 0.003 -0.168 0.867 -0.006 0.005
x3 0.1153 0.162 0.710 0.482 -0.212 0.443
x4 0.0212 0.020 1.076 0.288 -0.019 0.061
x5 0.0017 0.007 0.228 0.821 -0.013 0.017
==============================================================================
Omnibus: 5.980 Durbin-Watson: 0.728
Prob(Omnibus): 0.050 Jarque-Bera (JB): 5.762
Skew: -0.855 Prob(JB): 0.0561
Kurtosis: 2.876 Cond. No. 3.44e+03
==============================================================================
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large, 3.44e+03. This might indicate that there are
strong multicollinearity or other numerical problems.
■ 標準化偏回帰係数(β):
保育所密度 : β = +0.6159
保育士密度 : β = -0.0281
待機児童率 : β = +0.1041
女性就業代理 : β = +0.1390
高齢化率 : β = +0.0368sm.add_constant(X) — 切片項(定数1の列)を先頭に追加。statsmodelsで必須。sm.OLS(y, X).fit() — 最小二乗法でモデルを推定。model.params, model.pvalues, model.conf_int() で結果取得。r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。243 244 245 246 247 248 249 250 251 252 | print("\n" + "=" * 60) print("■ 相関分析(各変数 vs TFR)") print("=" * 60) print(f" {'変数':<12} {'r':>8} {'p値':>10} {'有意':>6}") print(" " + "-" * 42) for vname in X_vars: col = df22_clean[vname].values r, p = stats.pearsonr(col, y_reg) sig = '***' if p < 0.001 else '**' if p < 0.01 else '*' if p < 0.05 else 'n.s.' print(f" {vname:<12} {r:>8.4f} {p:>10.4f} {sig:>6}") |
============================================================ ■ 相関分析(各変数 vs TFR) ============================================================ 変数 r p値 有意 ------------------------------------------ 保育所密度 0.6278 0.0000 *** 保育士密度 0.3821 0.0080 ** 待機児童率 0.1053 0.4814 n.s. 女性就業代理 0.2422 0.1009 n.s. 高齢化率 0.2268 0.1252 n.s.
stats.pearsonr(x, y) — Pearson相関係数 r と p値を同時に返します。x if cond else y は三項演算子。リスト内包表記と組み合わせると、forとifを1行で書けます。253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 | fig4, ax4 = plt.subplots(figsize=(11, 10)) df_rank = df22_clean[['Prefecture', '待機児童率', 'TFR']].copy() df_rank = df_rank.sort_values('待機児童率', ascending=True).reset_index(drop=True) y_pos4 = np.arange(len(df_rank)) colors4 = ['#C62828' if v > 0.5 else '#FF8F00' if v > 0.1 else '#1565C0' for v in df_rank['待機児童率']] ax4.barh(y_pos4, df_rank['待機児童率'], color=colors4, alpha=0.75, edgecolor='white', height=0.7) ax4.set_yticks(y_pos4) ax4.set_yticklabels(df_rank['Prefecture'], fontsize=8.5) ax4.set_xlabel('待機児童率(待機児童数 / 定員数 × 100, %)', fontsize=11) ax4.set_title('待機児童率の都道府県別ランキング(2022年)\n(保育所等利用待機児童数 / 保育所等定員数 × 100)', fontsize=12, fontweight='bold') ax4.grid(axis='x', alpha=0.3) ax4.axvline(0, color='gray', linewidth=0.8) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。sort_values('列名', ascending=False) — 指定列で並べ替え(降順)。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。{値:.2f}(小数2桁)、{値:,}(3桁区切り)、{値:>10}(右寄せ10桁)など、覚えると出力が一気に整います。271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 | # TFRを右側に注記(上位・下位) for i, (_, row) in enumerate(df_rank.iterrows()): if row['待機児童率'] > 0.05: ax4.text(row['待機児童率'] + 0.01, i, f"TFR {row['TFR']:.2f}", va='center', fontsize=7.5, color='#555') from matplotlib.patches import Patch legend4 = [ Patch(facecolor='#C62828', alpha=0.75, label='0.5%超(高い待機圧力)'), Patch(facecolor='#FF8F00', alpha=0.75, label='0.1〜0.5%'), Patch(facecolor='#1565C0', alpha=0.75, label='0.1%未満'), ] ax4.legend(handles=legend4, fontsize=9, loc='lower right') fig4.tight_layout() fig4.savefig(os.path.join(FIG_DIR, '2022_H5_7_fig4_waiting.png'), bbox_inches='tight', dpi=150) plt.close(fig4) print("図4保存: 2022_H5_7_fig4_waiting.png") print("\n■ 全図の生成完了(4枚)") print(f" fig1_timeseries.png : TFRと保育所密度の時系列推移") print(f" fig2_scatter.png : 保育所密度 vs TFR 散布図(2022年)") print(f" fig3_coef.png : OLS回帰係数プロット") print(f" fig4_waiting.png : 待機児童率の都道府県別ランキング") |
図4保存: 2022_H5_7_fig4_waiting.png ■ 全図の生成完了(4枚) fig1_timeseries.png : TFRと保育所密度の時系列推移 fig2_scatter.png : 保育所密度 vs TFR 散布図(2022年) fig3_coef.png : OLS回帰係数プロット fig4_waiting.png : 待機児童率の都道府県別ランキング
import pandas as pd など — 必要なライブラリをまとめて呼び出します。as pd は短い別名(alias)。for _, row in df.iterrows() — DataFrameを1行ずつ取り出すループ。1点ずつ描画したいときに使用。plt.subplots(figsize=(W, H)) で図サイズ指定、fig.savefig(..., bbox_inches='tight') で余白を自動で詰めて保存。SSDSE-B(47都道府県、2012〜2022年)を用いた時系列・横断分析の結果:
| データ | 出典 | 主な変数 |
|---|---|---|
| SSDSE-B-2026(都道府県データ) | 統計数理研究所 SSDSE | TFR、保育所等数、保育士数など |
| 合計特殊出生率(A4103) | 厚生労働省 人口動態統計 | 都道府県別TFR |
| 保育所等数(J2503) | 厚生労働省 保育所等関連状況取りまとめ | 保育所・認定こども園等の合計 |
| 待機児童数(J250502) | 厚生労働省 保育所等利用待機児童数調査 | 4月1日現在の待機児童数 |
本教育用コードはSSDSE-B-2026.csvの実公的データのみを使用。合成データ・乱数データは一切使用していない。
統計分析の解釈で初心者がやりがちな勘違いをまとめます。特に「相関と因果の混同」「p値の過信」は研究現場でもよく起きる落とし穴です。本文を読む前にも、読んだ後にも、目を通してみてください。
統計の基本用語を初心者向けに解説します。本文中で見慣れない言葉が出てきたら、ここに戻って確認してください。
統計手法について「何のためか」「結果をどう読むか」を初心者向けに解説します。
この研究をさらに発展させるための3つの方向性を示します。「今回わかったこと(X)」から「次に検証すべき仮説(Y)」を立て、「具体的に何をするか(Z)」まで考えてみましょう。
学んだだけでは身につきません。実際に手を動かすのが最強の学習方法です。本論文のスクリプトをベースに、以下のチャレンジに挑戦してみてください。難易度別に5つ用意しました。
本論文で学んだ手法は、研究の世界だけでなく、行政・企業・NPO の現場でも様々に活用されています。具体的なシーンを紹介します。
この論文を読んで初心者が抱きやすい疑問に、教育的観点から答えます。