論文一覧に戻る 🕸 論文ネットワーク 🗺 用語ネットワーク 統計データ分析コンペ 教育用再現集
2020年度(令和2年度) 統計データ分析コンペティション | 大学生・一般の部
特別賞(統計活用)

社会増減が合計特殊出生率に与える影響

⏱️ 推定読了時間: 約32分
著者:堤敬司(京都府政策企画部企画統計課) 手法:重回帰分析・ランダムフォレスト・単回帰 データ:SSDSE-B(都道府県)+ e-Stat(住民基本台帳・人口動態)
🔬 ランダムフォレスト🔬 単回帰🔬 重回帰🔬 階層ベイズ🏷 移住・人口移動🏷 人口・少子化
🔬 手法タグ / 🏷 応用タグ(クリックで同タグの論文をネットワーク検索)
🔬 再現カルテ — 原論文のデータが手に入らないため、代替データで再現

この教材は、原論文が使ったデータそのものを使えていません。そこで代わりのデータで同じ問いを追いかけ、結論の向き(増える/減る、強い/弱い)が原論文と一致するかを確かめます。「同じ数値が出る」ことは目標にしていません。

原論文が使ったデータSSDSE-B・人口動態統計・人口推計・学校基本調査・住民基本台帳人口移動報告・住民基本台帳に基づく人口、人口動態及び世帯数調査・人口動態保健所・市区町村別統計
分析単位:その他
中核手法:重回帰分析・ランダムフォレスト
この教材が使うデータ
原論文(PDF)社会増減が合計特殊出生率に与える影響
特別賞/堤敬司(京都府政策企画部企画統計課)
✅ この教材でできること
  • 原論文の中核手法(重回帰分析)を実データで実行できる
  • 分析に使うデータは同梱済みで、ブラウザ上でそのまま読み込める
  • 数値・図を最後まで再計算できる(重回帰分析(OLS))
⚠️ この教材ではできないこと(原論文との違い)
  • 分析の細かさが原論文と違う:論文=市区町村パネル+階層ベイズ・RF併用。教材=SSDSE-B都道府県
📄 原論文との違いを、もっと詳しく
📄 原論文:市区町村パネルデータで社会増減が TFR に与える影響を分析。重回帰に加え階層ベイズモデル・ランダムフォレストも併用。
📘 本教材:SSDSE-B-2026(都道府県データ)の重回帰を中心に再現。階層ベイズ・ランダムフォレストの工程は扱っていない。
⚠️ 注意:原論文の手法構成(重回帰+階層ベイズ+RF)と教材の再現範囲(重回帰)が異なる。

▶ ここに書いてある分析は、ページ下部の「🐍 ブラウザで動かす」でそのまま実行できます(インストール不要)。動かしているのは code/2020_U5_4_shorei.py(213 行)そのものです。

🏅 論文審査会コメント(審査員はここを評価した)
「ランダムフォレストの利用例として、考察も面白かった。」
出典:統計センター「統計データ分析コンペティション」受賞論文PDF掲載の審査講評。プロの審査員が何を評価し、何を注文したかは論文の読み方の最良の手本になる。
📝 3行で分かる要約

目次

  1. 研究の背景
  2. TFRの定義と仮説
  3. データと変数(再現可能性トリアージ)
  4. 都道府県別TFRの分布【実再現】
  5. 人口あたり学生数とTFR【実再現】
  6. 年齢階級別転入超過率の効果【報告値】
  7. ランダムフォレストの変数重要度【報告値】
  8. 京都府の市区町村別分析【報告値】
  9. 結論
  10. 📥 データの準備
  11. ⚠️ よくある誤解
  12. 📖 用語集
  13. 📐 手法ガイド
  14. 🚀 発展の可能性
  15. 🎯 自分でやってみよう
  16. 🤔 Q&A
  17. 🐍 ブラウザで動かす

🎯 この記事を読むと何ができるようになるか

📥 データの準備(再現コードを動かす前に)

このページの【実再現】部分(図1・図2)は、SSDSE-B だけで再現できます。コードの編集は不要です。なお原論文の中核である年齢階級別女性転入超過率のデータは e-Stat 由来でSSDSEに含まれないため、図3・図4は「原論文の報告値の可視化」にとどめています(詳細はデータの章)。

1
データをダウンロードする 独立行政法人統計センターの SSDSE(教育用標準データセット)配布ページから、以下のファイルをダウンロードします。
SSDSE-B-2026.csv ← SSDSE-B(都道府県データ)📥 直接DL
⬇ SSDSEダウンロードページを開く
2
ファイルを所定のフォルダに配置する ダウンロードしたCSVを、プロジェクトの data/raw/ フォルダに入れます。
2026 統計・データ解析コンペ/ ├── code/ │ └── 2020_U5_4_shorei.py ← 実行するスクリプト └── data/ └── raw/ SSDSE-B-2026.csv ← ここに置く
3
スクリプトをそのまま実行する ターミナルでプロジェクトルートに移動し、以下を実行します。
python3 code/2020_U5_4_shorei.py
図は html/figures/ に自動保存されます。
研究の背景

我が国では、「1.57ショック」を契機に様々な少子化対策が講じられてきた。しかし低下トレンドから抜け出すことはできず、合計特殊出生率(TFR)の全国値は2005年に1.26まで低下、2015年に1.45と一旦回復した後、2019年は再び1.36へ下落した。人口置換水準2.07を下回る状況は1974年以来46年連続で続いている。

原論文の概要(統計センター公式):「都道府県及び市区町村別の年齢別女性人口と出生数を用いて合計特殊出生率(TFR)の推定を行い、女性の年齢階級別転入超過率を特徴量として重回帰モデル及びランダムフォレストによる分析を行って、女性の年齢別転入超過率が出生率に有意に影響していることを示している。また、結婚して出産を迎える女性の出産コア年齢である30-34歳の女性の転入超過が自治体別でみた場合のTFRの向上に資することを指摘している。」
本ページではこの分析の流れを実データでたどりながら、使われた統計手法を一つずつ学んでいく。

