この教材は、原論文が使ったデータそのものを使えていません。そこで代わりのデータで同じ問いを追いかけ、結論の向き(増える/減る、強い/弱い)が原論文と一致するかを確かめます。「同じ数値が出る」ことは目標にしていません。
| 原論文が使ったデータ | 論文本文からは特定できなかった 分析単位:市区町村 中核手法:サポートベクター回帰・テキストマイニング・重回帰分析 |
|---|---|
| この教材が使うデータ |
|
| 原論文(PDF) | 地方創生を推進する多角的複合指標の提案―SVM に基づく主観的でない変数選択と重み付け手法の検討― 審査員奨励賞/衣川 凌太(神戸大学国際人間科学部環境共生学科) |
▶ ここに書いてある分析は、ページ下部の「🐍 ブラウザで動かす」でそのまま実行できます(インストール不要)。動かしているのは code/2024_U5_2_shorei.py(452 行)そのものです。
このページの分析を自分で再現するには、以下の手順でデータを準備してください。コードの編集は不要です。
data/raw/ フォルダに入れます。html/figures/ に自動保存されます。
地方創生政策の効果を評価するには、「どの地域が地方創生に成功しているか」を多面的・客観的に測定する指標が必要である。本研究は、SVMの学習係数を利用して主観的な恣意性を排した変数選択と重み付けを行い、複合指標(Composite Index)を構築した。
この論文が挑んだ問いは「地方創生政策が地域の人口動態に与えた影響はあるか」(テーマ:移住・人口移動・財政・行政)。 著者はLASSO・SVM・VIF/多重共線性を軸にこの問いへ定量的に答えを出した。 本ページではその分析の流れを実データで再現しながら、使われた統計手法を一つずつ学んでいく。
SSDSE-B SVM(線形カーネル) 複合指標 PCA比較
SSDSE(社会・人口統計体系データセット)-B から、雇用・子育て・移住・経済の4カテゴリで計12変数を選定した。
| カテゴリ | 変数 | 地方創生との関係 |
|---|---|---|
| 雇用 | 有効求人倍率 | 雇用機会の充実度 |
| 若者雇用率 | 若者の地域定着 | |
| 女性就業率 | 多様な雇用 | |
| 子育て | 保育所充足率 | 子育て環境整備 |
| 合計特殊出生率 | 少子化対策 | |
| 子育て支援充実度 | 行政サービス水準 | |
| 移住 | 転入超過率 | 人口流入の実績 |
| 移住者受入施策スコア | 移住政策の積極性 | |
| 関係人口指数 | 交流人口の拡大 | |
| 経済 | 一人当たりGDP | 経済力の水準 |
| 中小企業活力指数 | 産業の多様性 | |
| 農業産出額成長率 | 地域産業の成長 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | print("\n" + "=" * 60) print("■ Step3. 複合指標(SVM重み付き)の構築") print("=" * 60) weights_norm = svm_weights / svm_weights.sum() composite_index = X_scaled @ weights_norm # (47,) rank_df = pd.DataFrame({ '都道府県': PREFS, '複合指標(SVM)': composite_index, }).sort_values('複合指標(SVM)', ascending=False).reset_index(drop=True) rank_df['順位'] = range(1, len(PREFS) + 1) print("【複合指標ランキング(上位10)】") print(rank_df.head(10).to_string(index=False)) |
============================================================ ■ Step3. 複合指標(SVM重み付き)の構築 ============================================================ 【複合指標ランキング(上位10)】 都道府県 複合指標(SVM) 順位 東京都 1.059180 1 高知県 0.722956 2 京都府 0.502017 3 長崎県 0.394129 4 佐賀県 0.333988 5 宮崎県 0.312622 6 大分県 0.275178 7 石川県 0.259513 8 島根県 0.248046 9 鳥取県 0.228208 10
sort_values('列名', ascending=False) — 指定列で並べ替え(降順)。.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。線形カーネルのSVR(Support Vector Regression)を学習し、各変数の係数の絶対値を「変数重要度」として扱う。係数が大きいほど、その変数が地方創生度の予測に重要であることを意味する。
線形カーネルのSVMは係数(重み)が直接解釈できる。ランダムフォレストの特徴量重要度や主成分分析と異なり、「各変数が目的変数にどれだけ貢献するか」を1つの係数ベクトルで明示できる。
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | print("\n図1: SVM重み棒グラフを作成中...") n_vars = len(VAR_NAMES) colors_bar = ['#1565C0' if i < 3 else '#90CAF9' for i in range(n_vars)] fig1, ax1 = plt.subplots(figsize=(10, 6)) # weight_df は降順 → 横棒グラフは上から重み大の順になるよう逆転 bars = ax1.barh( weight_df['変数'][::-1], weight_df['SVM重み(絶対値)'][::-1], color=colors_bar[::-1], edgecolor='white', alpha=0.88, ) mean_w = weight_df['SVM重み(絶対値)'].mean() ax1.axvline(mean_w, color='gray', linestyle='--', linewidth=1.2, label=f'平均 = {mean_w:.3f}') ax1.set_xlabel('LinearSVR係数の絶対値(変数重要度)', fontsize=12) ax1.set_title( 'SVM(線形カーネル)による変数重要度\n地方創生関連変数のランキング(2022年度, 47都道府県)', fontsize=13, fontweight='bold', ) ax1.legend(fontsize=10) ax1.grid(axis='x', alpha=0.3) for bar, val in zip(bars, weight_df['SVM重み(絶対値)'][::-1]): ax1.text(val + mean_w * 0.02, bar.get_y() + bar.get_height() / 2, f'{val:.3f}', va='center', fontsize=9, color='#333') plt.tight_layout() fig1.savefig(os.path.join(FIG_DIR, '2024_U5_2_fig1_svm_weights.png'), bbox_inches='tight', dpi=150) plt.close(fig1) print(" → 2024_U5_2_fig1_svm_weights.png 保存完了") |
図1: SVM重み棒グラフを作成中... → 2024_U5_2_fig1_svm_weights.png 保存完了
fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。SVM重みを正規化して各変数に掛け合わせ、都道府県ごとの「地方創生度スコア」を算出する。スコアが高いほど、地方創生の取り組みが総合的に優れている都道府県と評価される。
複合指標は「加重平均」の一種。SVMの重みを使うことで、純粋なデータ駆動型の重み付けが実現する。PCAやHDI(人間開発指数)など、さまざまな複合指標手法と比較して評価することが重要。
51 52 53 54 55 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 82 83 84 85 86 87 | print("図2: 複合指標ランキングを作成中...") top15 = rank_df.head(15).copy() bot15 = rank_df.tail(15).iloc[::-1].copy() # 上位15の最高スコア都道府県を強調 top1_name = rank_df.iloc[0]['都道府県'] last_name = rank_df.iloc[-1]['都道府県'] fig2, axes2 = plt.subplots(1, 2, figsize=(14, 7)) fig2.suptitle('地方創生度 複合指標ランキング(LinearSVR重み付き, 2022年度)', fontsize=13, fontweight='bold') for ax, sub_df, title, clr_hi, clr_lo in zip( axes2, [top15, bot15], ['上位15都道府県', '下位15都道府県'], ['#1565C0', '#C62828'], ['#90CAF9', '#EF9A9A'], ): bar_colors = [ clr_hi if row['都道府県'] == top1_name or row['都道府県'] == last_name else clr_lo for _, row in sub_df.iterrows() ] ax.barh(sub_df['都道府県'], sub_df['複合指標(SVM)'], color=bar_colors, edgecolor='white', alpha=0.88) ax.axvline(0, color='black', linewidth=0.8) ax.set_xlabel('複合指標スコア(標準化加重和)', fontsize=11) ax.set_title(title, fontsize=12, fontweight='bold') ax.grid(axis='x', alpha=0.3) plt.tight_layout() fig2.savefig(os.path.join(FIG_DIR, '2024_U5_2_fig2_index_rank.png'), bbox_inches='tight', dpi=150) plt.close(fig2) print(" → 2024_U5_2_fig2_index_rank.png 保存完了") |
図2: 複合指標ランキングを作成中... → 2024_U5_2_fig2_index_rank.png 保存完了
fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。for _, row in df.iterrows() — DataFrameを1行ずつ取り出すループ。1点ずつ描画したいときに使用。x if cond else y は三項演算子。リスト内包表記と組み合わせると、forとifを1行で書けます。主成分分析(PCA)の第1主成分スコアを地方創生度の代替指標として算出し、SVM複合指標との一致度を確認する。両者の相関が高ければ、SVM複合指標が統計的に妥当な指標であることの傍証になる。
| 指標 | 長所 | 短所 |
|---|---|---|
| SVM複合指標 | 目的変数に基づく客観的重み付け | 目的変数の定義が必要 |
| PCA第1主成分 | 分散最大化で教師なし圧縮 | 解釈が難しい場合がある |
| 等価重み付け | 最もシンプル | 変数の重要度を無視 |
バイプロットでは都道府県(点)と変数(矢印)を同一平面に表示する。矢印の方向に近い都道府県はその変数が高く、矢印の長さが長いほどその変数の第1・第2主成分への寄与が大きい。
89 90 91 92 93 94 95 96 97 98 99 100 101 | import os import numpy as np import pandas as pd import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt import warnings warnings.filterwarnings('ignore') from sklearn.svm import LinearSVR from sklearn.preprocessing import StandardScaler from sklearn.decomposition import PCA from scipy import stats as scipy_stats |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。import pandas as pd など — 必要なライブラリをまとめて呼び出します。as pd は短い別名(alias)。matplotlib.use('Agg') — グラフを画面表示せずファイルに保存するためのおまじない。StandardScaler().fit_transform(X) — 各列を「平均0・分散1」に標準化。単位が違う変数のβを比較可能に。f"...{x}..." はf-string。文字列の中に {変数} と書くだけで埋め込めて、{x:.2f} のように書式も指定できます。102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | # ── パス設定 ───────────────────────────────────────────────────────────────── BASE_DIR = os.path.join(_script_dir, '..') FIG_DIR = os.path.join(BASE_DIR, 'html', 'figures') DATA_PATH = os.path.join(BASE_DIR, 'data', 'raw', 'SSDSE-B-2026.csv') 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("=" * 60) print("■ Step1. SSDSE-B-2026 実データ読み込み(2022年度)") print("=" * 60) df_b = pd.read_csv(DATA_PATH, encoding='cp932', header=1) # 都道府県行のみ(市区町村を除外) df_b = df_b[df_b['地域コード'].str.match(r'^R\d{5}$', na=False)] df = df_b[df_b['年度'] == 2022].copy().reset_index(drop=True) print(f" 読み込み完了: {len(df)} 都道府県") |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。os.makedirs('html/figures', exist_ok=True) — 図の保存先フォルダを作る(既にあってもOK)。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ループ不要なのが強み。125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | # 都道府県名を短縮(「県」「都」「府」「道」を保持しつつ表示用に整理) PREFS = df['都道府県'].tolist() # ── 変数算出 ────────────────────────────────────────────────────────────────── pop = df['総人口'] # A1101 tfr = df['合計特殊出生率'] # A4103 ← 目的変数 inflow = df['転入者数(日本人移動者)'] # A5101 outflow = df['転出者数(日本人移動者)'] # A5102 nursery_n = df['保育所等数'] # J2503 nursery_c = df['保育所等定員数'] # J2505 univ_grads= df['高等学校卒業者のうち進学者数'] # E4602 hs_grads = df['高等学校卒業者数'] # E4601 consumption= df['消費支出(二人以上の世帯)'] # L3221 land_price = df['標準価格(平均価格)(住宅地)'] # C5401 job_open = df['月間有効求人数(一般)'] # F3103 job_seek = df['月間有効求職者数(一般)'] # F3102 elderly = df['65歳以上人口'] # A1303 hospitals = df['一般病院数'] # I510120 tourists = df['延べ宿泊者数'] # G7101 |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。144 145 146 147 148 149 150 151 152 153 | # 派生指標算出 in_rate = inflow / pop * 100 # 転入率 (%) out_rate = outflow / pop * 100 # 転出率 (%) nursery_dens = nursery_n / pop * 10000 # 保育所密度 (10000人当たり) nursery_cap = nursery_c / pop * 10000 # 保育定員密度 (10000人当たり) univ_rate = univ_grads / hs_grads * 100 # 高校→大学進学率 (%) job_ratio = job_open / job_seek # 有効求人倍率 aging_rate = elderly / pop * 100 # 高齢化率 (%) hosp_dens = hospitals / pop * 10000 # 一般病院密度 (10000人当たり) tourist_pc = tourists / pop # 宿泊者数per capita |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。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 181 182 183 184 185 186 | # 説明変数・変数名を整理 VAR_NAMES = [ '転入率(%)', '転出率(%)', '保育所密度', '保育定員密度', '大学進学率(%)', '消費支出(万円)', '住宅地標準価格', '有効求人倍率', '高齢化率(%)', '病院密度', '宿泊者数/人', ] X_raw = np.column_stack([ in_rate.values, out_rate.values, nursery_dens.values, nursery_cap.values, univ_rate.values, consumption.values / 10000, # 万円単位にスケール調整 land_price.values, job_ratio.values, aging_rate.values, hosp_dens.values, tourist_pc.values, ]) y = tfr.values # 目的変数:合計特殊出生率 print(f" 説明変数 ({len(VAR_NAMES)}個): {VAR_NAMES}") print(f" 目的変数: 合計特殊出生率(TFR), 範囲 {y.min():.2f} – {y.max():.2f}") |
説明変数 (11個): ['転入率(%)', '転出率(%)', '保育所密度', '保育定員密度', '大学進学率(%)', '消費支出(万円)', '住宅地標準価格', '有効求人倍率', '高齢化率(%)', '病院密度', '宿泊者数/人'] 目的変数: 合計特殊出生率(TFR), 範囲 1.04 – 1.70
r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 | print("\n" + "=" * 60) print("■ Step2. LinearSVR変数重み算出") print("=" * 60) scaler = StandardScaler() X_scaled = scaler.fit_transform(X_raw) svr = LinearSVR(C=1.0, max_iter=10000) svr.fit(X_scaled, y) svm_weights = np.abs(svr.coef_) # shape: (n_features,) weight_df = pd.DataFrame({ '変数': VAR_NAMES, 'SVM重み(絶対値)': svm_weights, }).sort_values('SVM重み(絶対値)', ascending=False).reset_index(drop=True) print("【SVM変数重み(降順)】") print(weight_df.round(4).to_string(index=False)) |
============================================================
■ Step2. LinearSVR変数重み算出
============================================================
【SVM変数重み(降順)】
変数 SVM重み(絶対値)
高齢化率(%) 0.0934
保育定員密度 0.0834
住宅地標準価格 0.0695
転入率(%) 0.0547
転出率(%) 0.0546
病院密度 0.0466
宿泊者数/人 0.0287
消費支出(万円) 0.0260
大学進学率(%) 0.0156
有効求人倍率 0.0017
保育所密度 0.0016sort_values('列名', ascending=False) — 指定列で並べ替え(降順)。StandardScaler().fit_transform(X) — 各列を「平均0・分散1」に標準化。単位が違う変数のβを比較可能に。df['A'] / df['B'] — pandasの列同士の四則演算は要素ごと(element-wise)。forループ不要なのが強み。205 206 207 208 209 210 211 212 213 214 | print("\n" + "=" * 60) print("■ Step4. PCA(主成分分析)との比較") print("=" * 60) pca = PCA(n_components=2) X_pca = pca.fit_transform(X_scaled) pca_index = X_pca[:, 0] # 第1主成分をPCA指標として使用 print(f" PCA説明分散比: PC1={pca.explained_variance_ratio_[0]:.3f}, " f"PC2={pca.explained_variance_ratio_[1]:.3f}") |
============================================================ ■ Step4. PCA(主成分分析)との比較 ============================================================ PCA説明分散比: PC1=0.428, PC2=0.176
[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 | print("図3: PCAバイプロットを作成中...") # 都道府県ラベル表示: 複合指標上位10・下位10 top10_prefs = set(rank_df.head(10)['都道府県'].tolist()) bot10_prefs = set(rank_df.tail(10)['都道府県'].tolist()) label_prefs = top10_prefs | bot10_prefs fig3, ax3 = plt.subplots(figsize=(11, 8)) sc = ax3.scatter( X_pca[:, 0], X_pca[:, 1], c=composite_index, cmap='RdYlBu', s=65, alpha=0.82, zorder=3, ) plt.colorbar(sc, ax=ax3, label='SVM複合指標スコア') for i, pref in enumerate(PREFS): if pref in label_prefs: ax3.annotate(pref, (X_pca[i, 0], X_pca[i, 1]), textcoords='offset points', xytext=(6, 3), fontsize=7.5, color='#333') |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。df[col](1列)と df[[col1, col2]](複数列)でカッコの数が違います。リストを渡していると覚えるとミスを減らせます。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 | # ローディングベクトル(矢印) loadings = pca.components_.T # shape: (n_features, 2) scale = 2.2 for j, var in enumerate(VAR_NAMES): ax3.annotate( '', xy=(loadings[j, 0] * scale, loadings[j, 1] * scale), xytext=(0, 0), arrowprops=dict(arrowstyle='->', color='#E65100', lw=1.5), ) ax3.text( loadings[j, 0] * scale * 1.12, loadings[j, 1] * scale * 1.12, var, color='#E65100', fontsize=8, ) ax3.axhline(0, color='gray', linewidth=0.5) ax3.axvline(0, color='gray', linewidth=0.5) ax3.set_xlabel(f'PC1(説明率 {pca.explained_variance_ratio_[0]*100:.1f}%)', fontsize=12) ax3.set_ylabel(f'PC2(説明率 {pca.explained_variance_ratio_[1]*100:.1f}%)', fontsize=12) ax3.set_title( 'PCAバイプロット(都道府県 × 地方創生変数, 2022年度)\n色:SVM複合指標スコア', fontsize=13, fontweight='bold', ) ax3.grid(True, alpha=0.2) plt.tight_layout() fig3.savefig(os.path.join(FIG_DIR, '2024_U5_2_fig3_pca.png'), bbox_inches='tight', dpi=150) plt.close(fig3) print(" → 2024_U5_2_fig3_pca.png 保存完了") |
→ 2024_U5_2_fig3_pca.png 保存完了
ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。s[:-n]「末尾n文字を除く」/s[n:]「先頭n文字を除く」。スライス [start:stop:step] はリスト・タプル・文字列共通の基本ワザです。264 265 266 267 268 269 270 271 272 273 274 | print("図4: SVM指標 vs PCA指標散布図を作成中...") # 両指標を標準化(方向を揃えるため) svm_std = (composite_index - composite_index.mean()) / composite_index.std() pca_std = (pca_index - pca_index.mean()) / pca_index.std() # PCAとSVM指標の方向が逆の場合は符号を揃える(正の相関になるよう) if scipy_stats.pearsonr(svm_std, pca_std)[0] < 0: pca_std = -pca_std r_val, p_val = scipy_stats.pearsonr(svm_std, pca_std) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。stats.pearsonr(x, y) — Pearson相関係数 r と p値を同時に返します。s[:-n]「末尾n文字を除く」/s[n:]「先頭n文字を除く」。スライス [start:stop:step] はリスト・タプル・文字列共通の基本ワザです。275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 | # 上位・下位5都道府県をラベル表示 top5_prefs = set(rank_df.head(5)['都道府県'].tolist()) bot5_prefs = set(rank_df.tail(5)['都道府県'].tolist()) label5_prefs = top5_prefs | bot5_prefs scatter_c = [ '#E53935' if p in top5_prefs else '#1E88E5' if p in bot5_prefs else '#BDBDBD' for p in PREFS ] fig4, ax4 = plt.subplots(figsize=(9, 7)) ax4.scatter(svm_std, pca_std, c=scatter_c, s=70, alpha=0.85, zorder=3) for i, pref in enumerate(PREFS): if pref in label5_prefs: ax4.annotate(pref, (svm_std[i], pca_std[i]), textcoords='offset points', xytext=(7, 3), fontsize=9.5, fontweight='bold', color='#C62828' if pref in top5_prefs else '#0D47A1') |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。np.cumsum(arr) は累積和、np.linspace(a, b, n) は「aからbを等間隔でn個」。NumPyの定石です。296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 | # 回帰直線 x_fit = np.linspace(svm_std.min(), svm_std.max(), 200) coef = np.polyfit(svm_std, pca_std, 1) ax4.plot(x_fit, np.polyval(coef, x_fit), color='#444', linewidth=1.8, linestyle='--') ax4.axhline(0, color='gray', linewidth=0.5) ax4.axvline(0, color='gray', linewidth=0.5) ax4.set_xlabel('SVM複合指標(標準化)', fontsize=12) ax4.set_ylabel('PCA第1主成分スコア(標準化)', fontsize=12) p_str = '<0.001' if p_val < 0.001 else f'={p_val:.3f}' ax4.set_title( f'SVM複合指標 vs PCA第1主成分(2022年度, 47都道府県)\nr = {r_val:.3f} (p{p_str})', fontsize=13, fontweight='bold', ) ax4.grid(True, alpha=0.2) ax4.text( 0.05, 0.95, f'相関係数 r = {r_val:.3f}', transform=ax4.transAxes, fontsize=11, va='top', bbox=dict(boxstyle='round', facecolor='#E3F2FD', alpha=0.8), ) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。{値:.2f}(小数2桁)、{値:,}(3桁区切り)、{値:>10}(右寄せ10桁)など、覚えると出力が一気に整います。318 319 320 321 322 323 324 325 326 327 328 329 330 331 | # 凡例 from matplotlib.patches import Patch legend_elements = [ Patch(facecolor='#E53935', label='SVM複合指標 上位5'), Patch(facecolor='#1E88E5', label='SVM複合指標 下位5'), Patch(facecolor='#BDBDBD', label='その他'), ] ax4.legend(handles=legend_elements, fontsize=9, loc='lower right') plt.tight_layout() fig4.savefig(os.path.join(FIG_DIR, '2024_U5_2_fig4_scatter.png'), bbox_inches='tight', dpi=150) plt.close(fig4) print(" → 2024_U5_2_fig4_scatter.png 保存完了") |
→ 2024_U5_2_fig4_scatter.png 保存完了
import pandas as pd など — 必要なライブラリをまとめて呼び出します。as pd は短い別名(alias)。plt.subplots(figsize=(W, H)) で図サイズ指定、fig.savefig(..., bbox_inches='tight') で余白を自動で詰めて保存。332 333 334 335 336 337 338 339 340 341 | print("\n" + "=" * 60) print("全図の生成完了(4枚)") print("=" * 60) print("\n【主要知見】") print(f" SVM変数重み上位3変数: {weight_df['変数'].head(3).tolist()}") print(f" 複合指標1位: {rank_df.iloc[0]['都道府県']}(スコア = {rank_df.iloc[0]['複合指標(SVM)']:.3f})") print(f" 複合指標最下位: {rank_df.iloc[-1]['都道府県']}(スコア = {rank_df.iloc[-1]['複合指標(SVM)']:.3f})") print(f" SVM指標 vs PCA指標 相関: r = {r_val:.3f}") print(f" PCA説明率: PC1={pca.explained_variance_ratio_[0]*100:.1f}%, " f"PC2={pca.explained_variance_ratio_[1]*100:.1f}%") |
============================================================ 全図の生成完了(4枚) ============================================================ 【主要知見】 SVM変数重み上位3変数: ['高齢化率(%)', '保育定員密度', '住宅地標準価格'] 複合指標1位: 東京都(スコア = 1.059) 複合指標最下位: 北海道(スコア = -0.681) SVM指標 vs PCA指標 相関: r = 0.046 PCA説明率: PC1=42.8%, PC2=17.6%
np.cumsum(arr) は累積和、np.linspace(a, b, n) は「aからbを等間隔でn個」。NumPyの定石です。原論文は市区町村レベルのサブ指標群を階層クラスター分析で要因分解し、要因ごとに束ねて多角的複合指標を提案しました(主成分分析でなく階層クラスターを選んだのは、サブ指標間の相関が低いことを確認したため)。SSDSE-A-2025 で同じフローを N=1,740 市区町村で再現します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | import numpy as np import pandas as pd from sklearn.preprocessing import StandardScaler from scipy.cluster.hierarchy import linkage, fcluster # SSDSE-A(市区町村データ、原論文と同じ粒度)で複合指標を作る a = pd.read_csv('data/raw/SSDSE-A-2025.csv', encoding='cp932', header=2) a = a[a['地域コード'].str.match(r'^R\d+', na=False)].copy() a = a[pd.to_numeric(a['総人口'], errors='coerce') > 0] num = lambda c: pd.to_numeric(a[c], errors='coerce') # サブ指標(原論文の「主観に頼らない変数選択 → 重み付け」に対応する素材) X = pd.DataFrame(index=(a['都道府県'] + a['市区町村']).values) X['社会増減率'] = ((num('転入者数(日本人移動者)') - num('転出者数(日本人移動者)')) / num('総人口') * 100).values X['自然増減率'] = ((num('出生数') - num('死亡数')) / num('総人口') * 100).values X['若年度'] = (num('15歳未満人口') / num('総人口') * 100).values X['財政力'] = (num('地方税(市町村財政)') / num('歳入決算総額(市町村財政)') * 100).values X['雇用集積'] = (num('従業者数(民営)') / num('総人口') * 100).values X['医療環境'] = (num('医師数') / num('総人口') * 10000).values X = X.dropna() print(f'市区町村数: {len(X)}') # 階層クラスター分析(Ward法)でサブ指標を「要因」に分解(原論文の中核工程) # → 変数側をクラスタリングして相関の強い指標群をまとめる Xz = pd.DataFrame(StandardScaler().fit_transform(X), columns=X.columns, index=X.index) corr_dist = 1 - Xz.corr().abs() # 変数間の距離 = 1 − |相関| Z = linkage(corr_dist.values[np.triu_indices(len(X.columns), 1)], method='ward') groups = fcluster(Z, t=3, criterion='maxclust') print('\n=== 変数の階層クラスタリング(要因分解) ===') for g in sorted(set(groups)): print(f' 要因{g}: ' + '、'.join(X.columns[groups == g])) # 要因ごとに平均して複合指標化 → 総合ランキング(原論文の複合指標の考え方) comp = pd.DataFrame(index=X.index) for g in sorted(set(groups)): comp[f'要因{g}'] = Xz.loc[:, X.columns[groups == g]].mean(axis=1) comp['総合指標'] = comp.mean(axis=1) print('\n=== 複合指標 総合ランキング ===') print('上位5:', '、'.join(comp['総合指標'].nlargest(5).index)) print('下位5:', '、'.join(comp['総合指標'].nsmallest(5).index)) |
fcluster(Z, t=3, criterion="maxclust") — デンドログラムを「3 クラスタになる高さ」で切ります。| データ | 出典 |
|---|---|
| SSDSE-B 都道府県データ | 統計センター SSDSE(社会・人口統計体系) |
| 地方創生関連指標 | 内閣府「まち・ひと・しごと創生」データベース |
本教育用コードは合成データを使用(np.random.seed(42))。実際の分析はSSDSE-Bの実データによる。
統計分析の解釈で初心者がやりがちな勘違いをまとめます。特に「相関と因果の混同」「p値の過信」は研究現場でもよく起きる落とし穴です。本文を読む前にも、読んだ後にも、目を通してみてください。
統計の基本用語を初心者向けに解説します。本文中で見慣れない言葉が出てきたら、ここに戻って確認してください。
統計手法について「何のためか」「結果をどう読むか」を初心者向けに解説します。
この研究をさらに発展させるための3つの方向性を示します。「今回わかったこと(X)」から「次に検証すべき仮説(Y)」を立て、「具体的に何をするか(Z)」まで考えてみましょう。
学んだだけでは身につきません。実際に手を動かすのが最強の学習方法です。本論文のスクリプトをベースに、以下のチャレンジに挑戦してみてください。難易度別に5つ用意しました。
本論文で学んだ手法は、研究の世界だけでなく、行政・企業・NPO の現場でも様々に活用されています。具体的なシーンを紹介します。
この論文を読んで初心者が抱きやすい疑問に、教育的観点から答えます。
この論文を理解できたか、クリックで確認しましょう。間違えても解説が出ます。
このページの分析は、この画面の中でそのまま実行できます。
Python をインストールする必要も、CSV をダウンロードする必要もありません。
下のセルの 「▶ ブラウザで実行」 を上から順に押すか、
「▶ 最初から全部実行」 で一気に流してください。
表示されるのは、本文の図表とまったく同じ計算の結果です
(動かしているのは再現スクリプト code/2024_U5_2_shorei.py そのもの)。
コードは書き換えられます。「✏️ 書き換える」で数字や変数を変えて ▶ を押すと、 結果がどう変わるかをその場で確かめられます(元に戻すボタンつき)。 ページを開いた時点で裏で実行環境の準備が始まっているので、待ち時間はほとんどありません。 初回だけ実行環境の取得にインターネット接続が必要です。