このページの分析を自分で再現するには、以下の手順でデータを準備してください。コードの編集は不要です。
data/raw/ フォルダに入れます。html/figures/ に自動保存されます。
文部科学省の体力・運動能力調査によれば、子どもの体力水準は1985年頃をピークに低下傾向が続き、近年ようやく下げ止まりの兆しが見えているものの、依然として課題は多い。体力・運動能力は子どもの健康・学習・社会性の基盤であり、その地域格差を統計的に解明することは重要な政策課題である。
まず「子どもの体力・運動能力と地域環境・家庭環境の関係分析」を統計的にとらえることが有効だと考えられる。 その理由は感覚や経験則だけでは、複雑な社会要因の中で「何が本当に効いているか」を見極めにくいからである。 本研究では公開データと統計手法を組み合わせ、この問いに定量的な答えを出すことを目指す。
本研究では、体力・運動能力の直接測定データの代わりに、SSDSE-B(都道府県別統計データ)から利用可能な代理変数を構築し、子どもを取り巻く学校・家庭・地域環境の相互関係を統計的に分析する。
SSDSE-B 2022年 Pearson相関 OLS重回帰 地域比較 政策提言
SSDSE-B(社会・人口統計体系)の都道府県データを使用。2022年度断面の47都道府県データから、子ども関連の代理変数と地域・家庭環境変数を構築した。
体力・運動能力の直接指標はSSSSDE-Bに含まれないため、以下の代理変数を構築する。
| カテゴリ | 変数名 | 計算式 | 解釈 |
|---|---|---|---|
| 子ども関連指標 (目的変数候補) |
学校当たり児童数 | 小学校児童数 / 小学校数 | 学校規模(都市部は大・活発な運動環境の代理) |
| 15歳未満人口比率 | 15歳未満人口 / 総人口 × 100 | 地域の子ども密度(%) | |
| 家庭の経済・文化環境 | 教育費比率 | 教育費 / 消費支出 × 100 | 教育投資の積極性 |
| 教養娯楽費比率 | 教養娯楽費 / 消費支出 × 100 | スポーツ・文化活動支出の代理 | |
| 地域の保育環境 | 保育所密度 | 保育所等数 / 総人口 × 10,000 | 1万人当たり保育所数(保育充実度) |
| 保育所定員率 | 保育所等定員数 / 15歳未満人口 × 100 | 子ども人口に対する保育受け入れ能力 | |
| 少子化指標 | 合計特殊出生率 | (統計値をそのまま使用) | 地域の出生傾向・家族形成の積極性 |
| 所得水準 | 消費支出(万円) | 消費支出(二人以上世帯)/ 10,000 | 世帯の経済的豊かさの代理 |
| 地域ブロック | 平均(%) | 最小(%) | 最大(%) | 特徴 |
|---|---|---|---|---|
| 九州・沖縄 | 13.14 | 11.83 | 16.35 | 最も子どもが多い。沖縄が突出 |
| 近畿 | 11.69 | 11.06 | 13.20 | 滋賀・奈良が比較的高い |
| 中部 | 11.64 | 10.91 | 12.65 | 愛知・福井が高め |
| 中国・四国 | 11.56 | 10.65 | 12.32 | ほぼ全国平均水準 |
| 関東 | 11.33 | 10.93 | 11.54 | 均質性が高い。東京は平均的 |
| 北海道・東北 | 10.51 | 9.25 | 11.32 | 最も少ない。少子高齢化が深刻 |
測定困難な概念(子どもの運動環境・体力)を扱う際、直接測定値の代わりに代理変数を使う手法は統計分析の基本技術。重要なのは「代理変数がターゲット概念をどの程度捉えられているか」を理論的に説明する論拠である。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | import os 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 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' 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} のように書式も指定できます。17 18 19 20 21 22 23 24 | # ── データ読込 ──────────────────────────────────────── df_b = pd.read_csv(DATA_B, encoding='cp932', header=1) df_b = df_b[df_b['地域コード'].str.match(r'^R\d{5}', na=False)].copy() df_b['年度'] = df_b['年度'].astype(int) # 2022年断面 df = df_b[df_b['年度'] == 2022].copy().reset_index(drop=True) assert len(df) == 47, f"47都道府県が揃っていません: {len(df)}行" |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。pd.read_csv(...) でCSVを読み込みます。encoding='cp932' は日本語Windows由来の文字コード、header=1 は「2行目を列名として使う」。df['地域コード'].str.match(r'^R\d{5}', ...) — 正規表現で「R+数字5桁」の行(47都道府県)だけTrueにし、真偽値で行をフィルタ。.astype(int) — 列を整数に変換(年度などを数値比較するため)。df['A'] / df['B'] — pandasの列同士の四則演算は要素ごと(element-wise)。forループ不要なのが強み。25 26 27 28 29 30 | # ── 変数作成 ───────────────────────────────────────── # 子ども関連指標(体力・運動環境の代理変数) df['学校当たり児童数'] = df['小学校児童数'] / df['小学校数'] # 学校規模(大→都市型・活発) df['15歳未満人口比率'] = df['15歳未満人口'] / df['総人口'] * 100 # 子ども人口の豊かさ df['教育費比率'] = df['教育費(二人以上の世帯)'] / df['消費支出(二人以上の世帯)'] * 100 df['教養娯楽費比率'] = df['教養娯楽費(二人以上の世帯)'] / df['消費支出(二人以上の世帯)'] * 100 |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。31 32 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 60 61 62 63 64 65 66 67 68 69 70 | # 地域・家庭環境指標 df['保育所密度'] = df['保育所等数'] / df['総人口'] * 10000 # 1万人当たり保育所数 df['保育所定員率'] = df['保育所等定員数'] / df['15歳未満人口'] * 100 df['出生率'] = df['合計特殊出生率'] df['消費支出_万円'] = df['消費支出(二人以上の世帯)'] / 10000 # 万円単位 # 地域区分 region_map = { '北海道': '北海道・東北', '青森県': '北海道・東北', '岩手県': '北海道・東北', '宮城県': '北海道・東北', '秋田県': '北海道・東北', '山形県': '北海道・東北', '福島県': '北海道・東北', '茨城県': '関東', '栃木県': '関東', '群馬県': '関東', '埼玉県': '関東', '千葉県': '関東', '東京都': '関東', '神奈川県': '関東', '新潟県': '中部', '富山県': '中部', '石川県': '中部', '福井県': '中部', '山梨県': '中部', '長野県': '中部', '岐阜県': '中部', '静岡県': '中部', '愛知県': '中部', '三重県': '近畿', '滋賀県': '近畿', '京都府': '近畿', '大阪府': '近畿', '兵庫県': '近畿', '奈良県': '近畿', '和歌山県': '近畿', '鳥取県': '中国・四国', '島根県': '中国・四国', '岡山県': '中国・四国', '広島県': '中国・四国', '山口県': '中国・四国', '徳島県': '中国・四国', '香川県': '中国・四国', '愛媛県': '中国・四国', '高知県': '中国・四国', '福岡県': '九州・沖縄', '佐賀県': '九州・沖縄', '長崎県': '九州・沖縄', '熊本県': '九州・沖縄', '大分県': '九州・沖縄', '宮崎県': '九州・沖縄', '鹿児島県': '九州・沖縄', '沖縄県': '九州・沖縄' } region_colors = { '北海道・東北': '#4e9af1', '関東': '#e05c5c', '中部': '#f0a500', '近畿': '#5cb85c', '中国・四国': '#9b59b6', '九州・沖縄': '#f39c12' } df['地域区分'] = df['都道府県'].map(region_map) df['地域色'] = df['地域区分'].map(region_colors) print("=" * 60) print("SSDSE-B 2022年断面 47都道府県 基本統計") print("=" * 60) target = '学校当たり児童数' features = ['15歳未満人口比率', '教育費比率', '教養娯楽費比率', '保育所密度', '保育所定員率', '出生率', '消費支出_万円'] analysis_cols = [target] + features print(df[analysis_cols].describe().round(2)) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。.describe() — 件数・平均・標準偏差・四分位・最大/最小を一括計算。データの素性チェックに必須。[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。71 72 73 74 75 76 | # ── 相関分析 ────────────────────────────────────────── print("\n── Pearson相関(目的変数: 学校当たり児童数)──") for feat in features: r, p = stats.pearsonr(df[target], df[feat]) sig = '***' if p < 0.001 else ('**' if p < 0.01 else ('*' if p < 0.05 else '')) print(f" {feat:20s}: r={r:+.3f}, p={p:.4f} {sig}") |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。stats.pearsonr(x, y) — Pearson相関係数 r と p値を同時に返します。r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。77 78 79 80 81 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 | # ── 重回帰分析 ─────────────────────────────────────── print("\n── OLS重回帰(目的変数: 学校当たり児童数)──") X = sm.add_constant(df[features]) model = sm.OLS(df[target], X).fit() print(model.summary()) # 標準化偏回帰係数 df_z = df[analysis_cols].apply(lambda s: (s - s.mean()) / s.std()) Xz = sm.add_constant(df_z[features]) model_z = sm.OLS(df_z[target], Xz).fit() beta = model_z.params.drop('const') pval = model_z.pvalues.drop('const') print("\n── 標準化偏回帰係数 ──") for feat in features: print(f" {feat:20s}: β={beta[feat]:+.3f}, p={pval[feat]:.4f}") print(f"\nR²={model.rsquared:.3f}, 調整済みR²={model.rsquared_adj:.3f}, F={model.fvalue:.2f}, p={model.f_pvalue:.4f}") fig1, ax1 = plt.subplots(figsize=(10, 7)) for region, grp in df.groupby('地域区分'): col = region_colors[region] ax1.scatter(grp['15歳未満人口比率'], grp['学校当たり児童数'], color=col, label=region, s=70, alpha=0.85, zorder=3, edgecolors='white', linewidths=0.5) for _, row in grp.iterrows(): ax1.annotate(row['都道府県'].replace('県', '').replace('府', '').replace('都', '').replace('道', ''), (row['15歳未満人口比率'], row['学校当たり児童数']), fontsize=6.5, ha='left', va='bottom', xytext=(2, 2), textcoords='offset points', alpha=0.8) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。df.groupby('列').apply(関数) — グループごとに関数を適用。時系列や地域別の集計でよく使います。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。for _, row in df.iterrows() — DataFrameを1行ずつ取り出すループ。1点ずつ描画したいときに使用。sm.add_constant(X) — 切片項(定数1の列)を先頭に追加。statsmodelsで必須。sm.OLS(y, X).fit() — 最小二乗法でモデルを推定。model.params, model.pvalues, model.conf_int() で結果取得。.apply(lambda x: ...) — その場限りの無名関数を適用。短いロジックなら def せずに書けます。x if cond else y は三項演算子。リスト内包表記と組み合わせると、forとifを1行で書けます。107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | # 回帰線 x_vals = df['15歳未満人口比率'].values y_vals = df['学校当たり児童数'].values slope, intercept, r_val, p_val, _ = stats.linregress(x_vals, y_vals) x_line = np.linspace(x_vals.min(), x_vals.max(), 100) ax1.plot(x_line, intercept + slope * x_line, 'k--', linewidth=1.5, label=f'回帰線 (r={r_val:.2f}, p={p_val:.3f})', zorder=2) ax1.set_xlabel('15歳未満人口比率(%)', fontsize=12) ax1.set_ylabel('学校当たり児童数(人)', fontsize=12) ax1.set_title('散布図:子ども人口比率と学校規模の関係\n(2022年・47都道府県)', fontsize=13, fontweight='bold') ax1.legend(loc='upper left', fontsize=9, framealpha=0.9) ax1.grid(True, alpha=0.3, linestyle='--') ax1.spines[['top', 'right']].set_visible(False) fig1.tight_layout() fig1.savefig(os.path.join(FIG_DIR, '2020_U5_5_fig1.png'), dpi=150, bbox_inches='tight') plt.close(fig1) print("\n図1 保存完了: 2020_U5_5_fig1.png") |
============================================================
SSDSE-B 2022年断面 47都道府県 基本統計
============================================================
学校当たり児童数 15歳未満人口比率 教育費比率 教養娯楽費比率 保育所密度 保育所定員率 出生率 消費支出_万円
count 47.00 47.00 47.00 47.00 47.00 47.00 47.00 47.00
mean 281.19 11.67 3.63 8.94 2.76 22.80 1.36 28.96
std 86.82 1.07 1.28 0.92 0.72 4.74 0.15 1.92
min 137.99 9.25 1.95 6.49 1.71 15.50 1.04 24.51
25% 213.55 11.08 2.83 8.32 2.16 19.18 1.25 27.68
50% 266.85 11.44 3.44 9.00 2.45 22.02 1.36 28.78
75% 329.54 12.11 3.97 9.51 3.30 25.39 1.46 30.23
max 506.39 16.35 8.61 10.91 4.51 35.93 1.70 32.48
── Pearson相関(目的変数: 学校当たり児童数)──
15歳未満人口比率 : r=+0.268, p=0.0684
教育費比率 : r=+0.577, p=0.0000 ***
教養娯楽費比率 : r=+0.552, p=0.0001 ***
保育所密度 : r=-0.509, p=0.0003 ***
保育所定員率 : r=-0.590, p=0.0000 ***
出生率 : r=-0.385, p=0.0076 **
消費支出_万円 : r=+0.435, p=0.0023 **
── OLS重回帰(目的変数: 学校当たり児童数)──
OLS Regression Results
==============================================================================
Dep. Variable: 学校当たり児童数 R-squared: 0.749
Model: OLS Adj. R-squared: 0.704
Method: Least Squares F-statistic: 16.62
Date: Mon, 18 May 2026 Prob (F-statistic): 6.27e-10
Time: 11:23:46 Log-Likelihood: -243.51
No. Observations: 47 AIC: 503.0
Df Residuals: 39 BIC: 517.8
Df Model: 7
Covariance Type: nonrobust
==============================================================================
coef std err t P>|t| [0.025 0.975]
------------------------------------------------------------------------------
const -230.9829 183.659 -1.258 0.216 -602.469 140.503
15歳未満人口比率 61.0317 10.894 5.602 0.000 38.996 83.067
教育費比率 8.3751 7.291 1.149 0.258 -6.372 23.122
教養娯楽費比率 22.2236 9.860 2.254 0.030 2.280 42.167
保育所密度 -22.4902 18.438 -1.220 0.230 -59.785 14.804
保育所定員率 0.0667 2.574 0.026 0.979 -5.140 5.274
出生率 -336.0968 88.658 -3.791 0.001 -515.425 -156.769
消費支出_万円 3.0285 4.872 0.622 0.538 -6.826 12.883
==============================================================================
Omnibus: 1.153 Durbin-Watson: 2.163
Prob(Omnibus): 0.562 Jarque-Bera (JB): 1.095
Skew: 0.348 Prob(JB): 0.578
Kurtosis: 2.729 Cond. No. 1.07e+03
==============================================================================
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large, 1.07e+03. This might indicate that there are
strong multicollinearity or other numerical problems.
── 標準化偏回帰係数 ──
15歳未満人口比率 : β=+0.755, p=0.0000
教育費比率 : β=+0.124, p=0.2577
教養娯楽費比率 : β=+0.237, p=0.0299
保育所密度
…(長いため省略)stats.linregress(x, y) — 単回帰の傾き・切片・r値・p値・標準誤差を返します。使わない値は _ で受け取り。df[col](1列)と df[[col1, col2]](複数列)でカッコの数が違います。リストを渡していると覚えるとミスを減らせます。まず全変数間のPearson相関係数を確認し、多重共線性や重要な関連パターンを把握する。
| 変数 | 相関係数 r | 有意水準 | 方向性 |
|---|---|---|---|
| 教育費比率 | +0.577 | ***(p<0.001) | 正:教育費が多い都市ほど学校規模が大 |
| 教養娯楽費比率 | +0.552 | ***(p<0.001) | 正:スポーツ文化費が多いほど学校規模大 |
| 保育所定員率 | −0.590 | ***(p<0.001) | 負:保育所が充実した地域は学校規模が小 |
| 保育所密度 | −0.509 | ***(p<0.001) | 負:地方ほど学校が小規模で保育所密度高い |
| 消費支出(万円) | +0.435 | **(p<0.01) | 正:所得の高い都市で大規模校が多い |
| 出生率 | −0.385 | **(p<0.01) | 負:出生率の高い地方は学校規模が小さい |
| 15歳未満人口比率 | +0.268 | n.s.(p=0.068) | 弱い正の傾向(有意水準 0.1 で有意) |
| 変数 | 非標準化係数 | 標準化係数 (β) | p値 | 有意性 |
|---|---|---|---|---|
| (定数) | −230.98 | — | 0.216 | — |
| 15歳未満人口比率 | +61.03 | +0.755 | <0.001 | *** |
| 教養娯楽費比率 | +22.22 | +0.237 | 0.030 | * |
| 出生率 | −336.10 | −0.578 | 0.001 | ** |
| 教育費比率 | +8.38 | +0.124 | 0.258 | n.s. |
| 保育所密度 | −22.49 | −0.186 | 0.230 | n.s. |
| 消費支出(万円) | +3.03 | +0.067 | 0.538 | n.s. |
| 保育所定員率 | +0.07 | +0.004 | 0.979 | n.s. |
*** p<0.001, ** p<0.01, * p<0.05。分析ソフト: Python statsmodels OLS。
異なる単位を持つ変数の効果の「大きさ」を比較するには、各変数を標準化(平均0・分散1)した上で回帰する。得られるβ係数は「他の変数を固定したとき、当該変数が1標準偏差増えると目的変数が何標準偏差変化するか」を表し、変数間の相対的な重要度を比較できる。
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 157 158 159 160 161 162 163 | heatmap_cols = ['学校当たり児童数', '15歳未満人口比率', '教育費比率', '教養娯楽費比率', '保育所密度', '保育所定員率', '出生率', '消費支出_万円'] heatmap_labels = ['学校当たり\n児童数', '15歳未満\n人口比率', '教育費\n比率', '教養娯楽費\n比率', '保育所\n密度', '保育所\n定員率', '出生率', '消費支出\n(万円)'] corr_mat = df[heatmap_cols].corr() fig2, ax2 = plt.subplots(figsize=(9, 7)) n = len(heatmap_cols) import matplotlib.colors as mcolors cmap = plt.cm.RdBu_r norm = mcolors.Normalize(vmin=-1, vmax=1) im = ax2.imshow(corr_mat.values, cmap=cmap, norm=norm, aspect='auto') plt.colorbar(im, ax=ax2, fraction=0.046, pad=0.04, label='Pearson r') ax2.set_xticks(range(n)) ax2.set_yticks(range(n)) ax2.set_xticklabels(heatmap_labels, fontsize=9) ax2.set_yticklabels(heatmap_labels, fontsize=9) for i in range(n): for j in range(n): val = corr_mat.values[i, j] r, p = stats.pearsonr(df[heatmap_cols[i]], df[heatmap_cols[j]]) star = '***' if p < 0.001 else ('**' if p < 0.01 else ('*' if p < 0.05 else '')) text_color = 'white' if abs(val) > 0.6 else 'black' ax2.text(j, i, f'{val:.2f}{star}', ha='center', va='center', fontsize=8.5, color=text_color, fontweight='bold' if p < 0.05 else 'normal') ax2.set_title('相関ヒートマップ:子ども・地域環境関連変数\n(2022年・47都道府県、*p<0.05, **p<0.01, ***p<0.001)', fontsize=12, fontweight='bold') fig2.tight_layout() fig2.savefig(os.path.join(FIG_DIR, '2020_U5_5_fig2.png'), dpi=150, bbox_inches='tight') plt.close(fig2) print("図2 保存完了: 2020_U5_5_fig2.png") |
図2 保存完了: 2020_U5_5_fig2.png
import pandas as pd など — 必要なライブラリをまとめて呼び出します。as pd は短い別名(alias)。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。stats.pearsonr(x, y) — Pearson相関係数 r と p値を同時に返します。df['A'] / df['B'] — pandasの列同士の四則演算は要素ごと(element-wise)。forループ不要なのが強み。地域ブロック別の子ども関連指標を比較することで、全国的な傾向と地方ごとの特徴を明らかにする。
| 指標 | 沖縄県 (子ども最多) | 秋田県 (子ども最少) | 東京都 (都市型) |
|---|---|---|---|
| 15歳未満人口比率 | 16.35% | 9.25% | 10.93% |
| 合計特殊出生率 | 1.70 | 1.18 | 1.04 |
| 学校当たり児童数 | 338.5人 | 214.2人 | 506.4人 |
| 保育所密度(1万人当たり) | 4.51 | 2.88 | 1.71 |
| 教養娯楽費比率 | 8.42% | 8.81% | 10.91% |
| 教育費比率 | 2.39% | 3.11% | 8.61% |
相関分析や重回帰分析は変数間の統計的関連を示すが、因果関係を証明しない。たとえば「教養娯楽費比率が高い都道府県で学校規模が大きい」は、「娯楽費が学校規模を増やす」のではなく、「都市部という第三の要因が両者を同時に増加させている」可能性(交絡因子)がある。因果推論には操作変数・差分の差分・ランダム化実験などの手法が必要。
165 166 167 168 169 170 171 172 | beta_vals = beta.values pval_vals = pval.values feat_labels_short = ['15歳未満\n人口比率', '教育費\n比率', '教養娯楽費\n比率', '保育所\n密度', '保育所\n定員率', '出生率', '消費支出\n(万円)'] # 信頼区間(95%) ci_lower = model_z.conf_int(alpha=0.05).loc[features, 0].values ci_upper = model_z.conf_int(alpha=0.05).loc[features, 1].values |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。173 174 175 176 177 178 179 180 181 182 183 184 185 186 | # 大きい順に並び替え sort_idx = np.argsort(beta_vals) beta_sorted = beta_vals[sort_idx] pval_sorted = pval_vals[sort_idx] labels_sorted = [feat_labels_short[i] for i in sort_idx] ci_lo_sorted = ci_lower[sort_idx] ci_hi_sorted = ci_upper[sort_idx] colors_bar = ['#e05c5c' if v > 0 else '#4e9af1' for v in beta_sorted] edge_colors = ['#c62828' if p < 0.05 else '#999' for p in pval_sorted] fig3, ax3 = plt.subplots(figsize=(9, 6)) bars = ax3.barh(range(len(beta_sorted)), beta_sorted, color=colors_bar, alpha=0.75, edgecolor=edge_colors, linewidth=1.5) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 | # エラーバー xerr_lo = beta_sorted - ci_lo_sorted xerr_hi = ci_hi_sorted - beta_sorted ax3.errorbar(beta_sorted, range(len(beta_sorted)), xerr=[xerr_lo, xerr_hi], fmt='none', color='black', capsize=4, linewidth=1) # p値アノテーション for i, (b, p) in enumerate(zip(beta_sorted, pval_sorted)): star = '***' if p < 0.001 else ('**' if p < 0.01 else ('*' if p < 0.05 else 'n.s.')) x_pos = b + (xerr_hi[i] + 0.02) if b >= 0 else b - xerr_lo[i] - 0.02 ha = 'left' if b >= 0 else 'right' ax3.text(x_pos, i, star, ha=ha, va='center', fontsize=10, color='#c62828' if p < 0.05 else '#999') ax3.axvline(0, color='black', linewidth=0.8, linestyle='-') ax3.set_yticks(range(len(labels_sorted))) ax3.set_yticklabels(labels_sorted, fontsize=10) ax3.set_xlabel('標準化偏回帰係数 (β)', fontsize=12) ax3.set_title(f'標準化偏回帰係数:学校当たり児童数の決定要因\n(OLS, R²={model.rsquared:.3f}, 調整済みR²={model.rsquared_adj:.3f})', fontsize=12, fontweight='bold') ax3.grid(True, axis='x', alpha=0.3, linestyle='--') ax3.spines[['top', 'right']].set_visible(False) from matplotlib.patches import Patch legend_elements = [Patch(facecolor='#e05c5c', alpha=0.75, label='正の効果(学校規模を増大)'), Patch(facecolor='#4e9af1', alpha=0.75, label='負の効果(学校規模を縮小)')] ax3.legend(handles=legend_elements, loc='lower right', fontsize=9) fig3.tight_layout() fig3.savefig(os.path.join(FIG_DIR, '2020_U5_5_fig3.png'), dpi=150, bbox_inches='tight') plt.close(fig3) print("図3 保存完了: 2020_U5_5_fig3.png") |
図3 保存完了: 2020_U5_5_fig3.png
import pandas as pd など — 必要なライブラリをまとめて呼び出します。as pd は短い別名(alias)。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。分析結果を踏まえ、子どもの体力・運動能力向上に資する地域政策の方向性を提示する。
220 221 222 223 224 225 | df_rank = df.sort_values('15歳未満人口比率', ascending=True).reset_index(drop=True) fig4, ax4 = plt.subplots(figsize=(10, 12)) bar_colors = [region_colors[r] for r in df_rank['地域区分']] bars4 = ax4.barh(range(len(df_rank)), df_rank['15歳未満人口比率'], color=bar_colors, alpha=0.85, edgecolor='white', linewidth=0.5) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。sort_values('列名', ascending=False) — 指定列で並べ替え(降順)。[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。226 227 228 229 230 231 232 233 | # 全国平均線 nat_avg = df['15歳未満人口比率'].mean() ax4.axvline(nat_avg, color='black', linewidth=1.5, linestyle='--', label=f'全国平均: {nat_avg:.2f}%') # 都道府県名ラベル ax4.set_yticks(range(len(df_rank))) ax4.set_yticklabels(df_rank['都道府県'], fontsize=8) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。234 235 236 237 238 239 240 241 242 | # 値ラベル for i, val in enumerate(df_rank['15歳未満人口比率']): ax4.text(val + 0.05, i, f'{val:.1f}%', va='center', fontsize=7.5, color='#333') ax4.set_xlabel('15歳未満人口比率(%)', fontsize=12) ax4.set_title('都道府県別 子ども人口比率ランキング\n(2022年・15歳未満人口 / 総人口×100)', fontsize=13, fontweight='bold') ax4.spines[['top', 'right']].set_visible(False) ax4.grid(True, axis='x', alpha=0.3, linestyle='--') |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。x if cond else y は三項演算子。リスト内包表記と組み合わせると、forとifを1行で書けます。243 244 245 246 247 248 249 250 251 252 253 | # 凡例 legend_handles = [Patch(facecolor=col, label=reg, alpha=0.85) for reg, col in region_colors.items()] legend_handles.append(plt.Line2D([0], [0], color='black', linewidth=1.5, linestyle='--', label=f'全国平均: {nat_avg:.2f}%')) ax4.legend(handles=legend_handles, loc='lower right', fontsize=9, framealpha=0.9) fig4.tight_layout() fig4.savefig(os.path.join(FIG_DIR, '2020_U5_5_fig4.png'), dpi=150, bbox_inches='tight') plt.close(fig4) print("図4 保存完了: 2020_U5_5_fig4.png") |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。df[col](1列)と df[[col1, col2]](複数列)でカッコの数が違います。リストを渡していると覚えるとミスを減らせます。254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 | # ── 統計サマリー出力(HTML生成用) ──────────────────── print("\n" + "=" * 60) print("HTML生成用 統計サマリー") print("=" * 60) top3_child = df_rank.tail(3)['都道府県'].tolist() bot3_child = df_rank.head(3)['都道府県'].tolist() print(f"15歳未満人口比率 上位3位: {top3_child}") print(f"15歳未満人口比率 下位3位: {bot3_child}") print(f"全国平均 15歳未満人口比率: {nat_avg:.2f}%") print(f"全国平均 学校当たり児童数: {df['学校当たり児童数'].mean():.1f}人") print(f"全国平均 保育所密度: {df['保育所密度'].mean():.2f}(1万人当たり)") print(f"全国平均 合計特殊出生率: {df['出生率'].mean():.2f}") print(f"学校当たり児童数 vs 15歳未満人口比率: r={stats.pearsonr(df['学校当たり児童数'], df['15歳未満人口比率'])[0]:.3f}") print(f"R²={model.rsquared:.3f}, 調整済みR²={model.rsquared_adj:.3f}") print(f"有意な係数: {', '.join([f for f, p in zip(features, model.pvalues[1:]) if p < 0.05])}") |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。stats.pearsonr(x, y) — Pearson相関係数 r と p値を同時に返します。s[:-n]「末尾n文字を除く」/s[n:]「先頭n文字を除く」。スライス [start:stop:step] はリスト・タプル・文字列共通の基本ワザです。269 270 271 272 273 274 | # 地域別平均 print("\n── 地域別 15歳未満人口比率 ──") reg_stats = df.groupby('地域区分')['15歳未満人口比率'].agg(['mean', 'min', 'max']).round(2) print(reg_stats) print("\n全図生成完了。") |
図4 保存完了: 2020_U5_5_fig4.png
============================================================
HTML生成用 統計サマリー
============================================================
15歳未満人口比率 上位3位: ['佐賀県', '滋賀県', '沖縄県']
15歳未満人口比率 下位3位: ['秋田県', '青森県', '北海道']
全国平均 15歳未満人口比率: 11.67%
全国平均 学校当たり児童数: 281.2人
全国平均 保育所密度: 2.76(1万人当たり)
全国平均 合計特殊出生率: 1.36
学校当たり児童数 vs 15歳未満人口比率: r=0.268
R²=0.749, 調整済みR²=0.704
有意な係数: 15歳未満人口比率, 教養娯楽費比率, 出生率
── 地域別 15歳未満人口比率 ──
mean min max
地域区分
中国・四国 11.56 10.65 12.32
中部 11.64 10.91 12.65
九州・沖縄 13.14 11.83 16.35
北海道・東北 10.51 9.25 11.32
近畿 11.69 11.06 13.20
関東 11.33 10.93 11.54
全図生成完了。df.groupby('列').apply(関数) — グループごとに関数を適用。時系列や地域別の集計でよく使います。np.cumsum(arr) は累積和、np.linspace(a, b, n) は「aからbを等間隔でn個」。NumPyの定石です。本研究では SSDSE-B の2022年断面データ(47都道府県)を用い、子どもの体力・運動環境の代理変数として「学校当たり児童数」を中心に、地域・家庭環境との関係を相関分析・重回帰分析で定量化した。
| 項目 | 内容 |
|---|---|
| 意義 | 公的統計データのみで子どもの運動環境を都道府県レベルで比較分析。地域格差の統計的根拠を提示。 |
| 課題1 | 体力測定の直接データが入手できず、代理変数に依存。変数の妥当性(構成概念妥当性)に課題。 |
| 課題2 | 断面データのため時系列変化や因果方向の確定が難しい。パネル分析(時系列×都道府県)が望ましい。 |
| 課題3 | 都道府県レベルの分析は市区町村レベルの格差を平均化している(生態学的誤謬のリスク)。 |
| 発展的方向 | スポーツ庁の体力・運動能力調査データとの統合、市区町村レベルのSSSDSE-Aとの組み合わせ分析。 |
都道府県単位の集計データで見られる相関は、個人レベルでは必ずしも成立しない。たとえば「教養娯楽費比率が高い都道府県の子どもが体力が高い」という関係が都道府県単位で見られても、その都道府県内の個人レベルでは逆の関係が存在することもある。集計データの分析では、この「生態学的誤謬」に常に注意が必要。
統計分析の解釈で初心者がやりがちな勘違いをまとめます。特に「相関と因果の混同」「p値の過信」は研究現場でもよく起きる落とし穴です。本文を読む前にも、読んだ後にも、目を通してみてください。
統計の基本用語を初心者向けに解説します。本文中で見慣れない言葉が出てきたら、ここに戻って確認してください。
統計手法について「何のためか」「結果をどう読むか」を初心者向けに解説します。
この研究をさらに発展させるための3つの方向性を示します。「今回わかったこと(X)」から「次に検証すべき仮説(Y)」を立て、「具体的に何をするか(Z)」まで考えてみましょう。
学んだだけでは身につきません。実際に手を動かすのが最強の学習方法です。本論文のスクリプトをベースに、以下のチャレンジに挑戦してみてください。難易度別に5つ用意しました。
本論文で学んだ手法は、研究の世界だけでなく、行政・企業・NPO の現場でも様々に活用されています。具体的なシーンを紹介します。
この論文を読んで初心者が抱きやすい疑問に、教育的観点から答えます。