問題意識:京都府はなぜTFRが低いのか 筆者が勤務・居住する京都府は、都道府県別TFRランキングでワースト3位(原論文図2、2017年 公表値1.31、全国値1.43)であり、1975年から基本的にワースト5以内で推移している。複数の都道府県での居住経験のある筆者の実感としては、京都府の子育て環境が他と比べてとりわけ悪いとは感じられない。ならば、なぜTFRは低く出るのか。
図1(TFR全国値の推移, 1947–2018年)について 原論文の図1は全国TFRの長期時系列(出典:厚生労働省「人口動態調査」)である。本教材が用いる SSDSE-B は都道府県別の断面データで長期時系列を含まないため、グラフは原論文を参照し、ここでは主要な報告値を文章で示す(2005年1.26/2015年1.45/2019年1.36)。

重回帰分析 ランダムフォレスト 単回帰 公的統計の再構成

TFRの定義と、そこから生まれる仮説

仮説を立てるにあたり、まず期間合計特殊出生率の定義を確認する。これは、ある年の15〜49歳の各歳の女性について「その歳の女性が産んだ子ども数 ÷ その歳の女性人口」を求め、合計したものである。

期間合計特殊出生率 = Σx=15〜49 ( Children(x) / Mothers(x) )

表1 TFRの変数の定義(原論文)

変数名意味出典
Children(x)x歳の女性が出産した子どもの数厚生労働省「人口動態統計」
Mothers(x)x歳の日本人女性人口(10/1時点)総務省「推計人口」または「国勢調査」

※通常、都道府県別・市区町村別のTFR算出では、利用できる統計の制約から5歳階級別人口を用い、5を乗じて便宜的に算出される。

定義から仮説へ:分子(出生数)ではなく分母(日本人女性人口)に注目する。京都府は地域性として学生が多く、その中には「出産をまだ想定しない女性」が相対的に多く含まれる。すると分母が水増しされ、TFRは低く出る(=TFRが薄まる)のではないか——これが本研究の中心仮説である。

DS LEARNING POINT 1

「期間TFR」は一人の女性の生涯出生数ではない

期間合計特殊出生率は、ある1年の各年齢の出生率を「仮想の一生」として足し合わせた合成指標である。実際の一人の女性が生涯に産む数(コホートTFR)とは異なり、その年の年齢別女性人口の構成に影響される。だからこそ、分母の人口構成を動かす社会増減が値を歪めうる。

データと変数(再現可能性トリアージ)

原論文は、都道府県レベルと京都府の市区町村レベルの二段構えで分析する。分母の女性人口を、通常の「推計人口」「国勢調査」ではなく総務省「住民基本台帳に基づく人口、人口動態及び世帯数調査」を用いてTFRを近似する点が方法上の工夫である。

表2 使用データセット(原論文)

記号変数出典本教材での扱い
A都道府県別TFR厚労省「人口動態統計」(2014-2017)SSDSE収録→実再現
B総人口総務省「人口推計」(2017)SSDSE収録→実再現
C・D短期大学・大学 学生数文科省「学校基本調査」(2017)SSDSE収録→実再現
E女性転入超過数(5歳階級別)総務省「住民基本台帳人口移動報告」(2014-2017)e-Stat→報告値の可視化
F女性人口(5歳階級別)総務省「住民基本台帳…調査」(2013-2017)e-Stat→報告値の可視化
G出生数(5歳階級別)厚労省「人口動態統計」(2013-2017)e-Stat→報告値の可視化
H市区町村別TFR-A厚労省「人口動態保健所・市区町村別統計」(2013-2017)e-Stat→報告値の可視化

表3 主な加工(原論文)

記号変数加工方法
I人口1000人あたり学生数(C+D) / B × 1000
J女性転入超過率(5歳階級別)E / F × 100(%)
K市区町村別TFR-B(G1519/F1519×5)+(G2024/F2024×5)+…+(G4549/F4549×5)
再現可能性トリアージ(本教材の方針)
  • 実再現:都道府県別TFR(A4103)・総人口(A1101)・学生数(E6301+E6302)は SSDSE-B に収録されているため、SSDSE-B-2026 の2017年断面で実際に計算する(図1・図2)。
  • 報告値の可視化:年齢階級別の女性転入超過率・女性人口・出生数、および市区町村別TFR-Aは e-Stat 由来でSSDSEに未収録。表4〜表6・図6・図7は原論文の報告値をそのまま示し、可視化した図は「再計算ではない」と明記する。新しい数値は一切算出しない。

DS LEARNING POINT 2

市区町村別TFRを自作する(TFR-B)

公表される市区町村別TFR-A(厚労省)は5年平均・分母5年固定のため、単年の時系列変化を追いにくい。そこで原論文は分母に住民基本台帳の女性人口を用いたTFR-Bを自作し、公表値TFR-Aとの散布図決定係数を確認して妥当性を検証した(ベイズ推定値と R²=0.7900、非ベイズ推定値と R²=0.8599、n=1467)。これは「既存指標をそのまま使えないとき、代替指標を作り妥当性を検証する」という実務的な作法の好例である。

1
都道府県別TFRの分布【実再現】

まず SSDSE-B-2026 の2017年断面(原論文の分析年に対応)で、都道府県別TFRを地域区分で色分けしてランキング表示する。原論文の図2に対応する実再現である。

都道府県別TFRランキング(2017年, 実再現)
図1【実再現】都道府県別 合計特殊出生率ランキング(2017年)。点線は47都道府県の単純平均(1.51)。濃紺は京都府。データ:SSDSE-B-2026 の2017年断面(独立行政法人統計センター)。
📊 図の読み取り
最下位グループ
東京都(1.21)が最低。北海道・宮城県・京都府(いずれも1.29〜1.31)が続く。京都府は低い方から4番目(原論文はワースト3位と記載。宮城県と同値1.31で順位は近接)。
上位グループ
沖縄県(1.94)が突出して高く、宮崎県・島根県など九州・中国地方が高い。
注意
点線は47府県の単純平均1.51で、人口で重み付けした全国値1.43(原論文)とは異なる。指標の集計方法で「平均」の意味が変わる点に注意。
やってみようセットアップ:ライブラリ・パス・分析年の設定
📝 コード
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import os, numpy as np, pandas as pd
import matplotlib; matplotlib.use('Agg')
import matplotlib.pyplot as plt
from matplotlib.patches import Patch
import statsmodels.api as sm

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'
YEAR    = 2017          # 原論文の断面年(SSDSE2020B の 2017 年に対応)
os.makedirs(FIG_DIR, exist_ok=True)
▼ 実行結果
このステップは print しません。ライブラリの読み込みと、出力先フォルダ・データパス・分析年(2017年)の設定だけを行います。
💡 解説
  • matplotlib.use('Agg') — 画面表示せず図をファイル保存するための設定。
  • YEAR = 2017 — 原論文の断面年。SSDSE2020Bの2017年に対応させ、忠実性を高めています。
  • FIG_DIRDATA_B — 図の保存先と入力CSVのパス。
