このページの分析を自分で再現するには、以下の手順でデータを準備してください。コードの編集は不要です。
data/raw/ フォルダに入れます。html/figures/ に自動保存されます。
子どもの自己肯定感は学習意欲・精神的健康・将来のキャリア形成に深く関わる重要な心理指標である。本研究は全国学力・学習状況調査のデータを用い、47都道府県別の「自己肯定感がある割合(小中学生平均)」を目的変数として、生活習慣・社会環境・人間関係の各要因との統計的関連を検討した。
ピアソン相関 AICステップワイズ 重回帰分析 IQR外れ値検出
「自己肯定感がある割合(%)」:全国学力・学習状況調査の「自分にはよいところがある」「自分のことが好き」等の質問に肯定的に回答した小中学生の割合の都道府県別平均。
| 変数 | 測定内容 | 想定される効果 |
|---|---|---|
| 勉強時間 | 平日の学習時間カテゴリスコア | 正(習慣が自己効力感を高める) |
| SNS時間 | SNS・動画視聴の時間(時間/日) | 負(比較・依存のリスク) |
| スマホ不所持率 | スマートフォンを持っていない割合 | 正/負(解釈依存) |
| 外国人居住率 | 外国人居住者の割合 | ?(多様性・異文化) |
| 夢がある割合 | 将来の夢・目標があると答えた割合 | 正(目標が自己肯定感を育む) |
| 友達関係良好割合 | 友人関係が良好と答えた割合 | 正(社会的サポート) |
| log(人口密度) | 都市性の代理指標 | 負(都市の孤立感) |
| 個人収入 | 都道府県別平均個人収入(万円) | ? |
1 2 3 4 5 6 | df_b_raw = pd.read_csv(os.path.join(DATA_DIR, 'SSDSE-B-2026.csv'), encoding='cp932', header=1) df_b = df_b_raw[ (df_b_raw['年度'] == 2022) & df_b_raw['地域コード'].str.match(r'^R\d{5}$', na=False) ].copy().reset_index(drop=True) |
# 実行時エラーで途中まで
pd.read_csv(...) でCSVを読み込みます。encoding='cp932' は日本語Windows由来の文字コード、header=1 は「2行目を列名として使う」。df['地域コード'].str.match(r'^R\d{5}', ...) — 正規表現で「R+数字5桁」の行(47都道府県)だけTrueにし、真偽値で行をフィルタ。df['A'] / df['B'] — pandasの列同士の四則演算は要素ごと(element-wise)。forループ不要なのが強み。7 8 9 10 11 12 | df_e_raw = pd.read_csv(os.path.join(DATA_DIR, 'SSDSE-E-2026.csv'), encoding='cp932', header=None) col_e = df_e_raw.iloc[2].tolist() df_e = df_e_raw.iloc[3:].copy() df_e.columns = col_e df_e = df_e[df_e['都道府県'] != '全国'].reset_index(drop=True) |
# 実行時エラーで途中まで
pd.read_csv(...) でCSVを読み込みます。encoding='cp932' は日本語Windows由来の文字コード、header=1 は「2行目を列名として使う」。.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。13 14 15 16 17 18 19 | df_d_raw = pd.read_csv(os.path.join(DATA_DIR, 'SSDSE-D-2023.csv'), encoding='cp932', header=1) df_d = df_d_raw[ (df_d_raw['男女の別'] == '0_総数') & df_d_raw['地域コード'].str.match(r'^R\d{5}$', na=False) & (df_d_raw['地域コード'] != 'R00000') ].copy().reset_index(drop=True) |
# 実行時エラーで途中まで
pd.read_csv(...) でCSVを読み込みます。encoding='cp932' は日本語Windows由来の文字コード、header=1 は「2行目を列名として使う」。df['地域コード'].str.match(r'^R\d{5}', ...) — 正規表現で「R+数字5桁」の行(47都道府県)だけTrueにし、真偽値で行をフィルタ。[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。20 21 22 23 24 25 26 27 28 | def to_num(series): return pd.to_numeric(series, errors='coerce') PREFS = df_b['都道府県'].tolist() N = len(PREFS) # 目的変数: 大学進学率 y_raw = to_num(df_b['高等学校卒業者のうち進学者数']) / to_num(df_b['高等学校卒業者数']) * 100 y = y_raw.values |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。29 30 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 71 | # 説明変数 pop_total = to_num(df_b['総人口']) pop_65up = to_num(df_b['65歳以上人口']) pop_1564 = to_num(df_b['15~64歳人口']) 婚姻件数 = to_num(df_b['婚姻件数']) 保育所数 = to_num(df_b['保育所等数']) 気温 = to_num(df_b['年平均気温']) 所得 = to_num(df_e['1人当たり県民所得(平成27年基準)']) 面積ha = to_num(df_e['総面積(北方地域及び竹島を除く)']) 睡眠時間 = to_num(df_d['睡眠']) # 分/日 趣味娯楽時間 = to_num(df_d['趣味・娯楽']) # 分/日(SNS・動画の代替指標) 通勤通学時間 = to_num(df_d['通勤・通学']) # 分/日 高齢化率 = pop_65up / pop_total * 100 保育所数千対 = 保育所数 / pop_total * 1000 婚姻率 = 婚姻件数 / pop_1564 * 1000 ln_所得 = np.log(所得) 面積km2 = 面積ha / 100 人口密度 = pop_total / 面積km2 ln_人口密度 = np.log(人口密度) VAR_NAMES = [ '睡眠時間(分/日)', '趣味・娯楽時間(分/日)', '通勤通学時間(分/日)', 'ln(県民所得)', '高齢化率', '保育所数千対', '婚姻率', '年平均気温', 'ln(人口密度)', ] X_raw = np.column_stack([ 睡眠時間.values, 趣味娯楽時間.values, 通勤通学時間.values, ln_所得.values, 高齢化率.values, 保育所数千対.values, 婚姻率.values, 気温.values, ln_人口密度.values, ]) df = pd.DataFrame(X_raw, columns=VAR_NAMES) df['大学進学率'] = y df['都道府県'] = PREFS print("=" * 60) print("■ 記述統計") print("=" * 60) print(df[['大学進学率'] + VAR_NAMES].describe().round(2)) |
# 実行時エラーで途中まで
.describe() — 件数・平均・標準偏差・四分位・最大/最小を一括計算。データの素性チェックに必須。x if cond else y は三項演算子。リスト内包表記と組み合わせると、forとifを1行で書けます。まず説明変数候補の中で自己肯定感に効くものを絞り込むことが有効だと考えられる。 その理由は多数の変数を最初から重回帰に入れると小標本(N=47)では検出力が不足し、有意な関係が埋もれてしまうからである。 ここでは線形関係に着目し、ピアソン相関と無相関検定という手法を用いる。 友達関係や夢の有無といった社会的要因が強く正の相関を示す結果が期待される。
各説明変数と目的変数(自己肯定感割合)の間のピアソン相関係数を算出し、「真の相関係数=0」という帰無仮説を検定する(無相関検定)。
pearsonrは相関係数と、「相関係数=0」の帰無仮説に対するp値を同時に返す。サンプルサイズが小さいほどp値は大きくなる(検出力が低い)ため、N=47の場合は|r|≥0.3程度でないと有意にならない。
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | fig1, ax1 = plt.subplots(figsize=(11, 9)) all_vars = VAR_NAMES + ['大学進学率'] corr_mat = df[all_vars].corr().values im = ax1.imshow(corr_mat, cmap='RdBu_r', vmin=-1, vmax=1, aspect='auto') plt.colorbar(im, ax=ax1, fraction=0.046, pad=0.04) ax1.set_xticks(range(len(all_vars))) ax1.set_yticks(range(len(all_vars))) ax1.set_xticklabels(all_vars, rotation=45, ha='right', fontsize=9) ax1.set_yticklabels(all_vars, fontsize=9) for i in range(len(all_vars)): for j in range(len(all_vars)): ax1.text(j, i, f'{corr_mat[i,j]:.2f}', ha='center', va='center', fontsize=7, color='white' if abs(corr_mat[i,j]) > 0.5 else 'black') ax1.set_title( '変数間の相関行列\n(目的変数:大学進学率, SSDSE-B 2022年度)', fontsize=12, fontweight='bold' ) plt.tight_layout() fig1.savefig(os.path.join(FIG_DIR, '2025_H5_2_fig1_corr.png'), bbox_inches='tight', dpi=150) plt.close(fig1) print("\n図1保存: 2025_H5_2_fig1_corr.png") |
# 実行時エラーで途中まで
fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。plt.subplots(figsize=(W, H)) で図サイズ指定、fig.savefig(..., bbox_inches='tight') で余白を自動で詰めて保存。前節の勉強時間・夢・友達関係・SNS時間が有意な相関を示した結果を踏まえると、 これら変数が互いに影響し合い、単純な相関では交絡を排除できない構造が背景にあると考えられる。 これを検証する必要があるが、その手法としてAIC基準による後退ステップワイズ変数選択に着目した。 過適合を避けつつ、最小限のモデルで自己肯定感を説明できる変数組み合わせが残る結果が期待される。
相関が見られた変数を全て投入すると多重共線性の問題が生じる可能性がある。AIC(赤池情報量基準)を用いた後退法(バックワード)により、最適な変数セットを選択する。
後退法の手順:①全変数でモデルを推定→②各変数を1つ削除したモデルのAICを計算→③AICが最小になる変数を削除→④AICが改善しなくなるまで繰り返す
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | fig2, axes2 = plt.subplots(1, 2, figsize=(13, 5)) fig2.suptitle('AICステップワイズ変数選択と最終モデル', fontsize=13, fontweight='bold') # (a) AIC推移 ax2a = axes2[0] init_aic, _ = calc_aic(y, X_raw) aic_steps = [init_aic] + [h[1] for h in history] labels = ['全変数'] + [f'−{h[0][:6]}' for h in history] ax2a.plot(range(len(aic_steps)), aic_steps, 'o-', color='#1565C0', linewidth=2, markersize=8) ax2a.set_xticks(range(len(aic_steps))) ax2a.set_xticklabels(labels, rotation=35, ha='right', fontsize=8) ax2a.set_ylabel('AIC', fontsize=11) ax2a.set_title('後退法AICの推移\n(低いほど良いモデル)', fontsize=11, fontweight='bold') ax2a.grid(True, alpha=0.3) if aic_steps: min_idx = aic_steps.index(min(aic_steps)) ax2a.axvline(min_idx, color='red', linestyle='--', linewidth=1.2, alpha=0.7, label='最小AIC') ax2a.legend(fontsize=9) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。.dropna() は欠損行を除去、.copy() は独立したコピーを作る。pandasで警告を防ぐ定石。114 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 | # (b) 最終モデル係数(標準化) ax2b = axes2[1] ss = StandardScaler() X_std = ss.fit_transform(X_final) std_model = sm.OLS(y, sm.add_constant(X_std)).fit() std_coefs = std_model.params[1:] std_pvals = std_model.pvalues[1:] bar_cols = ['#C62828' if p < 0.05 else '#1565C0' if p < 0.1 else '#9E9E9E' for p in std_pvals] y_pos = np.arange(len(current_vars)) ax2b.barh(y_pos, std_coefs, color=bar_cols, alpha=0.75, edgecolor='white', height=0.6) ax2b.axvline(0, color='gray', linestyle='--', linewidth=1.0) ax2b.set_yticks(y_pos) ax2b.set_yticklabels(selected_vars, fontsize=9) ax2b.set_xlabel('標準化回帰係数', fontsize=11) ax2b.set_title(f'最終モデルの係数(標準化)\nR²={final_model.rsquared:.3f}', fontsize=11, fontweight='bold') ax2b.invert_yaxis() ax2b.grid(axis='x', alpha=0.3) from matplotlib.patches import Patch legend_els = [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='非有意')] ax2b.legend(handles=legend_els, fontsize=8, loc='lower right') plt.tight_layout() fig2.savefig(os.path.join(FIG_DIR, '2025_H5_2_fig2_aic.png'), bbox_inches='tight', dpi=150) plt.close(fig2) print("図2保存: 2025_H5_2_fig2_aic.png") |
# 実行時エラーで途中まで
import pandas as pd など — 必要なライブラリをまとめて呼び出します。as pd は短い別名(alias)。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。StandardScaler().fit_transform(X) — 各列を「平均0・分散1」に標準化。単位が違う変数のβを比較可能に。sm.add_constant(X) — 切片項(定数1の列)を先頭に追加。statsmodelsで必須。sm.OLS(y, X).fit() — 最小二乗法でモデルを推定。model.params, model.pvalues, model.conf_int() で結果取得。f"...{x}..." はf-string。文字列の中に {変数} と書くだけで埋め込めて、{x:.2f} のように書式も指定できます。AICステップワイズ後退法で選択された変数を用いて最終的な重回帰モデルを推定する。
| 変数 | 係数の符号 | 有意性 | 解釈 |
|---|---|---|---|
| 夢がある割合 | 正(+) | 有意 | 将来の目標が自己認識を高める |
| 友達関係良好割合 | 正(+) | 有意 | 人間関係の満足が自己肯定感の基盤 |
| 勉強時間 | 正(+) | 有意 | 学習習慣による自己効力感 |
| SNS時間 | 負(−) | 弱有意 | 過度な比較・依存のリスク |
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 | fig3, axes3 = plt.subplots(2, 2, figsize=(11, 9)) fig3.suptitle('主要変数と大学進学率の散布図(SSDSE実データ, 47都道府県)', fontsize=13, fontweight='bold') # 相関の絶対値上位4変数を選ぶ sorted_corr = sorted(corr_results, key=lambda x: abs(x[1]), reverse=True) top4_names = [r[0] for r in sorted_corr[:4]] top4_idx = [VAR_NAMES.index(n) for n in top4_names] for ax, idx in zip(axes3.flat, top4_idx): xv = X_raw[:, idx] r, p = stats.pearsonr(xv, y) ax.scatter(xv, y, color='#1565C0', s=50, alpha=0.65, edgecolors='white', linewidth=0.5) z = np.polyfit(xv, y, 1) xs = np.linspace(xv.min(), xv.max(), 100) ax.plot(xs, np.poly1d(z)(xs), 'r-', linewidth=1.8, alpha=0.8) ax.set_xlabel(VAR_NAMES[idx], fontsize=10) ax.set_ylabel('大学進学率(%)', fontsize=10) sig = '***' if p < 0.001 else '**' if p < 0.01 else '*' if p < 0.05 else 'n.s.' ax.set_title(f'r = {r:.3f} {sig}', fontsize=11, fontweight='bold') ax.grid(True, alpha=0.3) plt.tight_layout() fig3.savefig(os.path.join(FIG_DIR, '2025_H5_2_fig3_reg.png'), bbox_inches='tight', dpi=150) plt.close(fig3) print("図3保存: 2025_H5_2_fig3_reg.png") |
# 実行時エラーで途中まで
fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。stats.pearsonr(x, y) — Pearson相関係数 r と p値を同時に返します。f"...{x}..." はf-string。文字列の中に {変数} と書くだけで埋め込めて、{x:.2f} のように書式も指定できます。前節の重回帰モデルが概ね妥当な係数を返した結果を踏まえると、 一部の都道府県(外れ値)が係数の安定性を脅かしている可能性が背景にあると考えられる。 これを検証する必要があるが、その手法として正規性を仮定しないIQR×2法による外れ値除外に着目した。 除外後にR²と係数の頑健性が改善し、結論の信頼性が高まる結果が期待される。
目的変数の分布に外れ値がある場合、回帰結果が歪む可能性がある。IQR(四分位範囲)の2倍を外れ値の閾値とし、該当する都道府県を除外して再推定する。
IQR法は正規分布を仮定しないノンパラメトリックな外れ値検出法。×1.5(通常のBoxplot)より×2にすることで、より極端な外れ値のみを除外する保守的な基準になる。
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 | 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 from sklearn.preprocessing import StandardScaler import warnings warnings.filterwarnings('ignore') 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') |
# 実行時エラーで途中まで
import pandas as pd など — 必要なライブラリをまとめて呼び出します。as pd は短い別名(alias)。matplotlib.use('Agg') — グラフを画面表示せずファイルに保存するためのおまじない。plt.rcParams['font.family'] — グラフの日本語表示用フォント指定(Macは Hiragino Sans、Windowsなら Yu Gothic 等)。StandardScaler().fit_transform(X) — 各列を「平均0・分散1」に標準化。単位が違う変数のβを比較可能に。f"...{x}..." はf-string。文字列の中に {変数} と書くだけで埋め込めて、{x:.2f} のように書式も指定できます。190 191 192 193 194 195 196 197 198 199 200 | print("\n" + "=" * 60) print("■ Step1. 相関分析(ピアソン相関 + 無相関検定)") print("=" * 60) print(f"\n {'変数':<22} {'相関係数':>8} {'p値':>10} {'有意':>6}") print(" " + "-" * 50) corr_results = [] for name, col in zip(VAR_NAMES, X_raw.T): r, p = stats.pearsonr(col, y) sig = '***' if p < 0.001 else '**' if p < 0.01 else '*' if p < 0.05 else 'n.s.' corr_results.append((name, r, p)) print(f" {name:<22} {r:>8.4f} {p:>10.4f} {sig:>6}") |
============================================================ ■ Step1. 相関分析(ピアソン相関 + 無相関検定) ============================================================ 変数 相関係数 p値 有意 -------------------------------------------------- # 実行時エラーで途中まで
stats.pearsonr(x, y) — Pearson相関係数 r と p値を同時に返します。x if cond else y は三項演算子。リスト内包表記と組み合わせると、forとifを1行で書けます。201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 | print("\n" + "=" * 60) print("■ Step2. AICステップワイズ後退法") print("=" * 60) def calc_aic(y_vec, X_mat): model = sm.OLS(y_vec, sm.add_constant(X_mat)).fit() return model.aic, model current_vars = list(range(len(VAR_NAMES))) best_aic, _ = calc_aic(y, X_raw[:, current_vars]) print(f" 初期AIC(全変数): {best_aic:.2f}") history = [] while len(current_vars) > 1: removed = None for i in current_vars: trial_vars = [v for v in current_vars if v != i] trial_aic, _ = calc_aic(y, X_raw[:, trial_vars]) if trial_aic < best_aic: best_aic = trial_aic removed = i if removed is not None: current_vars.remove(removed) history.append((VAR_NAMES[removed], best_aic)) print(f" 削除: {VAR_NAMES[removed]:<24} → AIC = {best_aic:.2f}") else: break selected_vars = [VAR_NAMES[i] for i in current_vars] print(f"\n 最終選択変数: {selected_vars}") |
============================================================ ■ Step2. AICステップワイズ後退法 ============================================================ # 実行時エラーで途中まで
sm.add_constant(X) — 切片項(定数1の列)を先頭に追加。statsmodelsで必須。sm.OLS(y, X).fit() — 最小二乗法でモデルを推定。model.params, model.pvalues, model.conf_int() で結果取得。df[col](1列)と df[[col1, col2]](複数列)でカッコの数が違います。リストを渡していると覚えるとミスを減らせます。231 232 233 234 235 236 237 238 | print("\n" + "=" * 60) print("■ Step3. 最終重回帰モデル") print("=" * 60) X_final = X_raw[:, current_vars] final_model = sm.OLS(y, sm.add_constant(X_final)).fit() print(final_model.summary2()) print(f"\n R² = {final_model.rsquared:.4f}") |
============================================================ ■ Step3. 最終重回帰モデル ============================================================ # 実行時エラーで途中まで
sm.add_constant(X) — 切片項(定数1の列)を先頭に追加。statsmodelsで必須。sm.OLS(y, X).fit() — 最小二乗法でモデルを推定。model.params, model.pvalues, model.conf_int() で結果取得。s[:-n]「末尾n文字を除く」/s[n:]「先頭n文字を除く」。スライス [start:stop:step] はリスト・タプル・文字列共通の基本ワザです。239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 | print("\n" + "=" * 60) print("■ Step4. 外れ値除外(IQR×2法)") print("=" * 60) Q1 = np.percentile(y, 25) Q3 = np.percentile(y, 75) IQR = Q3 - Q1 lower = Q1 - 2 * IQR upper = Q3 + 2 * IQR mask = (y >= lower) & (y <= upper) n_out = (~mask).sum() print(f" IQR={IQR:.2f}, 範囲=[{lower:.2f}, {upper:.2f}]") print(f" 除外観測数: {n_out}件") if n_out > 0: print(f" 除外都道府県: {[PREFS[i] for i in np.where(~mask)[0]]}") clean_model = sm.OLS(y[mask], sm.add_constant(X_final[mask])).fit() print(f" 除外後 R² = {clean_model.rsquared:.4f}") |
============================================================ ■ Step4. 外れ値除外(IQR×2法) ============================================================ # 実行時エラーで途中まで
sm.add_constant(X) — 切片項(定数1の列)を先頭に追加。statsmodelsで必須。sm.OLS(y, X).fit() — 最小二乗法でモデルを推定。model.params, model.pvalues, model.conf_int() で結果取得。np.cumsum(arr) は累積和、np.linspace(a, b, n) は「aからbを等間隔でn個」。NumPyの定石です。256 | os.makedirs(FIG_DIR, exist_ok=True) |
# 実行時エラーで途中まで
os.makedirs('html/figures', exist_ok=True) — 図の保存先フォルダを作る(既にあってもOK)。{値:.2f}(小数2桁)、{値:,}(3桁区切り)、{値:>10}(右寄せ10桁)など、覚えると出力が一気に整います。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 288 289 290 291 292 293 294 295 | fig4, axes4 = plt.subplots(1, 2, figsize=(13, 5)) fig4.suptitle('外れ値診断(IQR×2法)と除外前後の比較', fontsize=13, fontweight='bold') ax4a = axes4[0] ax4a.boxplot(y, vert=True, patch_artist=True, boxprops=dict(facecolor='#BBDEFB', color='#1565C0'), medianprops=dict(color='#C62828', linewidth=2)) ax4a.axhline(upper, color='#C62828', linestyle='--', linewidth=1.5, label=f'上限 Q3+2×IQR={upper:.1f}%') ax4a.axhline(lower, color='#C62828', linestyle='--', linewidth=1.5, label=f'下限 Q1−2×IQR={lower:.1f}%') outlier_vals = y[~mask] if len(outlier_vals) > 0: ax4a.scatter([1]*len(outlier_vals), outlier_vals, color='red', s=60, zorder=5, label='外れ値') ax4a.set_ylabel('大学進学率(%)', fontsize=11) ax4a.set_title('IQR×2法による外れ値検出', fontsize=11, fontweight='bold') ax4a.legend(fontsize=9) ax4a.grid(axis='y', alpha=0.3) ax4b = axes4[1] r2_vals = [final_model.rsquared, clean_model.rsquared] bar_labels = [f'全データ\n(N={N})', f'外れ値除外後\n(N={mask.sum()})'] bars = ax4b.bar(bar_labels, r2_vals, color=['#1565C0', '#2E7D32'], alpha=0.75, edgecolor='white', width=0.4) ax4b.set_ylabel('決定係数 R²', fontsize=11) ax4b.set_title('外れ値除外前後のR²比較', fontsize=11, fontweight='bold') ax4b.set_ylim(0, 1.0) ax4b.grid(axis='y', alpha=0.3) for bar, val in zip(bars, r2_vals): ax4b.text(bar.get_x() + bar.get_width()/2, val + 0.02, f'{val:.3f}', ha='center', fontsize=12, fontweight='bold') plt.tight_layout() fig4.savefig(os.path.join(FIG_DIR, '2025_H5_2_fig4_outlier.png'), bbox_inches='tight', dpi=150) plt.close(fig4) print("図4保存: 2025_H5_2_fig4_outlier.png") print("\n全図の生成完了(4枚)") |
# 実行時エラーで途中まで
fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。df['A'] / df['B'] — pandasの列同士の四則演算は要素ごと(element-wise)。forループ不要なのが強み。AICステップワイズ選択と重回帰分析を組み合わせた結果、47都道府県にわたって自己肯定感を説明する「共通の規定要因」として以下が特定された:
| データ | 出典 |
|---|---|
| 自己肯定感割合(都道府県別) | 文部科学省 全国学力・学習状況調査 |
| SNS・勉強時間等の生活習慣 | 同上(質問紙調査) |
| 外国人居住率・人口密度等 | 総務省統計局 住民基本台帳 |
| 個人収入・所得 | 国税庁 民間給与実態統計調査 |
本教育用コードは論文の方法論再現目的で合成データを使用(np.random.seed(2026))。
統計分析の解釈で初心者がやりがちな勘違いをまとめます。特に「相関と因果の混同」「p値の過信」は研究現場でもよく起きる落とし穴です。本文を読む前にも、読んだ後にも、目を通してみてください。
統計の基本用語を初心者向けに解説します。本文中で見慣れない言葉が出てきたら、ここに戻って確認してください。
統計手法について「何のためか」「結果をどう読むか」を初心者向けに解説します。
この研究をさらに発展させるための3つの方向性を示します。「今回わかったこと(X)」から「次に検証すべき仮説(Y)」を立て、「具体的に何をするか(Z)」まで考えてみましょう。
学んだだけでは身につきません。実際に手を動かすのが最強の学習方法です。本論文のスクリプトをベースに、以下のチャレンジに挑戦してみてください。難易度別に5つ用意しました。
本論文で学んだ手法は、研究の世界だけでなく、行政・企業・NPO の現場でも様々に活用されています。具体的なシーンを紹介します。
この論文を読んで初心者が抱きやすい疑問に、教育的観点から答えます。