このページの分析を自分で再現するには、以下の手順でデータを準備してください。コードの編集は不要です。
data/raw/ フォルダに入れます。html/figures/ に自動保存されます。
日本では年間約4,000万トンものごみが排出されており、廃棄物の削減は環境政策上の重要課題となっている。生活系ごみと事業系ごみの排出量は地域によって大きく異なるが、その要因は必ずしも明らかではない。
まず「生活系ごみ排出量と事業系ごみ排出量による回帰分析」を統計的にとらえることが有効だと考えられる。 その理由は感覚や経験則だけでは、複雑な社会要因の中で「何が本当に効いているか」を見極めにくいからである。 本研究では公開データと統計手法を組み合わせ、この問いに定量的な答えを出すことを目指す。
重回帰分析 VIF 標準化係数β ごみ排出量 SSDSE-B
| 変数名 | 単位 | 説明 |
|---|---|---|
| 1人1日当たりの排出量 | g/人/日 | 生活系ごみ+事業系ごみの合計排出量を人口・日数で除した値 |
| 変数 | 作成方法 | 単位 | 仮説(ごみとの関係) |
|---|---|---|---|
| 食料費割合 | 食料費(二人以上世帯)÷ 消費支出 × 100 | % | エンゲル係数が高い→食品廃棄ごみが増加?(正) |
| 教養娯楽費割合 | 教養娯楽費(二人以上世帯)÷ 消費支出 × 100 | % | 生活水準・消費行動との関連(正 or 負) |
| 65歳以上割合 | 65歳以上人口 ÷ 総人口 × 100 | % | 高齢化は在宅時間増→ごみ増?(正) |
| 年平均気温 | SSDSE-B 収録値 | ℃ | 温暖地域の食品ロスとの関係(正?) |
| リサイクル率 | ごみのリサイクル率(SSDSE-B 収録値) | % | リサイクル推進→ごみ削減(負) |
目的変数「1人1日ごみ排出量」と各説明変数、および説明変数間の相関係数を可視化する。説明変数間の高い相関は多重共線性の危険信号となるため、VIF 分析の前段階として重要な確認ステップである。
VIF(分散拡大因子)は、ある説明変数 xⱼ を他の全説明変数で回帰したときの決定係数 Rⱼ² を用いて計算される。多重共線性の度合いを定量化する指標。
1 2 3 4 5 6 7 8 9 10 11 12 13 | import os import warnings import numpy as np import pandas as pd import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt import matplotlib.patches as mpatches import statsmodels.api as sm from statsmodels.stats.outliers_influence import variance_inflation_factor from scipy import stats warnings.filterwarnings('ignore') |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。import pandas as pd など — 必要なライブラリをまとめて呼び出します。as pd は短い別名(alias)。matplotlib.use('Agg') — グラフを画面表示せずファイルに保存するためのおまじない。f"...{x}..." はf-string。文字列の中に {変数} と書くだけで埋め込めて、{x:.2f} のように書式も指定できます。14 15 16 17 18 19 20 21 22 23 24 25 | # ── パス設定 ────────────────────────────────────────────────────────── DATA_B = 'data/raw/SSDSE-B-2026.csv' FIG_DIR = 'html/figures' os.makedirs(FIG_DIR, exist_ok=True) plt.rcParams.update({ 'font.family': 'Hiragino Sans', 'axes.unicode_minus': False, 'figure.dpi': 150, 'axes.spines.top': False, 'axes.spines.right': False, }) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。os.makedirs('html/figures', exist_ok=True) — 図の保存先フォルダを作る(既にあってもOK)。df['A'] / df['B'] — pandasの列同士の四則演算は要素ごと(element-wise)。forループ不要なのが強み。26 27 28 29 30 31 32 | # ── データ読み込み ───────────────────────────────────────────────────── 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_2022 = df_b[df_b['年度'] == 2022].copy() df_2022 = df_2022.reset_index(drop=True) print(f"データ読み込み完了: {len(df_2022)}都道府県(2022年度)") |
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()。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 | # ── 変数作成 ────────────────────────────────────────────────────────── # 消費支出に占める構成比(%) df_2022['食料費割合'] = (df_2022['食料費(二人以上の世帯)'] / df_2022['消費支出(二人以上の世帯)']) * 100 df_2022['教養娯楽費割合'] = (df_2022['教養娯楽費(二人以上の世帯)'] / df_2022['消費支出(二人以上の世帯)']) * 100 df_2022['65歳以上割合'] = (df_2022['65歳以上人口'] / df_2022['総人口']) * 100 # 変数名リスト TARGET = '1人1日当たりの排出量' # g/人/日 PRED_NAMES = { 'food_ratio': '食料費割合(%)', 'culture_ratio': '教養娯楽費割合(%)', 'elder_ratio': '65歳以上割合(%)', 'temp': '年平均気温(℃)', 'recycle': 'リサイクル率(%)', } df_2022['food_ratio'] = df_2022['食料費割合'] df_2022['culture_ratio'] = df_2022['教養娯楽費割合'] df_2022['elder_ratio'] = df_2022['65歳以上割合'] df_2022['temp'] = df_2022['年平均気温'] df_2022['recycle'] = df_2022['ごみのリサイクル率'] df_2022['y'] = df_2022[TARGET] PREDS = list(PRED_NAMES.keys()) analysis_cols = ['都道府県', 'y'] + PREDS df_ana = df_2022[analysis_cols].dropna().copy() print(f"分析対象: {len(df_ana)}都道府県(欠損除外後)") print(f"\n目的変数 ({TARGET}) 記述統計:") print(df_ana['y'].describe().round(1)) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。.describe() — 件数・平均・標準偏差・四分位・最大/最小を一括計算。データの素性チェックに必須。[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。63 64 65 66 67 68 | # ── 記述統計 ────────────────────────────────────────────────────────── print(f"\n説明変数 記述統計:") for p, name in PRED_NAMES.items(): s = df_ana[p] print(f" {name}: mean={s.mean():.2f}, std={s.std():.2f}, " f"min={s.min():.2f}, max={s.max():.2f}") |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。69 70 71 72 73 74 75 76 77 78 79 | # ── OLS 重回帰分析 ────────────────────────────────────────────────── y = df_ana['y'].values X_raw = df_ana[PREDS].values X_sm = sm.add_constant(X_raw) model = sm.OLS(y, X_sm).fit() print("\n" + "=" * 60) print("■ OLS 重回帰分析結果") print("=" * 60) print(model.summary()) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。sm.add_constant(X) — 切片項(定数1の列)を先頭に追加。statsmodelsで必須。sm.OLS(y, X).fit() — 最小二乗法でモデルを推定。model.params, model.pvalues, model.conf_int() で結果取得。x if cond else y は三項演算子。リスト内包表記と組み合わせると、forとifを1行で書けます。80 81 82 83 84 85 86 87 88 89 90 91 | # ── VIF 計算(定数項を含めた行列で各説明変数のVIFを計算)────────── # statsmodels の variance_inflation_factor は X の i番目の列を他の列で回帰する # 正しい使い方: sm.add_constant した行列を渡し、定数列(index=0)を除く各列を計算 X_vif = sm.add_constant(X_raw) vif_vals = [] for i in range(1, X_vif.shape[1]): # index 0 は定数列 vif_vals.append(variance_inflation_factor(X_vif, i)) print("\n■ VIF(分散拡大因子)") for name, vif in zip(PRED_NAMES.values(), vif_vals): flag = " ← 問題なし" if vif < 5 else (" ← 要注意" if vif < 10 else " ← 問題あり") print(f" {name}: VIF = {vif:.2f}{flag}") |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。sm.add_constant(X) — 切片項(定数1の列)を先頭に追加。statsmodelsで必須。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 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 | # ── 標準化回帰係数 ───────────────────────────────────────────────── y_std = (y - y.mean()) / y.std() X_std = (X_raw - X_raw.mean(axis=0)) / X_raw.std(axis=0) X_std_sm = sm.add_constant(X_std) model_std = sm.OLS(y_std, X_std_sm).fit() beta_coef = model_std.params[1:] # 定数項を除く _ci_all = model_std.conf_int(alpha=0.05) # ndarray shape (k+1, 2) beta_ci = _ci_all[1:] # 定数項を除く 95%CI beta_pvals = model_std.pvalues[1:] print("\n■ 標準化回帰係数(β係数)") for name, beta, pval in zip(PRED_NAMES.values(), beta_coef, beta_pvals): sig = "**" if pval < 0.01 else ("*" if pval < 0.05 else "") print(f" {name}: β = {beta:.4f} (p={pval:.4f}) {sig}") print(f"\n R² = {model.rsquared:.4f}, Adj. R² = {model.rsquared_adj:.4f}") print(f" F統計量 = {model.fvalue:.2f}, p(F) = {model.f_pvalue:.4f}") label_map = {'y': '1人1日\nごみ排出量'} label_map.update({p: n.replace('(%)','').replace('(℃)','') for p, n in PRED_NAMES.items()}) corr_cols = ['y'] + PREDS corr_labels = [label_map[c] for c in corr_cols] corr_mat = df_ana[corr_cols].corr() fig, ax = plt.subplots(figsize=(8, 6.5)) im = ax.imshow(corr_mat.values, cmap='RdBu_r', vmin=-1, vmax=1, aspect='auto') n = len(corr_cols) ax.set_xticks(range(n)) ax.set_yticks(range(n)) ax.set_xticklabels(corr_labels, fontsize=10) ax.set_yticklabels(corr_labels, fontsize=10) plt.colorbar(im, ax=ax, shrink=0.85, label='相関係数 r') for i in range(n): for j in range(n): v = corr_mat.values[i, j] c = 'white' if abs(v) > 0.6 else 'black' ax.text(j, i, f'{v:.2f}', ha='center', va='center', fontsize=10, color=c, fontweight='bold') ax.set_title('図1:相関ヒートマップ\n(目的変数+説明変数、2022年度 N=47都道府県)', fontsize=12, fontweight='bold', pad=12) fig.tight_layout() out1 = os.path.join(FIG_DIR, '2022_U4_fig1_corr.png') fig.savefig(out1, bbox_inches='tight', dpi=150) plt.close(fig) print(f"\n[保存] {out1}") |
データ読み込み完了: 47都道府県(2022年度)
分析対象: 47都道府県(欠損除外後)
目的変数 (1人1日当たりの排出量) 記述統計:
count 47.0
mean 906.1
std 61.5
min 770.0
25% 863.5
50% 911.0
75% 951.5
max 1021.0
Name: y, dtype: float64
説明変数 記述統計:
食料費割合(%): mean=26.47, std=1.39, min=23.30, max=30.51
教養娯楽費割合(%): mean=8.94, std=0.92, min=6.49, max=10.91
65歳以上割合(%): mean=31.35, std=3.27, min=22.81, max=38.60
年平均気温(℃): mean=16.07, std=2.29, min=10.20, max=23.70
リサイクル率(%): mean=18.21, std=3.83, min=12.40, max=28.30
============================================================
■ OLS 重回帰分析結果
============================================================
OLS Regression Results
==============================================================================
Dep. Variable: y R-squared: 0.343
Model: OLS Adj. R-squared: 0.263
Method: Least Squares F-statistic: 4.288
Date: Mon, 18 May 2026 Prob (F-statistic): 0.00314
Time: 11:24:16 Log-Likelihood: -249.90
No. Observations: 47 AIC: 511.8
Df Residuals: 41 BIC: 522.9
Df Model: 5
Covariance Type: nonrobust
==============================================================================
coef std err t P>|t| [0.025 0.975]
------------------------------------------------------------------------------
const 816.5862 314.857 2.594 0.013 180.720 1452.453
x1 -0.3464 6.910 -0.050 0.960 -14.301 13.608
x2 -11.0201 9.955 -1.107 0.275 -31.125 9.084
x3 8.2989 3.211 2.585 0.013 1.814 14.783
x4 -3.4280 4.061 -0.844 0.403 -11.629 4.773
x5 -0.4346 2.419 -0.180 0.858 -5.320 4.450
==============================================================================
Omnibus: 0.835 Durbin-Watson: 1.939
Prob(Omnibus): 0.659 Jarque-Bera (JB): 0.510
Skew: -0.255 Prob(JB): 0.775
Kurtosis: 3.015 Cond. No. 1.99e+03
==============================================================================
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large, 1.99e+03. This might indicate that there are
strong multicollinearity or other numerical problems.
■ VIF(分散拡大因子)
食料費割合(%): VIF = 1.53 ← 問題なし
教養娯楽費割合(%): VIF = 1.40 ← 問題なし
65歳以上割合(%): VIF = 1.82 ← 問題なし
年平均気温(℃): VIF = 1.43 ← 問題なし
リサイクル率(%): VIF = 1.41 ← 問題なし
■ 標準化回帰係数(β係数)
食料費割合(%): β = -0.0078 (p=0.9603)
教養娯楽費割合(%): β = -0.1656 (p=0.2747)
65歳以上割合(%): β = 0.4411 (p=0.0134) *
年平均気温(℃): β = -0.1276 (p=0.4035)
リサイクル率(%): β = -0.0270 (p=0.8583)
R² = 0.3434, Adj. R² = 0.2633
F統計量 = 4.29, p(F) = 0.0031
[保存] html/figures/2022_U4_fig1_corr.pngfig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。sm.add_constant(X) — 切片項(定数1の列)を先頭に追加。statsmodelsで必須。sm.OLS(y, X).fit() — 最小二乗法でモデルを推定。model.params, model.pvalues, model.conf_int() で結果取得。fig.savefig(..., bbox_inches='tight') — 余白を自動で詰めて保存。plt.close() でメモリ解放。s[:-n]「末尾n文字を除く」/s[n:]「先頭n文字を除く」。スライス [start:stop:step] はリスト・タプル・文字列共通の基本ワザです。重回帰分析では、説明変数間に強い相関(多重共線性)があると回帰係数の推定が不安定になる。VIF(Variance Inflation Factor:分散拡大因子)はこの問題を定量的に診断するための指標である。
| VIF 値 | 判定 | 対応 |
|---|---|---|
| 1.0〜5.0 | 問題なし | そのまま回帰分析に使用可 |
| 5.0〜10.0 | 要注意 | 係数の符号・解釈を慎重に確認 |
| 10.0 以上 | 問題あり | 変数の除外、主成分回帰、リッジ回帰を検討 |
通常の回帰係数は変数の単位に依存するため、単位が異なる変数間の「影響力の大きさ」を直接比較できない。標準化回帰係数 β は、すべての変数を平均0・標準偏差1に標準化してから回帰することで、変数間の相対的な重要度を比較可能にする。
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 | short_names = [n.replace('(%)','').replace('(℃)','') for n in PRED_NAMES.values()] colors_vif = ['#E53935' if v >= 10 else ('#FFA726' if v >= 5 else '#42A5F5') for v in vif_vals] fig, ax = plt.subplots(figsize=(8, 4.5)) bars = ax.barh(short_names, vif_vals, color=colors_vif, edgecolor='white', linewidth=0.8, alpha=0.9) ax.axvline(5, color='#FFA726', linewidth=1.5, linestyle='--', label='VIF=5(注意水準)') ax.axvline(10, color='#E53935', linewidth=1.5, linestyle='--', label='VIF=10(問題水準)') for bar, v in zip(bars, vif_vals): ax.text(v + 0.05, bar.get_y() + bar.get_height()/2, f'{v:.2f}', va='center', fontsize=11, fontweight='bold') ax.set_xlabel('VIF(分散拡大因子)', fontsize=11) ax.set_title('図2:VIF による多重共線性チェック\n(VIF < 5: 問題なし、5–10: 要注意、≥ 10: 問題あり)', fontsize=12, fontweight='bold', pad=10) ax.legend(fontsize=10, loc='lower right') ax.set_xlim(0, max(vif_vals) * 1.2 + 0.5) fig.tight_layout() out2 = os.path.join(FIG_DIR, '2022_U4_fig2_vif.png') fig.savefig(out2, bbox_inches='tight', dpi=150) plt.close(fig) print(f"[保存] {out2}") |
[保存] html/figures/2022_U4_fig2_vif.png
fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。fig.savefig(..., bbox_inches='tight') — 余白を自動で詰めて保存。plt.close() でメモリ解放。df['A'] / df['B'] — pandasの列同士の四則演算は要素ごと(element-wise)。forループ不要なのが強み。重回帰分析の結果として、5つの説明変数の標準化回帰係数(β係数)を比較する。β係数は変数間の単位の違いを取り除いた相対的影響力の指標であり、どの要因がごみ排出量に最も強く関係しているかを直接比較できる。
| 指標 | 説明 | 解釈の目安 |
|---|---|---|
| R²(決定係数) | ごみ排出量の変動のうちモデルが説明できる割合 | 0〜1、高いほど良好 |
| Adj. R²(自由度調整済み) | 説明変数の数による過剰適合を補正した R² | R² より保守的な評価 |
| F 統計量(p 値) | モデル全体の有意性検定 | p < 0.05 でモデルが有意 |
単純相関では「食料費割合が高い都道府県ほどごみが多い」という関係が見えても、それは高齢化率や気温という第三の変数(交絡変数)を介した見せかけの相関かもしれない。重回帰分析では他の変数を「統制(コントロール)」した上で各変数の純粋な効果を推定できる。
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 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 | beta_ci_arr = np.array(beta_ci) ci_lo = beta_ci_arr[:, 0] ci_hi = beta_ci_arr[:, 1] err_lo = beta_coef - ci_lo err_hi = ci_hi - beta_coef # 係数の大きい順にソート order = np.argsort(np.abs(beta_coef))[::-1] b_sorted = beta_coef[order] lo_sorted = err_lo[order] hi_sorted = err_hi[order] names_sorted = [short_names[i] for i in order] sig_sorted = np.array(beta_pvals)[order] colors_coef = ['#C62828' if b > 0 else '#1565C0' for b in b_sorted] alpha_vals = [1.0 if p < 0.05 else 0.45 for p in sig_sorted] fig, ax = plt.subplots(figsize=(8, 5)) for i, (name, b, lo, hi, col, al, pv) in enumerate( zip(names_sorted, b_sorted, lo_sorted, hi_sorted, colors_coef, alpha_vals, sig_sorted)): ax.barh(i, b, xerr=[[lo], [hi]], color=col, alpha=al, edgecolor='white', linewidth=0.8, capsize=5, error_kw={'elinewidth': 1.5, 'ecolor': '#555'}) sig_mark = '**' if pv < 0.01 else ('*' if pv < 0.05 else 'n.s.') x_pos = b + (hi if b >= 0 else -lo) * 1.05 ax.text(x_pos, i, f' {sig_mark} β={b:.3f}', va='center', fontsize=9, color='#333') ax.axvline(0, color='black', linewidth=0.8, linestyle='-') ax.set_yticks(range(len(names_sorted))) ax.set_yticklabels(names_sorted, fontsize=11) ax.set_xlabel('標準化回帰係数(β)', fontsize=11) ax.set_title('図3:標準化回帰係数プロット(95%信頼区間付き)\n' '赤: 正の効果, 青: 負の効果, 透明: 非有意(p≥0.05)', fontsize=12, fontweight='bold', pad=10) red_patch = mpatches.Patch(color='#C62828', label='正の効果(β > 0)') blue_patch = mpatches.Patch(color='#1565C0', label='負の効果(β < 0)') ax.legend(handles=[red_patch, blue_patch], fontsize=10, loc='lower right') x_vals = np.concatenate([b_sorted - lo_sorted, b_sorted + hi_sorted]) x_margin = max(abs(x_vals)) * 0.25 ax.set_xlim(min(x_vals) - x_margin, max(x_vals) + x_margin) fig.tight_layout() out3 = os.path.join(FIG_DIR, '2022_U4_fig3_coef.png') fig.savefig(out3, bbox_inches='tight', dpi=150) plt.close(fig) print(f"[保存] {out3}") |
[保存] html/figures/2022_U4_fig3_coef.png
fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。fig.savefig(..., bbox_inches='tight') — 余白を自動で詰めて保存。plt.close() でメモリ解放。.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。重回帰分析の結果をより直感的に理解するため、食料費割合・教養娯楽費割合とごみ排出量の散布図を都道府県ラベル付きで可視化する。排出量上位・下位5府県をラベル表示し、地域差のパターンを探る。
重回帰分析の結果は、「どの要因に働きかければごみ排出量を削減できるか」という政策立案の手がかりを提供する。ただし観察データからは因果関係は確定できず、政策効果の推定には介入研究や差の差分析(DiD)などが必要になる。
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 | pref_labels = df_ana['都道府県'].values y_vals = df_ana['y'].values food_vals = df_ana['food_ratio'].values culture_vals = df_ana['culture_ratio'].values fig, axes = plt.subplots(1, 2, figsize=(14, 6)) for ax, x_vals, xlabel, x_col, label_idx in [ (axes[0], food_vals, '食料費割合(%)', 'food', True), (axes[1], culture_vals, '教養娯楽費割合(%)', 'culture', True), ]: sc = ax.scatter(x_vals, y_vals, s=55, c=y_vals, cmap='RdYlGn_r', vmin=y_vals.min() - 20, vmax=y_vals.max() + 20, edgecolors='#555', linewidths=0.5, zorder=3, alpha=0.9) # 都道府県ラベル(主要都道府県のみ) # 上位5位・下位5位 + 外れ値候補 y_rank = pd.Series(y_vals) top_idx = set(y_rank.nlargest(5).index) | set(y_rank.nsmallest(5).index) for i, (px, py, lbl) in enumerate(zip(x_vals, y_vals, pref_labels)): if i in top_idx: ax.annotate(lbl, (px, py), fontsize=7.5, alpha=0.9, xytext=(4, 2), textcoords='offset points', color='#222') # 回帰直線 slope, intercept, r_val, p_val, se = stats.linregress(x_vals, y_vals) x_line = np.linspace(x_vals.min(), x_vals.max(), 100) ax.plot(x_line, slope * x_line + intercept, color='#E53935', linewidth=1.8, linestyle='--', label=f'回帰直線 (r={r_val:.3f}, p={p_val:.3f})') ax.set_xlabel(xlabel, fontsize=11) ax.set_ylabel('1人1日当たりの排出量(g/人/日)', fontsize=11) ax.legend(fontsize=10, loc='best') ax.grid(axis='y', alpha=0.3) plt.colorbar(sc, ax=axes[1], label='1人1日排出量(g/人/日)', shrink=0.9) fig.suptitle('図4:消費支出構成割合 と 1人1日ごみ排出量 の関係\n' '(2022年度 都道府県別 N=47、ラベル: 排出量上位・下位5府県)', fontsize=12, fontweight='bold', y=1.01) fig.tight_layout() out4 = os.path.join(FIG_DIR, '2022_U4_fig4_scatter.png') fig.savefig(out4, bbox_inches='tight', dpi=150) plt.close(fig) print(f"[保存] {out4}") |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。stats.linregress(x, y) — 単回帰の傾き・切片・r値・p値・標準誤差を返します。使わない値は _ で受け取り。fig.savefig(..., bbox_inches='tight') — 余白を自動で詰めて保存。plt.close() でメモリ解放。[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 | # ── 結果サマリー ────────────────────────────────────────────────────── print("\n" + "=" * 60) print("■ 分析サマリー") print("=" * 60) print(f" 目的変数: {TARGET}") print(f" サンプル数: N = {len(df_ana)}") print(f" R² = {model.rsquared:.4f} ({model.rsquared:.1%}の変動を説明)") print(f" Adj. R² = {model.rsquared_adj:.4f}") print(f" F統計量 = {model.fvalue:.2f}, p = {model.f_pvalue:.4f}") print() print(" 【標準化回帰係数 上位変数】") sorted_abs = sorted(zip(PRED_NAMES.values(), beta_coef, np.array(beta_pvals)), key=lambda x: abs(x[1]), reverse=True) for name, beta, pv in sorted_abs: direction = "↑ 増加" if beta > 0 else "↓ 減少" sig = "**" if pv < 0.01 else ("*" if pv < 0.05 else "(n.s.)") print(f" {name}: β={beta:+.4f} → 排出量{direction} {sig}") print() print(" 【多重共線性】") for name, vif in zip(PRED_NAMES.values(), vif_vals): status = "問題なし" if vif < 5 else ("要注意" if vif < 10 else "問題あり") print(f" {name}: VIF={vif:.2f} ({status})") print("\n■ 完了。全4図を html/figures/ に保存しました。") |
[保存] html/figures/2022_U4_fig4_scatter.png ============================================================ ■ 分析サマリー ============================================================ 目的変数: 1人1日当たりの排出量 サンプル数: N = 47 R² = 0.3434 (34.3%の変動を説明) Adj. R² = 0.2633 F統計量 = 4.29, p = 0.0031 【標準化回帰係数 上位変数】 65歳以上割合(%): β=+0.4411 → 排出量↑ 増加 * 教養娯楽費割合(%): β=-0.1656 → 排出量↓ 減少 (n.s.) 年平均気温(℃): β=-0.1276 → 排出量↓ 減少 (n.s.) リサイクル率(%): β=-0.0270 → 排出量↓ 減少 (n.s.) 食料費割合(%): β=-0.0078 → 排出量↓ 減少 (n.s.) 【多重共線性】 食料費割合(%): VIF=1.53 (問題なし) 教養娯楽費割合(%): VIF=1.40 (問題なし) 65歳以上割合(%): VIF=1.82 (問題なし) 年平均気温(℃): VIF=1.43 (問題なし) リサイクル率(%): VIF=1.41 (問題なし) ■ 完了。全4図を html/figures/ に保存しました。
r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。SSDSE-B-2026 の 2022年度データ(N=47都道府県)を用いた重回帰分析により、1人1日当たりのごみ排出量の決定要因を以下の観点から分析した:
| 手法 | 目的 | 学習ポイント |
|---|---|---|
| 相関ヒートマップ | 変数間の相関構造の把握 | 多重共線性の予備診断、色と数値の読み方 |
| VIF(分散拡大因子) | 多重共線性の定量的診断 | VIF = 1/(1-R²) の計算、5/10 の基準値 |
| 標準化回帰係数 β | 説明変数の相対的重要度の比較 | z スコア変換後の OLS、効果量の解釈 |
| 散布図+回帰直線 | 2変数関係の可視化 | 単純相関と偏回帰係数の違い、交絡の概念 |
| データ・資料 | 出典・備考 |
|---|---|
| SSDSE-B-2026(都道府県別統計) | 統計数理研究所 SSDSE(社会・人口統計体系データ)2022年度 N=47都道府県 |
| ごみのリサイクル率、1人1日当たりの排出量 | SSDSE-B-2026 収録値(環境省 一般廃棄物処理事業実態調査を原典とする) |
| 消費支出・食料費・教養娯楽費(二人以上の世帯) | SSDSE-B-2026 収録値(総務省 家計調査を原典とする) |
| 65歳以上人口・総人口 | SSDSE-B-2026 収録値(総務省 人口推計を原典とする) |
| 年平均気温 | SSDSE-B-2026 収録値(気象庁データを原典とする) |
本分析スクリプトは SSDSE-B-2026.csv の実データを使用しています。合成データ(np.random.seed 等による乱数生成)は使用していません。実行には data/raw/SSDSE-B-2026.csv が必要です。
統計分析の解釈で初心者がやりがちな勘違いをまとめます。特に「相関と因果の混同」「p値の過信」は研究現場でもよく起きる落とし穴です。本文を読む前にも、読んだ後にも、目を通してみてください。
統計の基本用語を初心者向けに解説します。本文中で見慣れない言葉が出てきたら、ここに戻って確認してください。
統計手法について「何のためか」「結果をどう読むか」を初心者向けに解説します。
この研究をさらに発展させるための3つの方向性を示します。「今回わかったこと(X)」から「次に検証すべき仮説(Y)」を立て、「具体的に何をするか(Z)」まで考えてみましょう。
学んだだけでは身につきません。実際に手を動かすのが最強の学習方法です。本論文のスクリプトをベースに、以下のチャレンジに挑戦してみてください。難易度別に5つ用意しました。
本論文で学んだ手法は、研究の世界だけでなく、行政・企業・NPO の現場でも様々に活用されています。具体的なシーンを紹介します。
この論文を読んで初心者が抱きやすい疑問に、教育的観点から答えます。