💡 TIPS 分析年を変数 YEAR に切り出しておくと、あとで別年に切り替えて再現するのが一行で済みます。
やってみようSSDSE-Bの読み込みと2017年断面の抽出
📝 コード
45
46
47
48
49
50
51
52
53
54
# ── SSDSE-B の読み込み(1行目=英字コード, 2行目=日本語列名) ──
df = pd.read_csv(DATA_B, encoding='cp932', header=0, skiprows=[1])
df = df.rename(columns={'SSDSE-B-2026': '年度', 'Code': '地域コード', 'Prefecture': '都道府県'})
df = df[df['地域コード'].str.match(r'^R\d{5}', na=False)].copy()   # 47都道府県のみ
df['年度'] = df['年度'].astype(int)

# ── 2017年断面(原論文の分析年) ──
d = df[df['年度'] == YEAR].copy().reset_index(drop=True)
d['合計特殊出生率'] = d['A4103']                              # A4103: TFR
d['学生数per1000']  = (d['E6301'] + d['E6302']) / d['A1101'] * 1000  # (短大+大学)/総人口×1000
▼ 実行結果
このステップも print しません。CSVを読み込み、47都道府県・2017年だけを取り出し、TFRと「人口1000人あたり学生数」を計算しています。
💡 解説
  • header=0, skiprows=[1] — SSDSEは1行目が英字コード(A4103等)、2行目が日本語名。英字コードを列名に使い、日本語名の行だけ飛ばします。
  • rename(...) — 先頭3列の SSDSE-B-2026 / Code / Prefecture年度 / 地域コード / 都道府県 に読み替え。
  • str.match(r'^R\d{5}') — 地域コードが「R+5桁」の47都道府県行だけを残します。
  • A4103=合計特殊出生率、E6301+E6302=短大+大学の学生数、A1101=総人口。学生数を人口1000人あたりに換算します。
💡 TIPS SSDSEは列名が英字コードなので、A4103=合計特殊出生率 のようにコードと意味の対応を先に控えておくと迷いません。
やってみよう地域区分マップの作成
📝 コード
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# 地域区分マップ(8地方 → 6区分)
region_map = {
    '北海道': '北海道・東北', '青森県': '北海道・東北', '岩手県': '北海道・東北',
    '宮城県': '北海道・東北', '秋田県': '北海道・東北', '山形県': '北海道・東北',
    '福島県': '北海道・東北', '茨城県': '関東', '栃木県': '関東', '群馬県': '関東',
    '埼玉県': '関東', '千葉県': '関東', '東京都': '関東', '神奈川県': '関東',
    '新潟県': '中部', '富山県': '中部', '石川県': '中部', '福井県': '中部',
    '山梨県': '中部', '長野県': '中部', '岐阜県': '中部', '静岡県': '中部', '愛知県': '中部',
    '三重県': '近畿', '滋賀県': '近畿', '京都府': '近畿', '大阪府': '近畿',
    '兵庫県': '近畿', '奈良県': '近畿', '和歌山県': '近畿',
    '鳥取県': '中国・四国', '島根県': '中国・四国', '岡山県': '中国・四国',
    '広島県': '中国・四国', '山口県': '中国・四国', '徳島県': '中国・四国',
    '香川県': '中国・四国', '愛媛県': '中国・四国', '高知県': '中国・四国',
    '福岡県': '九州・沖縄', '佐賀県': '九州・沖縄', '長崎県': '九州・沖縄',
    '熊本県': '九州・沖縄', '大分県': '九州・沖縄', '宮崎県': '九州・沖縄',
    '鹿児島県': '九州・沖縄', '沖縄県': '九州・沖縄'
}
▼ 実行結果
47都道府県を6地域に対応づける辞書と、地域ごとの色を用意しています。図の色分けに使います。
💡 解説
  • region_map — 都道府県名→地域名の辞書。d['都道府県'].map(region_map) で一括変換。
  • region_colors — 地域→色コードの辞書。棒や点の色をそろえて、地域傾向を目で追えるようにします。
💡 TIPS Series.map(辞書) は「変換表を当てる」定番。forループなしで全行に一括適用できます。
やってみよう図1:都道府県別TFRランキングの描画
📝 コード
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
r = d.sort_values('合計特殊出生率', ascending=True).reset_index(drop=True)
mean_tfr = d['合計特殊出生率'].mean()

fig1, ax1 = plt.subplots(figsize=(10, 12))
colors_bar = [region_colors[g] for g in r['地域']]
ax1.barh(r['都道府県'], r['合計特殊出生率'], color=colors_bar, edgecolor='white', linewidth=0.5)
# 京都府を強調
kyoto_pos = r.index[r['都道府県'] == '京都府'][0]
ax1.barh('京都府', r.loc[kyoto_pos, '合計特殊出生率'], color='#1a3e72', edgecolor='white')
ax1.axvline(mean_tfr, color='black', linestyle='--', linewidth=1.2, alpha=0.7)
ax1.text(mean_tfr + 0.01, 1, f'47都道府県\n単純平均 {mean_tfr:.2f}', fontsize=9, va='bottom', ha='left')
ax1.set_xlabel('合計特殊出生率(TFR)', fontsize=12)
ax1.set_title('都道府県別 合計特殊出生率ランキング(2017年)\n【実再現:SSDSE-B-2026 の 2017 年断面】',
              fontsize=13, fontweight='bold', pad=12)
