このページの分析を自分で再現するには、以下の手順でデータを準備してください。コードの編集は不要です。
data/raw/ フォルダに入れます。html/figures/ に自動保存されます。
日本では「循環型社会形成推進基本法」のもとごみのリサイクル推進が重要政策とされているが、都道府県・市町村レベルでリサイクル率には大きなばらつきが存在する。本研究は 47 都道府県の 2022 年度データを用い、どのような地域・政策要因がリサイクル率の高低を説明するかを多変量統計手法で探索した。
まず「リサイクル活動に対する地域・政策要因の研究」を統計的にとらえることが有効だと考えられる。 その理由は感覚や経験則だけでは、複雑な社会要因の中で「何が本当に効いているか」を見極めにくいからである。 本研究では公開データと統計手法を組み合わせ、この問いに定量的な答えを出すことを目指す。
SSDSE-B 主成分分析 Ward法クラスタリング リサイクル率分析 政策要因
統計数理研究所 SSDSE(社会・人口統計体系)の都道府県別データ SSDSE-B-2026 から 2022 年度の 47 都道府県分を抽出して使用した。
| 項目 | 内容 |
|---|---|
| データソース | SSDSE-B-2026(統計数理研究所) |
| 対象年度 | 2022 年度 |
| 観測単位 | 47 都道府県 |
| 目的変数 | ごみのリサイクル率(%) |
| 変数(短縮名) | 元の変数名 | カテゴリ |
|---|---|---|
| ごみ総排出量 | ごみ総排出量(総量) | 廃棄物 |
| 1人1日排出量 | 1人1日当たりの排出量 | 廃棄物 |
| 消費支出 | 消費支出(二人以上の世帯) | 消費 |
| 食料費 | 食料費(二人以上の世帯) | 消費 |
| 光熱・水道費 | 光熱・水道費(二人以上の世帯) | 消費 |
| 教養娯楽費 | 教養娯楽費(二人以上の世帯) | 消費 |
| 年平均気温 | 年平均気温 | 気候 |
| 高齢化率 | 65歳以上人口 / 総人口(計算値) | 人口 |
高齢化率 = 65歳以上人口 / 総人口 × 100として計算した派生変数。高齢化率はごみ排出量やリサイクル意識と相関する可能性がある。
分析前に全説明変数を StandardScaler で標準化(平均0・標準偏差1)し、変数間のスケール差を除去した上で PCA とクラスタリングを実施した。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | print("\n" + "=" * 70) print("■ Step3. Ward法 階層的クラスタリング(47都道府県)") print("=" * 70) Z = linkage(X_scaled, method='ward') # k=4 でクラスター割り当て N_CLUSTERS = 4 labels_ward = fcluster(Z, t=N_CLUSTERS, criterion='maxclust') df_ana['cluster'] = labels_ward print(f"\n【クラスター別の都道府県(k={N_CLUSTERS})】") for cl in sorted(df_ana['cluster'].unique()): prefs_cl = df_ana[df_ana['cluster'] == cl]['都道府県'].tolist() print(f" クラスター{cl} (n={len(prefs_cl)}): {', '.join(prefs_cl)}") print(f"\n【クラスター別 リサイクル率の平均】") cl_recycling = df_ana.groupby('cluster')[TARGET].agg(['mean', 'std', 'min', 'max']) for cl, row in cl_recycling.iterrows(): print(f" クラスター{cl}: 平均={row['mean']:.2f}% SD={row['std']:.2f}% " f"[{row['min']:.1f}%~{row['max']:.1f}%]") |
====================================================================== ■ Step3. Ward法 階層的クラスタリング(47都道府県) ====================================================================== 【クラスター別の都道府県(k=4)】 クラスター1 (n=8): 石川県, 長野県, 岐阜県, 静岡県, 滋賀県, 京都府, 奈良県, 広島県 クラスター2 (n=8): 埼玉県, 千葉県, 東京都, 神奈川県, 愛知県, 大阪府, 兵庫県, 福岡県 クラスター3 (n=10): 三重県, 和歌山県, 香川県, 愛媛県, 佐賀県, 長崎県, 熊本県, 宮崎県, 鹿児島県, 沖縄県 クラスター4 (n=21): 北海道, 青森県, 岩手県, 宮城県, 秋田県, 山形県, 福島県, 茨城県, 栃木県, 群馬県, 新潟県, 富山県, 福井県, 山梨県, 鳥取県, 島根県, 岡山県, 山口県, 徳島県, 高知県, 大分県 【クラスター別 リサイクル率の平均】 クラスター1: 平均=17.34% SD=2.75% [14.4%~22.0%] クラスター2: 平均=20.94% SD=4.37% [13.0%~24.4%] クラスター3: 平均=17.09% SD=2.33% [12.4%~19.8%] クラスター4: 平均=18.03% SD=4.24% [12.8%~28.3%]
df.groupby('列').apply(関数) — グループごとに関数を適用。時系列や地域別の集計でよく使います。for _, row in df.iterrows() — DataFrameを1行ずつ取り出すループ。1点ずつ描画したいときに使用。r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。22 23 24 25 26 27 28 29 30 | ax2a = axes2[0] # クラスター色でサンプルを散布 for cl in sorted(df_ana['cluster'].unique()): mask = df_ana['cluster'] == cl ax2a.scatter(X_pca[mask, 0], X_pca[mask, 1], color=C_CLUSTER[cl], s=60, alpha=0.85, edgecolors='white', linewidth=0.5, zorder=3, label=f'クラスター{cl}') |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。{値:.2f}(小数2桁)、{値:,}(3桁区切り)、{値:>10}(右寄せ10桁)など、覚えると出力が一気に整います。31 32 33 34 35 36 | # 都道府県名ラベル for i, pref in enumerate(prefs): ax2a.annotate(pref, (X_pca[i, 0], X_pca[i, 1]), fontsize=5.5, ha='center', va='bottom', xytext=(0, 3), textcoords='offset points', color='#333333', zorder=4) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。plt.subplots(figsize=(W, H)) で図サイズ指定、fig.savefig(..., bbox_inches='tight') で余白を自動で詰めて保存。37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | # ローディングベクトル(矢印) scale = 2.5 for j, feat in enumerate(FEAT_SHORT): ax2a.annotate('', xy=(loadings[0, j] * scale, loadings[1, j] * scale), xytext=(0, 0), arrowprops=dict(arrowstyle='->', color='#C62828', lw=1.5, shrinkA=0, shrinkB=0)) ax2a.text(loadings[0, j] * scale * 1.12, loadings[1, j] * scale * 1.12, feat, fontsize=8, color='#C62828', fontweight='bold', ha='center', va='center') ax2a.axhline(0, color='gray', linewidth=0.6, linestyle='--', alpha=0.5) ax2a.axvline(0, color='gray', linewidth=0.6, linestyle='--', alpha=0.5) ax2a.set_xlabel(f'PC1(寄与率: {explained_var[0]*100:.1f}%)', fontsize=10) ax2a.set_ylabel(f'PC2(寄与率: {explained_var[1]*100:.1f}%)', fontsize=10) ax2a.set_title(f'バイプロット(PC1 vs PC2)\n累積寄与率: {cumulative_var[1]*100:.1f}%', fontsize=10, fontweight='bold') ax2a.legend(fontsize=8, loc='upper right') ax2a.grid(True, alpha=0.25) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。.dropna() は欠損行を除去、.copy() は独立したコピーを作る。pandasで警告を防ぐ定石。56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | print("\n" + "=" * 70) print("✓ 全図の生成完了(4枚)") print(" fig1_corr.png : 相関ヒートマップ(リサイクル率と説明変数)") print(" fig2_pca.png : PCA バイプロット(PC1-PC2・都道府県ラベル)") print(" fig3_dendrogram.png : Ward法デンドログラム(47都道府県)") print(" fig4_cluster.png : クラスター別プロファイル比較(棒グラフ)") print("=" * 70) print("\n【最終サマリー】") print(f"\n ■ リサイクル率との相関(上位)") for var, r in recycling_corr.items(): if abs(r) >= 0.3: print(f" {var:<14}: r = {r:+.4f}") print(f"\n ■ PCA 結果") print(f" PC1 寄与率: {explained_var[0]*100:.1f}%(主にごみ量・消費支出系)") print(f" PC2 寄与率: {explained_var[1]*100:.1f}%") print(f" PC1+PC2 累積: {cumulative_var[1]*100:.1f}%") print(f"\n ■ Ward法クラスタリング(k={N_CLUSTERS})") for cl in sorted(df_ana['cluster'].unique()): n_cl = (df_ana['cluster'] == cl).sum() m_rec = df_ana[df_ana['cluster'] == cl][TARGET].mean() prefs_cl = df_ana[df_ana['cluster'] == cl]['都道府県'].tolist() print(f" クラスター{cl}: n={n_cl}, リサイクル率平均={m_rec:.1f}%") print(f" → {', '.join(prefs_cl)}") |
======================================================================
✓ 全図の生成完了(4枚)
fig1_corr.png : 相関ヒートマップ(リサイクル率と説明変数)
fig2_pca.png : PCA バイプロット(PC1-PC2・都道府県ラベル)
fig3_dendrogram.png : Ward法デンドログラム(47都道府県)
fig4_cluster.png : クラスター別プロファイル比較(棒グラフ)
======================================================================
【最終サマリー】
■ リサイクル率との相関(上位)
消費支出 : r = +0.3693
ごみ総排出量 : r = +0.3677
■ PCA 結果
PC1 寄与率: 46.0%(主にごみ量・消費支出系)
PC2 寄与率: 27.1%
PC1+PC2 累積: 73.0%
■ Ward法クラスタリング(k=4)
クラスター1: n=8, リサイクル率平均=17.3%
→ 石川県, 長野県, 岐阜県, 静岡県, 滋賀県, 京都府, 奈良県, 広島県
クラスター2: n=8, リサイクル率平均=20.9%
→ 埼玉県, 千葉県, 東京都, 神奈川県, 愛知県, 大阪府, 兵庫県, 福岡県
クラスター3: n=10, リサイクル率平均=17.1%
→ 三重県, 和歌山県, 香川県, 愛媛県, 佐賀県, 長崎県, 熊本県, 宮崎県, 鹿児島県, 沖縄県
クラスター4: n=21, リサイクル率平均=18.0%
→ 北海道, 青森県, 岩手県, 宮城県, 秋田県, 山形県, 福島県, 茨城県, 栃木県, 群馬県, 新潟県, 富山県, 福井県, 山梨県, 鳥取県, 島根県, 岡山県, 山口県, 徳島県, 高知県, 大分県df['A'] / df['B'] — pandasの列同士の四則演算は要素ごと(element-wise)。forループ不要なのが強み。リサイクル率・ごみ関連変数・消費支出系変数・気候・人口変数の間の相関係数を行列で可視化した。
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | print("図1: 相関ヒートマップを作成中...") fig1, ax1 = plt.subplots(figsize=(9, 7)) # 相関行列(リサイクル率を含む全変数) all_vars_short = [TARGET] + FEAT_SHORT corr_mat = df_ana[[TARGET] + FEAT_SHORT].corr() im = ax1.imshow(corr_mat.values, cmap='RdBu_r', vmin=-1, vmax=1, aspect='auto') plt.colorbar(im, ax=ax1, fraction=0.046, pad=0.04, label='相関係数') tick_labels = ['リサイクル率', 'ごみ総排出量', '1人1日排出量', '消費支出', '食料費', '光熱・水道費', '教養娯楽費', '年平均気温', '高齢化率'] ax1.set_xticks(range(len(tick_labels))) ax1.set_yticks(range(len(tick_labels))) ax1.set_xticklabels(tick_labels, rotation=40, ha='right', fontsize=9) ax1.set_yticklabels(tick_labels, fontsize=9) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。s[:-n]「末尾n文字を除く」/s[n:]「先頭n文字を除く」。スライス [start:stop:step] はリスト・タプル・文字列共通の基本ワザです。100 101 102 103 104 105 106 | # 数値を各セルに表示 for i in range(len(tick_labels)): for j in range(len(tick_labels)): val = corr_mat.values[i, j] color = 'white' if abs(val) > 0.6 else 'black' ax1.text(j, i, f'{val:.2f}', ha='center', va='center', fontsize=7.5, color=color, fontweight='bold') |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。np.cumsum(arr) は累積和、np.linspace(a, b, n) は「aからbを等間隔でn個」。NumPyの定石です。107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | # リサイクル率の行・列をハイライト for pos in range(len(tick_labels)): ax1.add_patch(plt.Rectangle((pos - 0.5, -0.5), 1, 1, fill=False, edgecolor='#FFA000', linewidth=0, alpha=0)) ax1.axhline(y=0.5, color='#FFA000', linewidth=2) ax1.axhline(y=-0.5, color='#FFA000', linewidth=2) ax1.axvline(x=0.5, color='#FFA000', linewidth=2) ax1.axvline(x=-0.5, color='#FFA000', linewidth=2) ax1.set_title('相関係数行列ヒートマップ\n(2022年度 47都道府県・SSDSE-B)', fontsize=12, fontweight='bold', pad=14) fig1.text(0.5, 0.01, '出典: 統計数理研究所 SSDSE-B-2026(実データ)', ha='center', fontsize=8, color='#666') plt.tight_layout(rect=[0, 0.03, 1, 1]) save_fig('fig1_corr') |
図1: 相関ヒートマップを作成中... → html/figures/2022_U3_fig1_corr.png 保存完了
ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。{値:.2f}(小数2桁)、{値:,}(3桁区切り)、{値:>10}(右寄せ10桁)など、覚えると出力が一気に整います。標準化した 8 変数に対して主成分分析を実施し、PC1・PC2 の バイプロット と各主成分の 寄与率・累積寄与率 を可視化した。
| 主成分 | 解釈(負荷量が高い変数) | 寄与率の目安 |
|---|---|---|
| PC1 | 消費支出・ごみ排出量・生活規模系(都市規模・豊かさ軸) | 最大(第1成分) |
| PC2 | 気温・高齢化率など PC1 と直交する軸(地域特性軸) | 第2成分 |
| PC1+PC2 累積 | 2成分でデータの分散をどの程度説明できるか | 目安:70〜80%以上が望ましい |
PCA(主成分分析)は高次元データの分散を最大化する方向(主成分)を順番に求める次元削減手法。共分散行列の固有値分解によって実装される。
バイプロット(biplot)はサンプル(点)と変数(矢印)を同一グラフに重ね描きする可視化手法。変数間の関係とサンプルの分布を一度に把握できる。
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | ax2b = axes2[1] n_pcs = min(8, len(explained_var)) pc_labels = [f'PC{i+1}' for i in range(n_pcs)] bars = ax2b.bar(pc_labels, explained_var[:n_pcs] * 100, color='#1565C0', alpha=0.8, edgecolor='white', label='寄与率') ax2b_twin = ax2b.twinx() ax2b_twin.plot(pc_labels, cumulative_var[:n_pcs] * 100, 'o-', color='#E65100', linewidth=2.2, markersize=7, markerfacecolor='white', markeredgewidth=2, label='累積寄与率') ax2b_twin.axhline(80, color='#E65100', linestyle='--', linewidth=1, alpha=0.5) ax2b_twin.set_ylabel('累積寄与率(%)', fontsize=10, color='#E65100') ax2b_twin.set_ylim(0, 105) ax2b_twin.tick_params(axis='y', labelcolor='#E65100') for i, (bar, ev) in enumerate(zip(bars, explained_var[:n_pcs])): ax2b.text(bar.get_x() + bar.get_width() / 2, bar.get_height() + 0.3, f'{ev*100:.1f}%', ha='center', va='bottom', fontsize=8, color='#1565C0') for i, cv in enumerate(cumulative_var[:n_pcs]): ax2b_twin.annotate(f'{cv*100:.1f}%', (i, cv * 100), textcoords='offset points', xytext=(6, 4), fontsize=7.5, color='#E65100') ax2b.set_xlabel('主成分', fontsize=10) ax2b.set_ylabel('寄与率(%)', fontsize=10, color='#1565C0') ax2b.set_title('主成分の寄与率・累積寄与率', fontsize=10, fontweight='bold') ax2b.grid(axis='y', alpha=0.3) ax2b.tick_params(axis='y', labelcolor='#1565C0') |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。plt.subplots(figsize=(W, H)) で図サイズ指定、fig.savefig(..., bbox_inches='tight') で余白を自動で詰めて保存。151 152 153 154 155 156 157 158 159 160 | # 因子負荷量の凡例表として追加 load_text = '【因子負荷量 PC1 / PC2】\n' for j, feat in enumerate(FEAT_SHORT): load_text += f'{feat}: {loadings[0,j]:+.2f} / {loadings[1,j]:+.2f}\n' ax2b.text(0.01, 0.01, load_text.strip(), transform=ax2b.transAxes, fontsize=7, va='bottom', ha='left', fontfamily='monospace', bbox=dict(boxstyle='round', facecolor='#EFF3FF', alpha=0.9, edgecolor='#1565C0')) plt.tight_layout() save_fig('fig2_pca') |
→ html/figures/2022_U3_fig2_pca.png 保存完了
.dropna() は欠損行を除去、.copy() は独立したコピーを作る。pandasで警告を防ぐ定石。標準化した 8 変数を用いて 47 都道府県を Ward 法による階層的クラスタリング で分類した。デンドログラムのカット高さを検討し、k=4 クラスターを採用した。
※クラスター名は各クラスターの変数プロファイル(図4参照)に基づく解釈的ラベル。実際の所属都道府県は分析結果を参照。
Ward 法は「クラスターを統合した際にクラスター内分散がどれだけ増加するか」を最小化する凝集型クラスタリング手法。
クラスター数 k の選択は客観的な基準と実質的解釈の両方から判断する。階層的クラスタリングでは以下の方法が使われる。
162 163 164 165 166 167 168 169 170 | print("図3: Ward法デンドログラムを作成中...") fig3, ax3 = plt.subplots(figsize=(16, 7)) # クラスター色対応のデンドログラム cl_colors_ward = {} for i, pref in enumerate(prefs): cl = df_ana.loc[i, 'cluster'] cl_colors_ward[pref] = C_CLUSTER[cl] |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。.dropna() は欠損行を除去、.copy() は独立したコピーを作る。pandasで警告を防ぐ定石。171 172 173 174 175 176 177 178 179 180 | # デンドログラムを描画 ddata = dendrogram( Z, labels=prefs, ax=ax3, leaf_rotation=90, leaf_font_size=8, color_threshold=Z[-N_CLUSTERS + 1, 2], # k=4 のカット高さ above_threshold_color='#9E9E9E', ) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。f"...{x}..." はf-string。文字列の中に {変数} と書くだけで埋め込めて、{x:.2f} のように書式も指定できます。181 182 183 184 185 186 187 188 189 190 191 192 | # カット線 cut_height = (Z[-N_CLUSTERS, 2] + Z[-N_CLUSTERS + 1, 2]) / 2 ax3.axhline(y=cut_height, color='red', linestyle='--', linewidth=2, label=f'カット高さ(k={N_CLUSTERS}クラスター)') ax3.set_title(f'Ward法 階層的クラスタリング デンドログラム\n' f'47都道府県 ごみ・消費・人口・気温変数(2022年度・SSDSE-B)', fontsize=12, fontweight='bold') ax3.set_xlabel('都道府県', fontsize=10) ax3.set_ylabel('Ward距離(クラスター内分散の増分)', fontsize=10) ax3.legend(fontsize=9, loc='upper right') ax3.grid(axis='y', alpha=0.25) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。df['A'] / df['B'] — pandasの列同士の四則演算は要素ごと(element-wise)。forループ不要なのが強み。193 194 195 196 197 198 199 200 201 202 203 204 205 | # クラスター凡例 patch_list = [mpatches.Patch(color=C_CLUSTER[cl], label=f'クラスター{cl}') for cl in sorted(df_ana['cluster'].unique())] ax3.legend(handles=patch_list + [ mpatches.Patch(color='red', label=f'カット高さ(k={N_CLUSTERS})') ], fontsize=9, loc='upper left') fig3.text(0.5, 0.01, '出典: 統計数理研究所 SSDSE-B-2026(実データ)| Ward法: scipy.cluster.hierarchy', ha='center', fontsize=8, color='#666') plt.tight_layout(rect=[0, 0.03, 1, 1]) save_fig('fig3_dendrogram') |
図3: Ward法デンドログラムを作成中... → html/figures/2022_U3_fig3_dendrogram.png 保存完了
.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。k=4 クラスターそれぞれについて、目的変数(リサイクル率)と説明変数の平均値を比較した棒グラフで各クラスターの特徴を解釈する。
| クラスター | 想定される特徴 | リサイクル率の傾向 | 代表的な特徴変数 |
|---|---|---|---|
| CL1(赤) | 大都市圏・高消費型 | 高め | 消費支出・教養娯楽費が高い |
| CL2(青) | 中規模・バランス型 | 中程度 | 特定変数に偏りが少ない |
| CL3(緑) | 地方・高齢化型 | 変動あり | 高齢化率が全国平均より高い |
| CL4(橙) | 温暖地方・農村型 | 低め | 年平均気温が高い、ごみ排出量多め |
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 | import numpy as np import pandas as pd import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt import matplotlib.patches as mpatches from sklearn.preprocessing import StandardScaler from sklearn.decomposition import PCA from scipy.cluster.hierarchy import dendrogram, linkage, fcluster import warnings warnings.filterwarnings('ignore') plt.rcParams['font.family'] = 'Hiragino Sans' plt.rcParams['axes.unicode_minus'] = False plt.rcParams['figure.dpi'] = 150 import os FIGURE_DIR = 'html/figures' os.makedirs(FIGURE_DIR, exist_ok=True) def save_fig(name): path = os.path.join(FIGURE_DIR, f'2022_U3_{name}.png') plt.savefig(path, bbox_inches='tight', dpi=150) plt.close() print(f" → {path} 保存完了") |
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)。StandardScaler().fit_transform(X) — 各列を「平均0・分散1」に標準化。単位が違う変数のβを比較可能に。fig.savefig(..., bbox_inches='tight') — 余白を自動で詰めて保存。plt.close() でメモリ解放。f"...{x}..." はf-string。文字列の中に {変数} と書くだけで埋め込めて、{x:.2f} のように書式も指定できます。232 233 | # クラスターカラー(k=4) C_CLUSTER = {1: '#E53935', 2: '#1E88E5', 3: '#43A047', 4: '#FB8C00'} |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。df['A'] / df['B'] — pandasの列同士の四則演算は要素ごと(element-wise)。forループ不要なのが強み。234 235 236 237 238 239 240 241 242 243 | DATA_B = 'data/raw/SSDSE-B-2026.csv' 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() print("=" * 70) print("■ SSDSE-B-2026 実データ(2022年度、N=47都道府県)") print(" 出典: 統計数理研究所 SSDSE(社会・人口統計体系)") print("=" * 70) print(f" 行数: {len(df_2022)}, 列数: {df_2022.shape[1]}") |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。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ループ不要なのが強み。244 245 246 247 248 249 250 251 252 253 254 | # 分析変数の定義 TARGET = 'ごみのリサイクル率' FEATURES = { 'ごみ総排出量(総量)': 'ごみ総排出量', '1人1日当たりの排出量': '1人1日排出量', '消費支出(二人以上の世帯)': '消費支出', '食料費(二人以上の世帯)': '食料費', '光熱・水道費(二人以上の世帯)': '光熱・水道費', '教養娯楽費(二人以上の世帯)': '教養娯楽費', '年平均気温': '年平均気温', } |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 | # 高齢化率を計算(65歳以上人口 / 総人口) df_2022 = df_2022.copy() df_2022['高齢化率'] = df_2022['65歳以上人口'] / df_2022['総人口'] * 100 FEATURES['高齢化率'] = '高齢化率' # 分析用データフレームを作成 feat_cols = list(FEATURES.keys()) all_cols = [TARGET] + feat_cols df_ana = df_2022[['都道府県'] + all_cols].copy().reset_index(drop=True) df_ana.columns = ['都道府県', TARGET] + [FEATURES[c] for c in feat_cols] FEAT_SHORT = list(FEATURES.values()) # 短縮名リスト print(f"\n【分析変数一覧(N=47都道府県、2022年度)】") print(f" 目的変数: {TARGET}") print(f" 説明変数: {', '.join(FEAT_SHORT)}") print(f"\n【基本統計量】") print(df_ana[FEAT_SHORT + [TARGET]].describe().round(2)) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。.describe() — 件数・平均・標準偏差・四分位・最大/最小を一括計算。データの素性チェックに必須。[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。273 274 275 276 277 278 | # 標準化 scaler = StandardScaler() X_raw = df_ana[FEAT_SHORT].values X_scaled = scaler.fit_transform(X_raw) prefs = df_ana['都道府県'].tolist() |
======================================================================
■ SSDSE-B-2026 実データ(2022年度、N=47都道府県)
出典: 統計数理研究所 SSDSE(社会・人口統計体系)
======================================================================
行数: 47, 列数: 112
【分析変数一覧(N=47都道府県、2022年度)】
目的変数: ごみのリサイクル率
説明変数: ごみ総排出量, 1人1日排出量, 消費支出, 食料費, 光熱・水道費, 教養娯楽費, 年平均気温, 高齢化率
【基本統計量】
ごみ総排出量 1人1日排出量 消費支出 ... 年平均気温 高齢化率 ごみのリサイクル率
count 47.00 47.00 47.00 ... 47.00 47.00 47.00
mean 858384.81 906.09 289630.36 ... 16.07 31.35 18.21
std 847021.74 61.51 19186.91 ... 2.29 3.27 3.83
min 197867.00 770.00 245054.00 ... 10.20 22.81 12.40
25% 373903.50 863.50 276834.50 ... 15.25 29.85 15.30
50% 529085.00 911.00 287781.00 ... 16.40 31.42 16.90
75% 837860.00 951.50 302255.00 ... 17.35 33.72 21.25
max 4150715.00 1021.00 324793.00 ... 23.70 38.60 28.30
[8 rows x 9 columns]StandardScaler().fit_transform(X) — 各列を「平均0・分散1」に標準化。単位が違う変数のβを比較可能に。r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。279 280 281 282 283 284 285 286 287 288 289 | print("\n" + "=" * 70) print("■ Step1. 相関分析(リサイクル率 vs 説明変数)") print("=" * 70) corr_df = df_ana[[TARGET] + FEAT_SHORT].corr() print(f"\n【リサイクル率との相関係数】") recycling_corr = corr_df[TARGET].drop(TARGET).sort_values(ascending=False) for var, r in recycling_corr.items(): direction = '+' if r > 0 else '-' strength = '強' if abs(r) >= 0.5 else '中' if abs(r) >= 0.3 else '弱' print(f" {var:<14}: r = {r:+.4f} ({direction} {strength})") |
====================================================================== ■ Step1. 相関分析(リサイクル率 vs 説明変数) ====================================================================== 【リサイクル率との相関係数】 消費支出 : r = +0.3693 (+ 中) ごみ総排出量 : r = +0.3677 (+ 中) 教養娯楽費 : r = +0.2927 (+ 弱) 食料費 : r = +0.0852 (+ 弱) 年平均気温 : r = +0.0229 (+ 弱) 光熱・水道費 : r = -0.0854 (- 弱) 1人1日排出量 : r = -0.1589 (- 弱) 高齢化率 : r = -0.2310 (- 弱)
sort_values('列名', ascending=False) — 指定列で並べ替え(降順)。.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 | print("\n" + "=" * 70) print("■ Step2. 主成分分析(PCA)") print("=" * 70) pca = PCA() pca.fit(X_scaled) X_pca = pca.transform(X_scaled) explained_var = pca.explained_variance_ratio_ cumulative_var = np.cumsum(explained_var) loadings = pca.components_ # shape: (n_components, n_features) print(f"\n【主成分の寄与率】") print(f" {'PC':<6} {'固有値':>8} {'寄与率':>9} {'累積寄与率':>11}") print(" " + "-" * 38) eigenvalues = pca.explained_variance_ for i in range(min(6, len(explained_var))): print(f" PC{i+1:<4} {eigenvalues[i]:>8.4f} {explained_var[i]*100:>8.2f}% {cumulative_var[i]*100:>10.2f}%") print(f"\n【PC1・PC2 因子負荷量(loadings)】") print(f" {'変数':<14} {'PC1 負荷量':>11} {'PC2 負荷量':>11}") print(" " + "-" * 38) for j, feat in enumerate(FEAT_SHORT): print(f" {feat:<14} {loadings[0, j]:>+11.4f} {loadings[1, j]:>+11.4f}") print(f"\n PC1 寄与率: {explained_var[0]*100:.1f}% PC2 寄与率: {explained_var[1]*100:.1f}%") print(f" PC1+PC2 累積: {cumulative_var[1]*100:.1f}%") |
====================================================================== ■ Step2. 主成分分析(PCA) ====================================================================== 【主成分の寄与率】 PC 固有値 寄与率 累積寄与率 -------------------------------------- PC1 3.7565 45.96% 45.96% PC2 2.2129 27.07% 73.03% PC3 0.7109 8.70% 81.73% PC4 0.6314 7.72% 89.45% PC5 0.3601 4.41% 93.86% PC6 0.2069 2.53% 96.39% 【PC1・PC2 因子負荷量(loadings)】 変数 PC1 負荷量 PC2 負荷量 -------------------------------------- ごみ総排出量 +0.4050 +0.0422 1人1日排出量 -0.3316 -0.2125 消費支出 +0.3864 -0.2494 食料費 +0.4330 -0.2703 光熱・水道費 -0.1349 -0.5874 教養娯楽費 +0.4408 -0.2154 年平均気温 +0.0731 +0.6094 高齢化率 -0.4139 -0.2344 PC1 寄与率: 46.0% PC2 寄与率: 27.1% PC1+PC2 累積: 73.0%
[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 | print("\n" + "=" * 70) print("■ Step4. クラスター別プロファイル比較(標準化平均)") print("=" * 70) df_ana_z = df_ana.copy() for j, feat in enumerate(FEAT_SHORT): df_ana_z[feat + '_z'] = X_scaled[:, j] profile = df_ana_z.groupby('cluster')[[f + '_z' for f in FEAT_SHORT]].mean() print(f"\n【クラスター別 標準化変数平均値(高いほど全国平均より上)】") print(f" {'変数':<14}", end='') for cl in sorted(df_ana['cluster'].unique()): print(f" {'CL' + str(cl):>8}", end='') print() print(" " + "-" * (14 + 9 * N_CLUSTERS)) for feat in FEAT_SHORT: print(f" {feat:<14}", end='') for cl in sorted(df_ana['cluster'].unique()): v = profile.loc[cl, feat + '_z'] print(f" {v:>+8.3f}", end='') print() |
====================================================================== ■ Step4. クラスター別プロファイル比較(標準化平均) ====================================================================== 【クラスター別 標準化変数平均値(高いほど全国平均より上)】 変数 CL1 CL2 CL3 CL4 -------------------------------------------------- ごみ総排出量 -0.261 +1.929 -0.515 -0.390 1人1日排出量 -1.137 -0.778 -0.100 +0.777 消費支出 +0.762 +0.689 -1.057 -0.049 食料費 +0.814 +1.163 -1.172 -0.195 光熱・水道費 +0.251 -0.814 -0.815 +0.603 教養娯楽費 +0.717 +1.213 -1.051 -0.235 年平均気温 -0.013 +0.396 +0.995 -0.620 高齢化率 -0.290 -1.370 +0.153 +0.560
df.groupby('列').apply(関数) — グループごとに関数を適用。時系列や地域別の集計でよく使います。x if cond else y は三項演算子。リスト内包表記と組み合わせると、forとifを1行で書けます。338 339 340 | print("\n\n" + "=" * 70) print("■ 図の生成(4枚)") print("=" * 70) |
====================================================================== ■ 図の生成(4枚) ======================================================================
df[col](1列)と df[[col1, col2]](複数列)でカッコの数が違います。リストを渡していると覚えるとミスを減らせます。341 342 343 344 345 346 | print("図2: PCA バイプロットを作成中...") fig2, axes2 = plt.subplots(1, 2, figsize=(16, 6.5)) fig2.suptitle('Step2. 主成分分析(PCA)バイプロット\n' '2022年度 47都道府県 ごみ・消費・人口・気温変数(SSDSE-B)', fontsize=12, fontweight='bold') |
図2: PCA バイプロットを作成中...
fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。np.cumsum(arr) は累積和、np.linspace(a, b, n) は「aからbを等間隔でn個」。NumPyの定石です。347 348 349 350 351 352 353 354 | print("図4: クラスター別プロファイル棒グラフを作成中...") fig4, axes4 = plt.subplots(2, 4, figsize=(16, 9)) fig4.suptitle(f'Step4. Ward法クラスタリング(k={N_CLUSTERS})クラスター別プロファイル比較\n' '2022年度 47都道府県(SSDSE-B-2026 実データ)', fontsize=12, fontweight='bold') axes4_flat = axes4.flatten() |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。f"...{x}..." はf-string。文字列の中に {変数} と書くだけで埋め込めて、{x:.2f} のように書式も指定できます。355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 | # リサイクル率をプロファイル変数に追加 profile_vars = [TARGET] + FEAT_SHORT n_vars_p = len(profile_vars) cl_order = sorted(df_ana['cluster'].unique()) x_pos = np.arange(len(cl_order)) for ax_idx, var in enumerate(profile_vars): if ax_idx >= len(axes4_flat): break ax = axes4_flat[ax_idx] means = [df_ana[df_ana['cluster'] == cl][var].mean() for cl in cl_order] stds = [df_ana[df_ana['cluster'] == cl][var].std() for cl in cl_order] bars = ax.bar(x_pos, means, yerr=stds, capsize=4, color=[C_CLUSTER[cl] for cl in cl_order], alpha=0.85, edgecolor='white', error_kw={'elinewidth': 1.2, 'capthick': 1.2}) ax.set_xticks(x_pos) ax.set_xticklabels([f'CL{cl}' for cl in cl_order], fontsize=9) ax.set_title(var, fontsize=9, fontweight='bold') ax.grid(axis='y', alpha=0.3) # 全国平均との差を示す水平線 overall_mean = df_ana[var].mean() ax.axhline(overall_mean, color='black', linestyle='--', linewidth=1.2, alpha=0.7, label=f'全国平均 ({overall_mean:.1f})') ax.legend(fontsize=6.5, loc='upper right') # 値ラベル for bar, m, s in zip(bars, means, stds): ax.text(bar.get_x() + bar.get_width() / 2, m + s + (max(means) - min(means)) * 0.03, f'{m:.1f}', ha='center', va='bottom', fontsize=7, fontweight='bold') |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。df['A'] / df['B'] — pandasの列同士の四則演算は要素ごと(element-wise)。forループ不要なのが強み。391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 | # 余った軸を非表示 for ax_idx in range(n_vars_p, len(axes4_flat)): axes4_flat[ax_idx].set_visible(False) # クラスター凡例(図全体) patch_list4 = [mpatches.Patch(color=C_CLUSTER[cl], label=f'クラスター{cl}') for cl in cl_order] fig4.legend(handles=patch_list4, fontsize=10, loc='lower right', bbox_to_anchor=(0.98, 0.02), title='クラスター', title_fontsize=10) fig4.text(0.5, 0.005, '出典: 統計数理研究所 SSDSE-B-2026 | Ward法(scipy)| エラーバー: ±1 SD', ha='center', fontsize=8, color='#666') plt.tight_layout(rect=[0, 0.03, 1, 0.96]) save_fig('fig4_cluster') |
図4: クラスター別プロファイル棒グラフを作成中... → html/figures/2022_U3_fig4_cluster.png 保存完了
.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。SSDSE-B の 47 都道府県 2022 年度データを用い、PCA と Ward 法クラスタリングの組み合わせにより以下の知見が得られた:
| データ | 出典 |
|---|---|
| SSDSE-B-2026 都道府県別統計(2022年度) | 統計数理研究所 SSDSE(社会・人口統計体系) |
| ごみのリサイクル率(都道府県別) | SSDSE-B 廃棄物関連変数 |
| 消費支出・食料費等(世帯統計) | SSDSE-B 家計関連変数(総務省 家計調査ベース) |
| 年平均気温(都道府県別) | SSDSE-B 気候関連変数(気象庁ベース) |
本分析コードは SSDSE-B-2026 の実データを使用(合成データ・乱数生成は一切使用しない)。
統計分析の解釈で初心者がやりがちな勘違いをまとめます。特に「相関と因果の混同」「p値の過信」は研究現場でもよく起きる落とし穴です。本文を読む前にも、読んだ後にも、目を通してみてください。
統計の基本用語を初心者向けに解説します。本文中で見慣れない言葉が出てきたら、ここに戻って確認してください。
統計手法について「何のためか」「結果をどう読むか」を初心者向けに解説します。
この研究をさらに発展させるための3つの方向性を示します。「今回わかったこと(X)」から「次に検証すべき仮説(Y)」を立て、「具体的に何をするか(Z)」まで考えてみましょう。
学んだだけでは身につきません。実際に手を動かすのが最強の学習方法です。本論文のスクリプトをベースに、以下のチャレンジに挑戦してみてください。難易度別に5つ用意しました。
本論文で学んだ手法は、研究の世界だけでなく、行政・企業・NPO の現場でも様々に活用されています。具体的なシーンを紹介します。
この論文を読んで初心者が抱きやすい疑問に、教育的観点から答えます。