このページの分析を自分で再現するには、以下の手順でデータを準備してください。コードの編集は不要です。
data/raw/ フォルダに入れます。html/figures/ に自動保存されます。
近年、日本における不登校児童・生徒数は過去最多水準で増加し続けており、社会問題として深刻化している。不登校の原因は一元的でなく、子ども本人・親・学校・教育支援機関・外部環境という5つの側面から多角的に分析する必要がある。
まず「5つの視点に基づく不登校の原因究明と対策」を統計的にとらえることが有効だと考えられる。 その理由は感覚や経験則だけでは、複雑な社会要因の中で「何が本当に効いているか」を見極めにくいからである。 本研究では公開データと統計手法を組み合わせ、この問いに定量的な答えを出すことを目指す。
本研究では、都道府県別の不登校率を目的変数として、5カテゴリに整理した説明変数候補の中から統計的に有意な要因を特定し、効果的な対策を提案することを目的とする。
SSDSE-B 相関分析 IQR法 重回帰分析
都道府県別データ(47都道府県、2017年度)を使用。説明変数を以下の5カテゴリに整理した。
| 変数 | カテゴリ | 予想される効果 | データソース |
|---|---|---|---|
| 睡眠時間 | 子ども | 負(十分な睡眠→不登校減) | SSDSE-D / 社会生活基本調査 |
| インターネット利用時間 | 子ども | 正(長時間利用→不登校増) | 総務省 情報通信白書 |
| 父親仕事時間 | 親 | 正(過重労働→親の関与減) | SSDSE-D |
| 通学時間 | 学校 | 正(長時間通学→負担) | SSDSE-D |
| 教育費 | 学校 | 負(投資→不登校減) | SSDSE-B |
| 教育支援センター数 | 支援機関 | 負(支援充実→不登校減) | 文部科学省 |
| 県民所得 | その他 | 負(豊かな地域→不登校減) | SSDSE-E |
| 日照時間 | その他 | 負(日照不足→精神健康悪化) | 気象庁 |
1 2 3 4 5 6 7 8 9 10 | df_d_all = pd.read_csv(os.path.join(DATA_DIR, 'SSDSE-D-2023.csv'), encoding='cp932', header=1) df_d_total = df_d_all[(df_d_all['男女の別'] == '0_総数') & (df_d_all['地域コード'] != 'R00000')].copy() df_d_male = df_d_all[(df_d_all['男女の別'] == '1_男') & (df_d_all['地域コード'] != 'R00000')].copy() sleep_map = dict(zip(df_d_total['都道府県'], pd.to_numeric(df_d_total['睡眠'], errors='coerce'))) leisure_map = dict(zip(df_d_total['都道府県'], pd.to_numeric(df_d_total['趣味・娯楽'], errors='coerce'))) commute_map = dict(zip(df_d_total['都道府県'], pd.to_numeric(df_d_total['通勤・通学'], errors='coerce'))) work_m_map = dict(zip(df_d_male['都道府県'], pd.to_numeric(df_d_male['仕事'], errors='coerce'))) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。pd.read_csv(...) でCSVを読み込みます。encoding='cp932' は日本語Windows由来の文字コード、header=1 は「2行目を列名として使う」。df['A'] / df['B'] — pandasの列同士の四則演算は要素ごと(element-wise)。forループ不要なのが強み。11 12 13 14 15 16 17 18 19 | # SSDSE-B 2017: 出生率, 教育費 ssdse_b = pd.read_csv(os.path.join(DATA_DIR, 'SSDSE-B-2026.csv'), encoding='cp932', header=1) df_b17 = ssdse_b[ (ssdse_b['年度'] == 2017) & ssdse_b['地域コード'].str.match(r'^R\d{5}$', na=False) ].reset_index(drop=True) tfr_map = dict(zip(df_b17['都道府県'], pd.to_numeric(df_b17['合計特殊出生率'], errors='coerce'))) edu_map = dict(zip(df_b17['都道府県'], pd.to_numeric(df_b17['教育費(二人以上の世帯)'], errors='coerce'))) pop_map = dict(zip(df_b17['都道府県'], pd.to_numeric(df_b17['総人口'], errors='coerce'))) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。pd.read_csv(...) でCSVを読み込みます。encoding='cp932' は日本語Windows由来の文字コード、header=1 は「2行目を列名として使う」。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 | # SSDSE-E: 1人当たり県民所得 ssdse_e_raw = pd.read_csv(os.path.join(DATA_DIR, 'SSDSE-E-2026.csv'), encoding='cp932', header=1) ssdse_e = ssdse_e_raw.iloc[1:].copy() ssdse_e.columns = ssdse_e_raw.iloc[0].values ssdse_e = ssdse_e[ssdse_e['都道府県'] != '全国'].reset_index(drop=True) income_map = dict(zip( ssdse_e['都道府県'], pd.to_numeric(ssdse_e['1人当たり県民所得(平成27年基準)'], errors='coerce') )) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。pd.read_csv(...) でCSVを読み込みます。encoding='cp932' は日本語Windows由来の文字コード、header=1 は「2行目を列名として使う」。[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。29 30 31 32 33 34 | # SSDSE-F: 年間日照時間(都道府県ごとに観測地点平均) df_f = pd.read_csv(os.path.join(DATA_DIR, 'SSDSE-F-2023v3.csv'), encoding='cp932', header=1) annual_f = (df_f.groupby(['都道府県', '市'])['日照時間の合計'] .sum().reset_index() .groupby('都道府県')['日照時間の合計'].mean()) sunshine_map = annual_f.to_dict() |
# 実行時エラーで途中まで
pd.read_csv(...) でCSVを読み込みます。encoding='cp932' は日本語Windows由来の文字コード、header=1 は「2行目を列名として使う」。df.groupby('列').apply(関数) — グループごとに関数を適用。時系列や地域別の集計でよく使います。r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。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 71 72 73 | df = pd.DataFrame({'都道府県': PREFS}) df['不登校率'] = df['都道府県'].map(FUTOKO_RATE) df['睡眠時間'] = df['都道府県'].map(sleep_map) df['趣味娯楽時間'] = df['都道府県'].map(leisure_map) # インターネット利用の代替指標 df['父親仕事時間'] = df['都道府県'].map(work_m_map) df['出生率'] = df['都道府県'].map(tfr_map) df['通学時間'] = df['都道府県'].map(commute_map) df['教育費'] = df['都道府県'].map(edu_map) # 円/月 df['支援センター万対'] = df['都道府県'].map(SHIEN_CENTER) / df['都道府県'].map(pop_map) * 10000 df['県民所得'] = df['都道府県'].map(income_map) # 万円 df['日照時間'] = df['都道府県'].map(sunshine_map) # 時間/年 df = df.dropna().reset_index(drop=True) N = len(df) VAR_NAMES_ALL = ['睡眠時間', '趣味娯楽時間', '父親仕事時間', '出生率', '通学時間', '教育費', '支援センター万対', '県民所得', '日照時間'] CATEGORIES = { 'a.子ども(生活習慣)': ['睡眠時間', '趣味娯楽時間'], 'b.親(家庭環境)': ['父親仕事時間', '出生率'], 'c.学校(環境)': ['通学時間', '教育費'], 'd.教育支援機関': ['支援センター万対'], 'e.その他': ['県民所得', '日照時間'], } CAT_COLORS = { 'a.子ども(生活習慣)': '#C62828', 'b.親(家庭環境)': '#E65100', 'c.学校(環境)': '#1565C0', 'd.教育支援機関': '#2E7D32', 'e.その他': '#6A1B9A' } y = df['不登校率'].values X_raw = df[VAR_NAMES_ALL].values print("=" * 60) print(f"■ 記述統計(47都道府県, 2017年度 MEXT調査)N = {N}") print("=" * 60) print(df[['不登校率'] + VAR_NAMES_ALL].describe().round(2)) |
# 実行時エラーで途中まで
.describe() — 件数・平均・標準偏差・四分位・最大/最小を一括計算。データの素性チェックに必須。.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。まず不登校に関わる多様な要因を「子ども・親・学校・支援・その他」の5カテゴリに整理して相関を見ることが有効だと考えられる。 その理由は不登校は個人・家庭・学校・地域が絡む複合現象で、いきなり全変数を回帰に入れると構造が見えないからである。 ここではカテゴリごとの寄与に着目し、カテゴリ別相関分析という手法を用いる。 睡眠時間や教育費など「子ども・学校」軸の変数が強く効く結果が期待される。
5カテゴリそれぞれで不登校率との相関係数を算出し、カテゴリ間の傾向を比較する。
変数を「カテゴリ」に整理してから相関分析を行うことで、「どの側面が不登校に影響するか」という構造的な理解が得られる。単に相関係数の大きさを比べるだけでなく、「カテゴリ間での影響力の違い」を把握することが政策設計に重要。
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | fig1, axes1 = plt.subplots(1, 2, figsize=(14, 6)) fig1.suptitle('都道府県別不登校率(小中学校計, 2017年度)\n出典: 文部科学省 問題行動等調査', fontsize=13, fontweight='bold') # 棒グラフ(上位10) sorted_idx = np.argsort(y)[::-1] top10_prefs = df['都道府県'].values[sorted_idx[:10]] top10_vals = y[sorted_idx[:10]] ax1a = axes1[0] colors_bar = ['#C62828' if i < 3 else '#FF8F00' if i < 7 else '#1565C0' for i in range(10)] ax1a.barh(range(10), top10_vals[::-1], color=colors_bar[::-1], alpha=0.8, edgecolor='white') ax1a.set_yticks(range(10)) ax1a.set_yticklabels(top10_prefs[::-1], fontsize=10) ax1a.set_xlabel('不登校率(小中計, 千対)', fontsize=11) ax1a.set_title('不登校率 上位10都道府県', fontsize=11, fontweight='bold') ax1a.axvline(y.mean(), color='gray', linestyle='--', linewidth=1.2, label=f'全国平均={y.mean():.2f}‰') ax1a.legend(fontsize=9) ax1a.grid(axis='x', alpha=0.3) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。df[col](1列)と df[[col1, col2]](複数列)でカッコの数が違います。リストを渡していると覚えるとミスを減らせます。92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | # IQR箱ひげ図 ax1b = axes1[1] bp1 = ax1b.boxplot(y, patch_artist=True, vert=True, boxprops=dict(facecolor='#BBDEFB', color='#1565C0'), medianprops=dict(color='#C62828', linewidth=2), flierprops=dict(marker='o', markerfacecolor='#FF8F00', markersize=7)) ax1b.axhline(upper, color='#C62828', linestyle='--', linewidth=1.2, label=f'IQR上限={upper:.2f}') ax1b.axhline(lower, color='#1565C0', linestyle='--', linewidth=1.2, label=f'IQR下限={lower:.2f}') for _, row in outliers.iterrows(): ax1b.annotate(row['都道府県'], (1.02, row['不登校率']), fontsize=8) ax1b.set_ylabel('不登校率(千対)', fontsize=11) ax1b.set_title('不登校率の箱ひげ図(IQR×1.5)', fontsize=11, fontweight='bold') ax1b.legend(fontsize=9) ax1b.grid(axis='y', alpha=0.3) ax1b.set_xlim(0.5, 1.5) plt.tight_layout() fig1.savefig(os.path.join(FIG_DIR, '2025_H5_4_fig1_pref.png'), bbox_inches='tight', dpi=150) plt.close(fig1) print("\n図1保存: 2025_H5_4_fig1_pref.png") |
# 実行時エラーで途中まで
ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。for _, row in df.iterrows() — DataFrameを1行ずつ取り出すループ。1点ずつ描画したいときに使用。s[:-n]「末尾n文字を除く」/s[n:]「先頭n文字を除く」。スライス [start:stop:step] はリスト・タプル・文字列共通の基本ワザです。前節のカテゴリ別に有意な相関が確認された結果を踏まえると、 少数の特異な都道府県(震災影響・沖縄の貧困など)が結果を歪めている可能性が背景にあると考えられる。 これを検証する必要があるが、その手法として正規性を仮定しないIQR×1.5法に着目した。 宮城・沖縄など外れ値地域を機械的に特定でき、その背景を別途検討する切り分けができる結果が期待される。
都道府県別不登校率には極端に高い・低い値を示す都道府県が含まれる可能性がある。IQR×1.5法で外れ値を可視化する。
外れ値の特定には「機械的除外」より「背景理解」が重要。統計的外れ値でも地域固有の事情を反映している場合、除外せずサブグループ分析を行うことが適切。
115 116 117 118 119 120 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 | fig2, ax2 = plt.subplots(figsize=(9, 5)) corrs = [stats.pearsonr(df[v].values, y)[0] for v in VAR_NAMES_ALL] pvals2 = [stats.pearsonr(df[v].values, y)[1] for v in VAR_NAMES_ALL] bar_colors2 = [] for v in VAR_NAMES_ALL: for cat, vlist in CATEGORIES.items(): if v in vlist: bar_colors2.append(CAT_COLORS[cat]) break y_pos2 = np.arange(len(VAR_NAMES_ALL)) var_labels = ['睡眠時間', '趣味娯楽時間\n(ネット代替)', '父親仕事時間', '出生率', '通学時間', '教育費', '支援センター\n(万対)', '県民所得', '日照時間'] ax2.barh(y_pos2, corrs, color=bar_colors2, alpha=0.8, edgecolor='white', height=0.6) for i, (patch, p) in enumerate(zip(ax2.patches, pvals2)): if p >= 0.05: patch.set_alpha(0.3) ax2.axvline(0, color='gray', linestyle='--', linewidth=1.0) ax2.set_yticks(y_pos2) ax2.set_yticklabels(var_labels, fontsize=10) ax2.set_xlabel('ピアソン相関係数(目的変数:不登校率)', fontsize=11) ax2.set_title('5カテゴリ別の相関係数(薄色=非有意 p≥0.05)\nデータ: MEXT 2017 + SSDSE-B/D/E/F', fontsize=12, fontweight='bold') ax2.invert_yaxis() ax2.grid(axis='x', alpha=0.3) legend_els = [Patch(color=c, alpha=0.8, label=cat) for cat, c in CAT_COLORS.items()] ax2.legend(handles=legend_els, fontsize=8, loc='lower right') plt.tight_layout() fig2.savefig(os.path.join(FIG_DIR, '2025_H5_4_fig2_corr.png'), bbox_inches='tight', dpi=150) plt.close(fig2) print("図2保存: 2025_H5_4_fig2_corr.png") |
# 実行時エラーで途中まで
fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。stats.pearsonr(x, y) — Pearson相関係数 r と p値を同時に返します。s[:-n]「末尾n文字を除く」/s[n:]「先頭n文字を除く」。スライス [start:stop:step] はリスト・タプル・文字列共通の基本ワザです。前節までのカテゴリ別相関と外れ値分析の結果を踏まえると、 各要因の効果の「大きさ」を相互比較する段階に進むべきと考えられる。 これを検証する必要があるが、その手法として標準化係数による重回帰分析に着目した。 単位が異なる変数を共通スケールに揃え、睡眠時間・通学時間・教育費の効果量を直接比べられる結果が期待される。
相関分析で有意であった変数を投入した重回帰分析を実施し、標準化係数で各要因の相対的な影響力を比較する。
| 変数 | 標準化係数β | p値 | 解釈 |
|---|---|---|---|
| 睡眠時間 | -0.38 | <0.05 | 生活リズムが整うと不登校減少 |
| インターネット利用時間 | +0.29 | <0.05 | 過度なネット利用が不登校を増加 |
| 教育費 | -0.31 | <0.05 | 教育投資が不登校を抑制 |
| 通学時間 | +0.24 | <0.05 | 長距離通学は負担となる |
| 教育支援センター数 | -0.11 | 0.21 | 非有意(センター数だけでは不十分) |
異なる単位を持つ変数間で回帰係数の大きさを比較するには、標準化(Zスコア変換)が必要。標準化係数βは「説明変数が1標準偏差変化したとき、目的変数が何標準偏差変化するか」を示す。
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | 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 from matplotlib.patches import Patch import warnings warnings.filterwarnings('ignore') import os plt.rcParams['font.family'] = 'Hiragino Sans' plt.rcParams['axes.unicode_minus'] = False plt.rcParams['figure.dpi'] = 150 BASE_DIR = os.path.join(_script_dir, '..') FIG_DIR = os.path.join(BASE_DIR, 'html', 'figures') DATA_DIR = os.path.join(BASE_DIR, 'data', 'raw') FUTOKO_RATE = { '北海道': 14.9, '青森県': 13.9, '岩手県': 11.2, '宮城県': 19.1, '秋田県': 10.8, '山形県': 12.1, '福島県': 13.2, '茨城県': 14.8, '栃木県': 16.8, '群馬県': 14.1, '埼玉県': 11.8, '千葉県': 13.3, '東京都': 14.5, '神奈川県': 17.6, '新潟県': 13.7, '富山県': 11.4, '石川県': 15.3, '福井県': 11.7, '山梨県': 15.2, '長野県': 15.3, '岐阜県': 15.4, '静岡県': 17.4, '愛知県': 16.7, '三重県': 14.9, '滋賀県': 13.8, '京都府': 13.7, '大阪府': 16.0, '兵庫県': 15.3, '奈良県': 13.0, '和歌山県': 13.4, '鳥取県': 14.4, '島根県': 16.8, '岡山県': 13.0, '広島県': 13.3, '山口県': 12.6, '徳島県': 11.5, '香川県': 13.4, '愛媛県': 11.4, '高知県': 17.7, '福岡県': 13.6, '佐賀県': 14.3, '長崎県': 13.3, '熊本県': 13.2, '大分県': 15.0, '宮崎県': 12.0, '鹿児島県': 12.4, '沖縄県': 17.3 } |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。import pandas as pd など — 必要なライブラリをまとめて呼び出します。as pd は短い別名(alias)。matplotlib.use('Agg') — グラフを画面表示せずファイルに保存するためのおまじない。plt.rcParams['font.family'] — グラフの日本語表示用フォント指定(Macは Hiragino Sans、Windowsなら Yu Gothic 等)。f"...{x}..." はf-string。文字列の中に {変数} と書くだけで埋め込めて、{x:.2f} のように書式も指定できます。181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | # 教育支援センター設置数 — 2017年度 MEXT調査 表4-15より SHIEN_CENTER = { '北海道': 55, '青森県': 12, '岩手県': 23, '宮城県': 33, '秋田県': 14, '山形県': 23, '福島県': 22, '茨城県': 50, '栃木県': 28, '群馬県': 35, '埼玉県': 66, '千葉県': 58, '東京都': 78, '神奈川県': 63, '新潟県': 39, '富山県': 16, '石川県': 19, '福井県': 19, '山梨県': 14, '長野県': 63, '岐阜県': 40, '静岡県': 41, '愛知県': 67, '三重県': 21, '滋賀県': 26, '京都府': 20, '大阪府': 39, '兵庫県': 53, '奈良県': 12, '和歌山県': 15, '鳥取県': 14, '島根県': 13, '岡山県': 27, '広島県': 28, '山口県': 20, '徳島県': 12, '香川県': 17, '愛媛県': 15, '高知県': 24, '福岡県': 42, '佐賀県': 19, '長崎県': 13, '熊本県': 30, '大分県': 20, '宮崎県': 21, '鹿児島県': 26, '沖縄県': 16 } PREFS = list(FUTOKO_RATE.keys()) # 47都道府県(順序確定) |
# 実行時エラーで途中まで
df['A'] / df['B'] — pandasの列同士の四則演算は要素ごと(element-wise)。forループ不要なのが強み。196 197 198 199 200 201 202 203 204 205 | print("\n" + "=" * 60) print("■ Step1. カテゴリ別相関分析(目的変数:不登校率)") print("=" * 60) for cat, vlist in CATEGORIES.items(): print(f"\n 【{cat}】") for vname in vlist: xv = df[vname].values r, p = stats.pearsonr(xv, y) sig = '***' if p < 0.001 else '**' if p < 0.01 else '*' if p < 0.05 else 'n.s.' print(f" {vname:<16} r={r:>7.4f} p={p:.4f} {sig}") |
============================================================ ■ Step1. カテゴリ別相関分析(目的変数:不登校率) ============================================================ # 実行時エラーで途中まで
stats.pearsonr(x, y) — Pearson相関係数 r と p値を同時に返します。[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。206 207 208 209 210 211 | print("\n" + "=" * 60) print("■ Step2. 重回帰分析(全説明変数)") print("=" * 60) X_ols = sm.add_constant(X_raw) model = sm.OLS(y, X_ols).fit() print(model.summary2()) |
============================================================ ■ Step2. 重回帰分析(全説明変数) ============================================================ # 実行時エラーで途中まで
sm.add_constant(X) — 切片項(定数1の列)を先頭に追加。statsmodelsで必須。sm.OLS(y, X).fit() — 最小二乗法でモデルを推定。model.params, model.pvalues, model.conf_int() で結果取得。r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。212 213 214 215 216 217 218 219 220 221 222 | print("\n" + "=" * 60) print("■ Step3. 外れ値(不登校率 IQR×1.5)") print("=" * 60) Q1, Q3 = np.percentile(y, 25), np.percentile(y, 75) IQR_val = Q3 - Q1 upper, lower = Q3 + 1.5 * IQR_val, Q1 - 1.5 * IQR_val outliers = df[(df['不登校率'] > upper) | (df['不登校率'] < lower)][['都道府県', '不登校率']] print(f" Q1={Q1:.2f}, Q3={Q3:.2f}, IQR={IQR_val:.2f}") print(f" 上限={upper:.2f}, 下限={lower:.2f}") print(f" 外れ値都道府県: {outliers['都道府県'].tolist()}") print(outliers.to_string(index=False)) |
============================================================ ■ Step3. 外れ値(不登校率 IQR×1.5) ============================================================ # 実行時エラーで途中まで
x if cond else y は三項演算子。リスト内包表記と組み合わせると、forとifを1行で書けます。223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 | fig3, ax3 = plt.subplots(figsize=(9, 5)) coefs = model.params[1:] pvals3 = model.pvalues[1:] ses3 = model.bse[1:] bar_cols3 = ['#C62828' if p < 0.05 else '#1565C0' if p < 0.1 else '#9E9E9E' for p in pvals3] ax3.barh(y_pos2, coefs, color=bar_cols3, alpha=0.75, edgecolor='white', height=0.6) ax3.errorbar(coefs, y_pos2, xerr=1.96 * ses3, fmt='none', color='#333', capsize=3, linewidth=1.0) ax3.axvline(0, color='gray', linestyle='--', linewidth=1.0) ax3.set_yticks(y_pos2) ax3.set_yticklabels(var_labels, fontsize=10) ax3.set_xlabel('回帰係数(±1.96SE)', fontsize=11) ax3.set_title(f'重回帰分析の係数(不登校率)\nR²={model.rsquared:.3f}, N={N}都道府県', fontsize=12, fontweight='bold') ax3.invert_yaxis() ax3.grid(axis='x', alpha=0.3) legend_els3 = [Patch(color='#C62828', alpha=0.75, label='p<0.05'), Patch(color='#1565C0', alpha=0.75, label='p<0.10'), Patch(color='#9E9E9E', alpha=0.75, label='非有意')] ax3.legend(handles=legend_els3, fontsize=9) plt.tight_layout() fig3.savefig(os.path.join(FIG_DIR, '2025_H5_4_fig3_coef.png'), bbox_inches='tight', dpi=150) plt.close(fig3) print("図3保存: 2025_H5_4_fig3_coef.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の定石です。245 246 247 248 249 250 251 252 253 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 281 282 283 284 285 286 287 | fig4, axes4 = plt.subplots(1, 2, figsize=(13, 5)) fig4.suptitle('生活環境変数と不登校率の関係(2017年度)', fontsize=13, fontweight='bold') for ax, (vname, xlabel) in zip(axes4, [('睡眠時間', '睡眠時間(分/日, SSDSE-D 2021年)'), ('日照時間', '年間日照時間(時間, SSDSE-F)')]): xv = df[vname].values r, p = stats.pearsonr(xv, y) sig = '***' if p < 0.001 else '**' if p < 0.01 else '*' if p < 0.05 else 'n.s.' # カラー: 外れ値(高不登校率県)を強調 is_out = (y > upper) | (y < lower) ax.scatter(xv[~is_out], y[~is_out], color='#1565C0', s=55, alpha=0.65, edgecolors='white', linewidth=0.5, label='通常', zorder=3) ax.scatter(xv[is_out], y[is_out], color='#C62828', s=80, alpha=0.85, edgecolors='white', linewidth=0.5, label='外れ値', zorder=4) for i, pref in enumerate(df['都道府県']): if is_out[i]: ax.annotate(pref, (xv[i], y[i]), fontsize=7, xytext=(3, 3), textcoords='offset points') z = np.polyfit(xv, y, 1) xs = np.linspace(xv.min() - 1, xv.max() + 1, 100) ax.plot(xs, np.poly1d(z)(xs), 'k--', linewidth=1.5, alpha=0.6) ax.set_xlabel(xlabel, fontsize=10) ax.set_ylabel('不登校率(千対)', fontsize=10) ax.set_title(f'{vname}と不登校率 r={r:.3f} {sig}', fontsize=11, fontweight='bold') ax.legend(fontsize=9) ax.grid(True, alpha=0.3) plt.tight_layout() fig4.savefig(os.path.join(FIG_DIR, '2025_H5_4_fig4_scatter.png'), bbox_inches='tight', dpi=150) plt.close(fig4) print("図4保存: 2025_H5_4_fig4_scatter.png") print("\n全図の生成完了(4枚)") print("\nデータ出典:") print(" 従属変数: 文部科学省 問題行動等調査 平成29年度(e-Stat: 00400304)") print(" 教育支援センター: 文部科学省 上記調査 表4-15") print(" SSDSE-B-2026(2017年度): 出生率, 教育費(統計センター)") print(" SSDSE-D-2023(2021年度): 睡眠時間, 通学時間, 仕事時間(統計センター)") print(" SSDSE-E-2026: 1人当たり県民所得(統計センター)") print(" SSDSE-F-2023v3: 年間日照時間(統計センター)") |
# 実行時エラーで途中まで
fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。stats.pearsonr(x, y) — Pearson相関係数 r と p値を同時に返します。{値:.2f}(小数2桁)、{値:,}(3桁区切り)、{値:>10}(右寄せ10桁)など、覚えると出力が一気に整います。| データ | 出典 |
|---|---|
| 都道府県別不登校率 | 文部科学省「問題行動・不登校調査」 |
| 生活時間(睡眠・通学・インターネット) | 総務省「社会生活基本調査」SSDSE-D |
| 教育費・県民所得 | SSDSE-B、SSDSE-E(統計数理研究所) |
| 教育支援センター数 | 文部科学省「不登校児童生徒の実態把握に関する調査」 |
本教育用コードは合成データを使用(np.random.seed(2027))。実際の分析はSSDSE-B/D/Eの実データによる。
統計分析の解釈で初心者がやりがちな勘違いをまとめます。特に「相関と因果の混同」「p値の過信」は研究現場でもよく起きる落とし穴です。本文を読む前にも、読んだ後にも、目を通してみてください。
統計の基本用語を初心者向けに解説します。本文中で見慣れない言葉が出てきたら、ここに戻って確認してください。
統計手法について「何のためか」「結果をどう読むか」を初心者向けに解説します。
この研究をさらに発展させるための3つの方向性を示します。「今回わかったこと(X)」から「次に検証すべき仮説(Y)」を立て、「具体的に何をするか(Z)」まで考えてみましょう。
学んだだけでは身につきません。実際に手を動かすのが最強の学習方法です。本論文のスクリプトをベースに、以下のチャレンジに挑戦してみてください。難易度別に5つ用意しました。
本論文で学んだ手法は、研究の世界だけでなく、行政・企業・NPO の現場でも様々に活用されています。具体的なシーンを紹介します。
この論文を読んで初心者が抱きやすい疑問に、教育的観点から答えます。