ax1.set_xlim(1.0, 2.0)
ax1.tick_params(axis='y', labelsize=8.5)
legend_elements = [Patch(facecolor=v, label=k) for k, v in region_colors.items()]
ax1.legend(handles=legend_elements, loc='lower right', fontsize=9, framealpha=0.8)
plt.tight_layout()
fig1.savefig(os.path.join(FIG_DIR, '2020_U5_4_fig1.png'), bbox_inches='tight')
plt.close(fig1)
print('[OK] fig1 saved')
▼ 実行結果
[OK] fig1 saved
💡 解説
  • sort_values(...) でTFR昇順に並べ、barh で横棒に。色は地域区分。
  • 京都府だけ濃紺で塗り直して強調。axvline で47府県の単純平均に点線を引きます。
  • set_xlim(1.0, 2.0) — 横軸を1.0〜2.0に固定し、府県間の差を見やすくします。
💡 TIPS ランキング図は「基準線(平均)」を一本入れるだけで、各府県が平均より上か下かが一目で分かります。
2
人口あたり学生数とTFR【実再現】

仮説「学生が多い地域はTFRが低い」を、SSDSE-Bだけで直接検証する。人口1000人あたり学生数を説明変数、TFRを目的変数とする単回帰である。これは原論文の表5に対応する分析で、使用データがSSDSEに収録されているため実再現できる。

人口あたり学生数とTFRの散布図(実再現)
図2【実再現】人口1000人あたり学生数とTFRの関係(2017年, 47都道府県)。回帰係数 −0.0071(t=−3.53, p=0.001), R²=0.217。データ:SSDSE-B-2026。
📊 図の読み取り
右下がりの関係
学生数が多い府県ほどTFRが低い。京都府・東京都は学生数が多くTFRが低い右下、沖縄県は学生数が少なくTFRが高い左上に位置。
有意性
回帰係数はp=0.001で1%水準で有意。仮説の向き(負の関係)が確認できる。
原論文との対応
原論文 表5 の報告値は回帰係数 −0.147**、R²=0.200、n=47。本再現は符号・有意性・R²の水準が一致する。係数の絶対値の違いは、変数のスケール(学生数の定義・単位)やSSDSEのデータ版の違いによるもので、結論の向きは変わらない。
やってみよう図2:人口あたり学生数とTFRの単回帰
📝 コード
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
X = sm.add_constant(d['学生数per1000'])
model = sm.OLS(d['合計特殊出生率'], X).fit()
b0, b1 = model.params['const'], model.params['学生数per1000']

fig2, ax2 = plt.subplots(figsize=(9, 7))
for region, grp in d.groupby('地域'):
    ax2.scatter(grp['学生数per1000'], grp['合計特殊出生率'],
                color=region_colors[region], label=region, s=60, zorder=3, alpha=0.85)
for name in ['京都府', '東京都', '沖縄県']:
    row = d[d['都道府県'] == name].iloc[0]
    ax2.annotate(name, xy=(row['学生数per1000'], row['合計特殊出生率']),
                 fontsize=10, fontweight='bold', ha='center', va='bottom',
                 xytext=(0, 6), textcoords='offset points')
xline = np.linspace(d['学生数per1000'].min(), d['学生数per1000'].max(), 100)
ax2.plot(xline, b0 + b1 * xline, 'k-', linewidth=1.6, alpha=0.75,
         label=f'回帰線 (係数={b1:.4f}, R²={model.rsquared:.3f})')
ax2.set_xlabel('人口1000人あたり学生数(短大+大学)/千人', fontsize=12)
ax2.set_ylabel('合計特殊出生率(TFR)', fontsize=12)
ax2.set_title('人口あたり学生数と TFR の関係(2017年, 47都道府県)\n【実再現:SSDSE-B-2026】',
              fontsize=13, fontweight='bold', pad=10)
ax2.legend(fontsize=9, loc='upper right', framealpha=0.85, ncol=2)
ax2.grid(True, alpha=0.3)
plt.tight_layout()
fig2.savefig(os.path.join(FIG_DIR, '2020_U5_4_fig2.png'), bbox_inches='tight')
▼ 実行結果
[OK] fig2 saved
💡 解説
  • sm.add_constantsm.OLS(...).fit()最小二乗単回帰を推定。b1が学生数の係数。
  • 地域ごとに色分けして散布図を描き、京都府・東京都・沖縄県だけラベル付け。
  • b0 + b1*xline で回帰直線を重ね描き。凡例に係数とR²を表示します。
💡 TIPS 散布図に回帰直線を重ねると「相関の強さ」と「傾きの向き」を同時に伝えられます。ただし因果の証明ではない点に注意。
⚠️ 単回帰の限界 この学生数の係数は、他の要因を統制していない単回帰の値。学生数は都市度・所得・年齢構成などと絡むため、交絡の可能性がある。だからこそ原論文は、次章で年齢階級別の転入超過率という、より粒度の細かい特徴量で重回帰を行っている。
3
年齢階級別転入超過率の効果【報告値】

原論文の中核。5歳階級別の女性転入超過率(15〜49歳の7変数)を特徴量として、TFRを重回帰する。使用データ(年齢階級別の転入超過数・女性人口)はe-Stat由来でSSDSEに未収録のため、以下は原論文 表4 の報告値である。

推定式(原論文)
ŷ = β0 + β1x1 + … + β7x7
ŷ:TFR推定値 β0:切片 βi:偏回帰係数 xi:5歳階級別 日本人女性の転入超過率(15〜49歳)

表4 都道府県別TFRに係る重回帰分析結果(原論文の報告値)

変数偏回帰係数標準誤差t値95%C.I.VIF
切片1.457 **0.011133.4821.435〜1.479
15-19歳 転入超過率−0.039 **0.012−3.206−0.064〜−0.0154.421
20-24歳 転入超過率−0.012 †0.007−1.656−0.027〜0.0026.503
25-29歳 転入超過率−0.0030.021−0.120−0.044〜0.0393.160
30-34歳 転入超過率0.116 *0.0492.3730.020〜0.2132.805
35-39歳 転入超過率0.150 †0.0781.911−0.005〜0.3042.227
40-44歳 転入超過率−0.0650.118−0.557−0.297〜0.1672.266
45-49歳 転入超過率0.0880.1320.670−0.172〜0.3492.735

サンプルサイズ 188(47都道府県 × 4年分)/ R²=0.408、補正R²=0.385、標準誤差=0.106。 † p<0.1 * p<0.05 ** p<0.01

都道府県別TFR重回帰の偏回帰係数(報告値の可視化)
図3【報告値の可視化・再計算ではない】都道府県別TFRに係る重回帰の偏回帰係数(原論文 表4 の報告値)。誤差棒は95%C.I.。年齢階級別データはSSDSE未収録のため、原論文の数値をそのまま図示したもので、当方の再計算ではない。
📊 主要な発見(原論文の報告値)
15-19歳:TFRを押し下げ
係数 −0.039**。転入超過率が1%上がるとTFRは約0.04下がる。仮説どおり「学生(進学期)の流入がTFRを薄める」方向。
30-34歳:TFRを押し上げ
係数 +0.116*。転入超過率が1%上がるとTFRは約0.12上がる。出産コア年齢の流入はTFRを押し上げる
特徴量ゼロモデル
すべての転入超過率を0とすると切片 1.457 が残る。全国の2014-2017年TFR公表値平均1.44は、この95%C.I.(1.435〜1.479)内に収まる。
多重共線性
20-24歳などVIFが6前後とやや高め。隣接年齢の転入超過率は相関しやすい点に留意(後述の手法ガイド参照)。
やってみよう図3:重回帰の偏回帰係数を可視化(報告値)
📝 コード
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
167
168
169
ages   = ['15-19歳', '20-24歳', '25-29歳', '30-34歳', '35-39歳', '40-44歳', '45-49歳']
coef   = [-0.039, -0.012, -0.003, 0.116, 0.150, -0.065, 0.088]   # 偏回帰係数(報告値)
ci_lo  = [-0.064, -0.027, -0.044, 0.020, -0.005, -0.297, -0.172] # 95%C.I.下限(報告値)
ci_hi  = [-0.015,  0.002,  0.039, 0.213, 0.304,  0.167,  0.349]  # 95%C.I.上限(報告値)
sig    = ['**', '†', 'n.s.', '*', '†', 'n.s.', 'n.s.']

y = np.arange(len(ages))[::-1]
bar_col = ['#e05c5c' if s in ('*', '**') else '#aaaaaa' for s in sig]
fig3, ax3 = plt.subplots(figsize=(8.5, 5.5))
ax3.barh(y, coef, color=bar_col, height=0.55, zorder=3)
ax3.errorbar(coef, y, xerr=[np.array(coef) - np.array(ci_lo), np.array(ci_hi) - np.array(coef)],
             fmt='none', color='black', capsize=4, linewidth=1.4, zorder=4)
ax3.axvline(0, color='black', linewidth=1.0)
ax3.set_yticks(y); ax3.set_yticklabels(ages, fontsize=11)
for yi, c, s in zip(y, coef, sig):
    ax3.text(c + (0.01 if c >= 0 else -0.01), yi, s, va='center',
             ha='left' if c >= 0 else 'right', fontsize=11, fontweight='bold',
             color='#cc0000' if s in ('*', '**') else '#666')
ax3.set_xlabel('偏回帰係数 β(TFR への影響)', fontsize=12)
ax3.set_title('都道府県別 TFR に係る重回帰の偏回帰係数\n'
              '【原論文 表4 の報告値の可視化・再計算ではない/R²=0.408, n=188】',
              fontsize=12, fontweight='bold', pad=10)
ax3.grid(True, axis='x', alpha=0.3)
legend_items = [Patch(facecolor='#e05c5c', label='p<0.05(有意)'),
                Patch(facecolor='#aaaaaa', label='†p<0.1 / n.s.')]
ax3.legend(handles=legend_items, loc='lower right', fontsize=9)
plt.tight_layout()
fig3.savefig(os.path.join(FIG_DIR, '2020_U5_4_fig3.png'), bbox_inches='tight')
plt.close(fig3)
print('[OK] fig3 saved')
▼ 実行結果
[OK] fig3 saved
💡 解説
  • coef / ci_lo / ci_hi原論文 表4 の報告値をそのまま配列に書き写したもの。ここで新たな回帰は行っていません。
  • barherrorbar で係数と95%信頼区間を横棒+誤差棒に。0の縦線をまたぐ棒は「有意でない」と読めます。
  • 有意(p<0.05)を赤、それ以外を灰色にし、タイトルに「報告値の可視化・再計算ではない」と明記しています。
💡 TIPS 他者の報告値を図示するときは、図タイトル・キャプションの両方に「再計算ではない」と書くのが誠実。数値の出所を必ず添えましょう。
4
ランダムフォレストの変数重要度【報告値】

同じ特徴量でランダムフォレストを回し、変数重要度を見る。重回帰(線形・係数の符号あり)とは別の角度から「どの年齢の転入超過が効くか」を確認する狙い。こちらも使用データがSSDSE未収録のため、原論文 図6・図7 の報告値である。

ランダムフォレストの変数重要度(報告値の可視化)
図4【報告値の可視化・再計算ではない】ランダムフォレストによる年齢階級別転入超過率の変数重要度。青=都道府県別(原論文図6)、橙=京都府市区町村別(原論文図7)。原論文の報告値を図示したもので、当方の再計算ではない。
📊 主要な発見(原論文の報告値)
都道府県別(図6)
重要度は 15-19歳(0.697) が最大、次いで30-34歳・20-24歳。重回帰と同様に若年層の転入超過が効く。
京都府(図7)
25-29歳(1.504) と15-19歳(0.939)が大きい。都道府県別とは効く年齢層に差がある。
読み方の注意
変数重要度は「寄与の大きさ」で、符号(押し上げ/押し下げ)は示さない。方向は重回帰の係数と合わせて解釈する必要がある。
やってみよう図4:ランダムフォレスト変数重要度を可視化(報告値)
📝 コード
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
imp_pref  = [0.697, 0.489, 0.293, 0.490, 0.275, 0.184, 0.254]   # 図6(報告値)
imp_kyoto = [0.939, 0.469, 1.504, 0.677, 0.551, 0.496, 0.428]   # 図7(報告値)
x = np.arange(len(ages)); w = 0.4
fig4, ax4 = plt.subplots(figsize=(9.5, 5.5))
ax4.bar(x - w/2, imp_pref, w, label='都道府県別(図6)', color='#1565C0', zorder=3)
ax4.bar(x + w/2, imp_kyoto, w, label='京都府 市区町村別(図7)', color='#E65100', zorder=3)
ax4.set_xticks(x); ax4.set_xticklabels(ages, fontsize=10)
ax4.set_ylabel('変数重要度(Importance)', fontsize=12)
ax4.set_title('ランダムフォレストによる年齢階級別 転入超過率の変数重要度\n'
              '【原論文 図6・図7 の報告値の可視化・再計算ではない】',
              fontsize=12, fontweight='bold', pad=10)
ax4.legend(fontsize=10); ax4.grid(True, axis='y', alpha=0.3)
plt.tight_layout()
fig4.savefig(os.path.join(FIG_DIR, '2020_U5_4_fig4.png'), bbox_inches='tight')
plt.close(fig4)
print('[OK] fig4 saved')
▼ 実行結果
[OK] fig4 saved
💡 解説
  • imp_pref / imp_kyoto原論文 図6・図7 の報告値を書き写した配列。ランダムフォレストの学習はここでは行いません(元データが手元にないため)。
  • 都道府県別と京都府を隣り合う2本の棒で並べ、効く年齢層の違いを対比できるようにしています。
  • タイトルに「報告値の可視化・再計算ではない」と明記。
💡 TIPS ランダムフォレストの重要度は「効く度合い」を教えてくれますが「向き」は教えてくれません。線形回帰と併用するのが定石です。
5
京都府の市区町村別分析【報告値】

京都府内の市区町村について、自作のTFR-Bを目的変数、市区町村別の5歳階級別女性転入超過率を特徴量として重回帰する。市区町村別データはe-Stat由来のため、以下は原論文 表6 の報告値である。

表6 京都府のTFR-Bを用いた重回帰分析結果(原論文の報告値)

変数偏回帰係数標準誤差t値95%C.I.VIF
切片1.420 **0.01782.7801.386〜1.454
15-19歳 転入超過率−0.021 **0.006−3.639−0.032〜−0.0101.855
20-24歳 転入超過率−0.007 *0.003−2.425−0.014〜−0.0011.940
25-29歳 転入超過率0.035 **0.0057.1890.026〜0.0451.665
30-34歳 転入超過率0.020 *0.0092.3010.003〜0.0372.102
35-39歳 転入超過率−0.0020.014−0.117−0.030〜0.0261.774
40-44歳 転入超過率−0.0260.018−1.438−0.061〜0.0101.213
45-49歳 転入超過率0.0090.0240.377−0.038〜0.0561.368

サンプルサイズ 128(32市区町村 × 4年分)/ R²=0.581、補正R²=0.557、標準誤差=0.152。 * p<0.05 ** p<0.01。 ※TFRの標準誤差が0.1以上の「笠置町」「和束町」「南山城村」「伊根町」は外れ値として除外。

特徴量ゼロモデルが示すこと 京都府の市区町村別モデルで転入超過率をすべて0にすると、TFRは 1.42(95%C.I. 1.39〜1.45)。SSDSE の京都府2014-2017年の公表値平均1.31より高く、ほぼ全国並み(全国1.44)。社会増減の影響を取り除けば、京都府のTFRは特別低くない——筆者の「子育て環境が悪いとは感じない」という実感と整合する。
図4・図5(TFR-A と TFR-B の散布図)について 原論文の図4・図5は、自作TFR-Bと公表TFR-Aの散布図(ベイズ推定値と R²=0.7900、非ベイズ推定値と R²=0.8599、n=1467)。市区町村別データがSSDSE未収録のためグラフは原論文を参照し、決定係数の報告値のみ示す。
やってみよう実行結果のまとめ(実再現部分の出力)
📝 コード
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
print('\n=== 図1・図2の基礎統計(2017年, SSDSE-B, 実再現) ===')
print(f'47都道府県 単純平均TFR: {mean_tfr:.3f}')
low5 = r[['都道府県', '合計特殊出生率']].head(5).to_string(index=False)
print('TFRが低い5府県:\n' + low5)
kr = r.reset_index(drop=True)
kyoto_rank = kr.index[kr['都道府県'] == '京都府'][0] + 1
print(f'京都府: TFR={d.loc[d["都道府県"]=="京都府","合計特殊出生率"].iloc[0]:.2f}, '
      f'低い方から {kyoto_rank}/47 位')
print(f'最高: 沖縄県 {d["合計特殊出生率"].max():.2f} / 最低: 東京都 {d["合計特殊出生率"].min():.2f}')

print('\n=== 図2:人口1000人あたり学生数 → TFR 単回帰(実再現) ===')
print(f'切片            : {b0:.3f}')
print(f'回帰係数(学生数): {b1:.4f}  (t={model.tvalues["学生数per1000"]:.3f}, '
      f'p={model.pvalues["学生数per1000"]:.4f})')
print(f'決定係数 R²      : {model.rsquared:.3f}  (補正 R²={model.rsquared_adj:.3f}, n={int(model.nobs)})')
print('→ 学生数が多い府県ほど TFR が低い、という原論文の指摘(表5)が符号・有意性・R²の水準で再現された。')
print('   (原論文 表5 の報告値: 回帰係数 -0.147**, R²=0.200, n=47。'
      '係数の絶対値は変数スケール・データ版の違いで異なる。)')
▼ 実行結果
[OK] fig1 saved
[OK] fig2 saved
[OK] fig3 saved
[OK] fig4 saved

=== 図1・図2の基礎統計(2017年, SSDSE-B, 実再現) ===
47都道府県 単純平均TFR: 1.513
TFRが低い5府県:
都道府県  合計特殊出生率
 東京都     1.21
 北海道     1.29
 宮城県     1.31
 京都府     1.31
 奈良県     1.33
京都府: TFR=1.31, 低い方から 4/47 位
最高: 沖縄県 1.94 / 最低: 東京都 1.21

=== 図2:人口1000人あたり学生数 → TFR 単回帰(実再現) ===
切片            : 1.627
回帰係数(学生数): -0.0071  (t=-3.531, p=0.0010)
決定係数 R²      : 0.217  (補正 R²=0.200, n=47)
→ 学生数が多い府県ほど TFR が低い、という原論文の指摘(表5)が符号・有意性・R²の水準で再現された。
   (原論文 表5 の報告値: 回帰係数 -0.147**, R²=0.200, n=47。係数の絶対値は変数スケール・データ版の違いで異なる。)
💡 解説
  • このブロックは実再現部分(図1・図2)の実際の標準出力です。京都府のTFR=1.31・低い方から4/47位、学生数単回帰の係数−0.0071(p=0.001, R²=0.217)が確認できます。
  • 末尾で原論文 表5 の報告値(−0.147**, R²=0.200)との対応を明示し、符号・有意性・R²が一致することを述べています。
💡 TIPS 再現結果は「原論文の報告値」と並べて出力しておくと、どこまで一致し、どこが(スケール等で)違うのかを読者が自分で確認できます。
結論

都道府県別・市区町村別の両方で、共通して15-19歳と30-34歳の女性転入超過率が5%水準で有意だった。15-19歳(進学期)の転入超過はTFRを薄め、30-34歳(出産コア年齢)の転入超過はTFRを押し上げる

結論1:TFR公表値 ≠ 子育て環境の良さ 社会増減という撹乱要因を取り除くと、京都府のTFRはほぼ全国並みだった。TFRを目的変数にする分析では、TFRに撹乱要因が含まれるため「TFRの高低=子育て環境の良し悪し」と捉えることには注意が必要である。
結論2:自治体施策の効果とマクロの限界 30-34歳の女性の転入促進・流出防止は、自治体単位でみればTFR向上に資する。ただし全国の社会増減数は海外との転出入を除けばゼロであり、自治体間の転入促進は「パイの奪い合い」になりうる。国全体のTFR向上には、やはり「未婚化」「晩婚化」にフォーカスした施策が根本的に必要である。
論文審査会コメント(原文) 「ランダムフォレストの利用例として、考察も面白かった。」

参考文献(原論文)

  1. 内閣府「平成19年版少子化社会白書」第2章第1節
  2. P.F.ドラッカー「すでに起こった未来」ダイヤモンド社(1994年)
  3. 東北大学経済学研究科吉田研究室「子ども人口時計」
  4. 松村波・熊野翔・川田瑛貴「市区町村別でみる合計特殊出生率推移の特徴分析」2019年度統計データコンペティション 統計活用奨励賞(大学生・一般の部)
  5. 小野恵子・宮内はじめ・白松俊・河口信夫・五十嵐康伸「日本の全市町村における人口の自然増減の分布と説明要因」2018年度統計データコンペティション 特別賞(大学生・一般の部)

📦 再現コードとデータ

本ページの【実再現】部分(図1・図2)は、以下のスクリプトとSSDSE-Bで再現できます。

🐍 再現コード(2020_U5_4_shorei.py) 📊 SSDSE-B-2026.csv

※図3・図4は原論文(表4・図6・図7)の報告値を可視化したもので、再計算ではありません。年齢階級別の転入超過データはe-Statから別途取得が必要です。

⚠️ よくある誤解

この研究を読み解くうえで、つまずきやすいポイントを整理します。

誤解1:「TFRが低い=子育て環境が悪い」
TFRは分母にその地域の女性人口の年齢構成を含みます。学生など「出産をまだ想定しない女性」が多い地域では分母が膨らみ、TFRは低く出ます。低いTFRは、必ずしも子育てのしにくさを意味しません。
誤解2:「合計特殊出生率は一人の女性が実際に産む子ども数」
よく使われる期間TFRは、ある1年の各年齢の出生率を仮想的に足し合わせた合成指標です。実際の生涯出生数(コホートTFR)とは別物で、その年の人口構成に左右されます。
誤解3:「自治体でTFRを上げれば全国も上がる」
30-34歳の転入を促せばその自治体のTFRは上がりますが、全国の社会増減は海外分を除けばゼロ。自治体間の転入促進は人口の奪い合いで、全国のTFRは動きません。
誤解4:「単回帰で負なら、学生が出生率を下げる原因だ」
図2の単回帰は他要因を統制していません。学生数は都市度・所得・年齢構成と絡む交絡の可能性があります。だから原論文は年齢階級別の重回帰へ進み、影響を切り分けています。相関は因果ではありません。

📖 用語集

この記事に出てくる主要用語をまとめます。

合計特殊出生率(TFR)
15〜49歳の各歳の「女性が産んだ子ども数÷女性人口」を合計した指標。期間TFRは1年の断面から作る合成値で、人口構成に影響される。
社会増減・転入超過率
転入から転出を引いた人口の増減が社会増減。転入超過率=(女性転入超過数÷女性人口)×100。本研究の中心的な特徴量
特徴量ゼロモデル(切片モデル)
説明変数をすべて0にしたときの推定値。ここでは「社会増減の影響を取り除いたTFR」を表し、都道府県別1.457/京都府1.42となった。
偏回帰係数
他の変数を一定にしたときの、その変数1単位あたりの目的変数の変化量。符号と大きさで影響の向き・強さを読む。
VIF・多重共線性
VIF多重共線性の指標。説明変数どうしが相関すると係数が不安定に。目安として10超で要注意(本研究は最大6.5程度)。
ランダムフォレストの変数重要度
ランダムフォレストで各特徴量が予測に寄与する度合い。大きさは分かるが符号(向き)は分からない
決定係数 R²
モデルが目的変数のばらつきをどれだけ説明できたか(0〜1)。補正R²を併記する。
信頼区間・ベイズ推定値
95%C.I.は推定値の不確かさの幅。市区町村別TFR-Aは、少人数地域のばらつきを抑えたベイズ推定値も公表されている。

📐 手法ガイド

本研究で使われた手法と、使うときの注意点を手法別に整理します。

データの出所について
本研究の【実再現】部分は、独立行政法人統計センターが提供するSSDSE(教育用標準データセット)を使います。年齢階級別の転入超過などの詳細データは、政府統計ポータルe-Statから個別に取得します。
重回帰分析(Multiple Regression)
目的
複数の説明変数が、目的変数(TFR)にそれぞれどれだけ影響するかを同時に推定する。
読み方
偏回帰係数の符号・大きさ、p値で有意性、95%C.I.で幅を見る。
注意点
隣接年齢の転入超過率は相関しやすく多重共線性が出る。VIFを確認する(本研究は20-24歳でVIF=6.5とやや高め)。
ランダムフォレスト(変数重要度)
目的
非線形・交互作用も拾いつつ、どの特徴量が予測に効くかを重要度で把握する。
注意点
重要度は寄与の大きさのみで符号を示さない。重回帰の係数と併用して向きを補う。相関の強い変数間で重要度が分散することもある。
単回帰分析(Simple Regression)
目的
1つの説明変数(人口あたり学生数)とTFRの関係を素朴に把握する。
注意点
他要因を統制しないため交絡に注意。因果の主張には使えない。方向性の確認や仮説の入口として用いる。

🚀 発展の可能性

この研究をさらに広げる方向性を挙げます。

1. TFR-Bを最新年へ拡張
アイデア
自作のTFR-Bは最新データで2018年以降も計算できる。近年の社会増減とTFRの関係を追跡する。
使うデータ
住民基本台帳(女性人口)+人口動態統計(出生数)をe-Statから。
2. 他都道府県のローカル分析
アイデア
京都府と同様の市区町村別分析を、大学の多い他府県(東京都・愛知県など)にも展開し、効く年齢層の違いを比較する。
3. 未婚化・晩婚化変数の追加
アイデア
結論が指摘する「未婚化・晩婚化」を、有配偶率・平均初婚年齢などの変数として重回帰に加え、社会増減と切り分ける。
4. パネル固定効果で時系列を統制
アイデア
4年分のデータをパネルとして扱い、固定効果で地域固有の差を除いたうえで転入超過の効果を推定する。

🎯 自分でやってみよう

SSDSE-Bだけでできる練習問題です(【実再現】の範囲)。

難易度 ★☆☆☆☆
別の年でTFRランキングを描く
YEAR = 20172023 などに変え、図1を作り直そう。京都府の順位は変わる?
難易度 ★★☆☆☆
学生数単回帰を別年で確認
図2の単回帰を2023年で回し、係数の符号と有意性が保たれるか確かめよう(本文の2023年参考値:係数−0.0058, p=0.004)。
難易度 ★★★☆☆
高齢化率とTFRの相関
SSDSE-Bの65歳以上人口(A1303等)から高齢化率を作り、TFRとの相関を計算しよう。都市度との交絡に注意。
難易度 ★★★★☆
複数年をまとめて分析
2014-2017の4年を縦に結合し、年ダミーを入れてTFRと学生数の関係を推定してみよう。
💡 ヒント:列名は英字コード(A4103=TF, A1101=総人口, E6301/E6302=学生数)。skiprows=[1] で日本語名の行を飛ばして読み込みます。

💼 実社会での応用

この研究の考え方が生きる場面。

🏙️
自治体の人口・子育て政策評価
TFRの公表値だけで施策効果を判断せず、社会増減を除いた「素のTFR」で評価する。指標の落とし穴を避けられる。
🎓
大学立地と地域指標
学生流入が地域統計(TFR・年齢構成・消費)に与える見かけの影響を切り分け、大学都市の実像を捉える。
📐
公的統計の指標設計
「分母に何を使うか」で指標が変わる。代替指標(TFR-B)を作って妥当性を検証する作法は、EBPM実務で有用。

🤔 Q&A

読者が抱きやすい疑問に答えます。

Q. なぜ京都府はTFRが低いのですか?
A. 大学が多く学生(特に15-19歳の進学期)の女性が多く流入するため、TFRの分母が膨らんで値が低く出ます。社会増減の影響を除くと京都府のTFRはほぼ全国並み(1.42)でした。
Q. 市区町村別のTFRを自作したのはなぜ?
A. 公表の市区町村別TFR-Aは5年平均・分母5年固定で単年の動きを追えません。そこで住民基本台帳の女性人口を分母にしたTFR-Bを作り、公表値との相関(R²≈0.79〜0.86)で妥当性を確かめました。
Q. 重回帰とランダムフォレストで結果が違うのはなぜ?
A. 重回帰は線形で符号(向き)が出るのに対し、ランダムフォレストの重要度は寄与の大きさだけを示し向きは出ません。京都府では25-29歳の重要度が最大でしたが、向きは重回帰の係数(正)で補って解釈します。
Q. 「特徴量ゼロモデル」とは何ですか?
A. 説明変数(各年齢の転入超過率)をすべて0にしたときの推定値、つまり切片です。社会増減の影響を取り除いたTFRと解釈でき、京都府で1.42、全国並みでした。

✅ 理解度チェック(4問)

この論文を理解できたか、クリックで確認しましょう。間違えても解説が出ます。

🐍 ブラウザで動かす — インストール不要でこの論文の分析を再現する

このページの分析は、この画面の中でそのまま実行できます。 Python をインストールする必要も、CSV をダウンロードする必要もありません。 下のセルの 「▶ ブラウザで実行」 を上から順に押すか、 「▶ 最初から全部実行」 で一気に流してください。 表示されるのは、本文の図表とまったく同じ計算の結果です (動かしているのは再現スクリプト code/2020_U5_4_shorei.py そのもの)。

コードは書き換えられます。「✏️ 書き換える」で数字や変数を変えて ▶ を押すと、 結果がどう変わるかをその場で確かめられます(元に戻すボタンつき)。 ページを開いた時点で裏で実行環境の準備が始まっているので、待ち時間はほとんどありません。 初回だけ実行環境の取得にインターネット接続が必要です。