このページの分析を自分で再現するには、以下の手順でデータを準備してください。コードの編集は不要です。
data/raw/ フォルダに入れます。html/figures/ に自動保存されます。
建物火災は毎年多くの人命と財産を奪う重大な社会問題である。しかし「どの地域でなぜ火災が多く発生するのか」という決定要因は、単純な件数の比較だけでは把握しにくい。人口規模・気候・高齢化率・建物活動量など、様々な地域属性が複合的に絡み合っているためである。
まず「建物火災発生率予測モデルの構築および発生率の決定要因分析」を統計的にとらえることが有効だと考えられる。 その理由は感覚や経験則だけでは、複雑な社会要因の中で「何が本当に効いているか」を見極めにくいからである。 本研究では公開データと統計手法を組み合わせ、この問いに定量的な答えを出すことを目指す。
本研究はSSDSE-B(都道府県別データ)N=47を用いて、建物火災発生率の代理指標を構築し、OLS重回帰とRandomForestの2手法で決定要因を分析した。都市部と地方の違い、高齢化の影響、気候的要因などを統計的に検証することで、地域ごとのリスク構造の解明を試みた。
SSDSE-B 火災リスク分析 OLS重回帰 RandomForest 代理変数
「建物火災発生率」を直接測定するデータは都道府県別に取得しにくい場合がある。本研究では「着工建築物密度(人口1万人あたりの着工建築物数)」を建物活動率・火災リスクの代理変数として採用した。
| 変数名 | 算出式 | 想定される効果 |
|---|---|---|
| 消費支出 | 消費支出(二人以上世帯) / 1000(千円) | 都市部生活水準の代理。高いほど人口集積・建物活動が活発 |
| 高齢化率 | 65歳以上人口 / 総人口 × 100(%) | 高齢者は在宅時間が長く火災リスクが上がる可能性 |
| 年平均気温 | 年平均気温(℃)そのまま利用 | 寒冷地では暖房使用頻度が高く、火災リスクに影響する可能性 |
| 人口密度 | 総人口 / 都道府県面積(km²)(人/km²) | 都市化・密集度。高密度ほど建物数が多く着工建築物も多い |
| 病院密度 | 一般病院数 / 総人口 × 10,000(人口1万人対) | 緊急対応体制・医療資源の充実度の代理変数 |
直接測定が困難な概念を既存の統計データで近似するのが「代理変数(Proxy Variable)」の発想。重要なのは「なぜこれが代理になり得るか」という理論的根拠を示すこと。代理変数を使う場合には、その限界(測定誤差・構成概念妥当性)も明示すべきである。
1 2 3 4 5 6 7 | 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() # N=47 print("=" * 60) print(f"■ 都道府県データ 2022年度: N={len(df_2022)}") print("=" * 60) |
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ループ不要なのが強み。8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | # ── 都道府県面積(km²)── 国土地理院 全国都道府県市区町村別面積調(令和5年) area_km2 = { '北海道': 83424, '青森県': 9646, '岩手県': 15275, '宮城県': 7282, '秋田県': 11638, '山形県': 9323, '福島県': 13784, '茨城県': 6097, '栃木県': 6408, '群馬県': 6362, '埼玉県': 3798, '千葉県': 5158, '東京都': 2194, '神奈川県': 2416, '新潟県': 12584, '富山県': 4248, '石川県': 4186, '福井県': 4191, '山梨県': 4465, '長野県': 13562, '岐阜県': 10621, '静岡県': 7777, '愛知県': 5173, '三重県': 5774, '滋賀県': 4017, '京都府': 4612, '大阪府': 1905, '兵庫県': 8401, '奈良県': 3691, '和歌山県': 4725, '鳥取県': 3507, '島根県': 6708, '岡山県': 7114, '広島県': 8480, '山口県': 6114, '徳島県': 4147, '香川県': 1877, '愛媛県': 5676, '高知県': 7104, '福岡県': 4987, '佐賀県': 2441, '長崎県': 4130, '熊本県': 7409, '大分県': 6341, '宮崎県': 7735, '鹿児島県': 9187,'沖縄県': 2281, } df_2022 = df_2022.copy() df_2022['面積_km2'] = df_2022['都道府県'].map(area_km2) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。24 25 26 27 28 29 30 31 | # ── 特徴量エンジニアリング ───────────────────────────────────── pop = df_2022['総人口'].values.astype(float).clip(1) pop65 = df_2022['65歳以上人口'].values.astype(float) hosp = df_2022['一般病院数'].values.astype(float) kouji = df_2022['着工建築物数'].values.astype(float) temp = df_2022['年平均気温'].values.astype(float) spend = df_2022['消費支出(二人以上の世帯)'].values.astype(float) area = df_2022['面積_km2'].values.astype(float).clip(1) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。32 33 34 35 36 37 38 39 40 41 42 43 44 45 | # 目的変数: 着工建築物密度(建物火災リスク代理変数) y_vals = kouji / pop * 10000 # 人口1万人あたり着工建築物数 # 説明変数 aging_rate = pop65 / pop * 100 # 高齢化率(%) hospital_den = hosp / pop * 10000 # 一般病院密度(人口1万人対) pop_density = pop / area # 人口密度(人/km²) temp_vals = temp # 年平均気温(℃) spend_vals = spend / 1000 # 消費支出(千円) VAR_NAMES = ['消費支出', '高齢化率', '年平均気温', '人口密度', '病院密度'] pref_names = df_2022['都道府県'].values X_raw = np.column_stack([spend_vals, aging_rate, temp_vals, pop_density, hospital_den]) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。46 47 48 49 50 51 52 53 54 55 | # 欠損値チェック・除外 valid = np.all(np.isfinite(X_raw), axis=1) & np.isfinite(y_vals) X_raw = X_raw[valid] y_vals = y_vals[valid] pref_names = pref_names[valid] N = len(y_vals) print(f"有効都道府県数: N={N}") print(f"\n 目的変数(着工建築物密度): mean={y_vals.mean():.2f}, std={y_vals.std():.2f}") print(f" 範囲: [{y_vals.min():.2f}, {y_vals.max():.2f}]") |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。x if cond else y は三項演算子。リスト内包表記と組み合わせると、forとifを1行で書けます。56 57 58 59 60 61 62 | # ── 基本統計 ─────────────────────────────────────────────────── df_main = pd.DataFrame(X_raw, columns=VAR_NAMES) df_main['着工建築物密度'] = y_vals df_main['都道府県'] = pref_names print("\n 基本統計:") print(df_main[['着工建築物密度'] + VAR_NAMES].describe().round(2)) |
============================================================
■ 都道府県データ 2022年度: N=47
============================================================
有効都道府県数: N=47
目的変数(着工建築物密度): mean=47.34, std=8.84
範囲: [30.23, 80.32]
基本統計:
着工建築物密度 消費支出 高齢化率 年平均気温 人口密度 病院密度
count 47.00 47.00 47.00 47.00 47.00 47.00
mean 47.34 289.63 31.35 16.07 652.75 0.69
std 8.93 19.19 3.27 2.29 1221.27 0.28
min 30.23 245.05 22.81 10.20 61.61 0.32
25% 41.28 276.83 29.85 15.25 169.95 0.49
50% 47.92 287.78 31.42 16.40 261.74 0.60
75% 52.84 302.26 33.72 17.35 463.20 0.84
max 80.32 324.79 38.60 23.70 6398.36 1.60.describe() — 件数・平均・標準偏差・四分位・最大/最小を一括計算。データの素性チェックに必須。df[col](1列)と df[[col1, col2]](複数列)でカッコの数が違います。リストを渡していると覚えるとミスを減らせます。OLS回帰やRandomForestを適用する前に、目的変数と説明変数の間の相関、および説明変数同士の多重共線性の傾向を相関ヒートマップで可視化した。
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | all_col_names = ['着工建築物密度'] + VAR_NAMES df_corr = df_main[all_col_names].copy() corr_mat = df_corr.corr() fig1, ax1 = plt.subplots(figsize=(8, 6.5)) im = ax1.imshow(corr_mat.values, cmap='RdBu_r', vmin=-1, vmax=1, aspect='auto') plt.colorbar(im, ax=ax1, shrink=0.8, label='ピアソン相関係数') ax1.set_xticks(range(len(all_col_names))) ax1.set_yticks(range(len(all_col_names))) ax1.set_xticklabels(all_col_names, rotation=35, ha='right', fontsize=10) ax1.set_yticklabels(all_col_names, fontsize=10) for i in range(len(all_col_names)): for j in range(len(all_col_names)): val = corr_mat.values[i, j] color = 'white' if abs(val) > 0.5 else 'black' ax1.text(j, i, f'{val:.2f}', ha='center', va='center', fontsize=9.5, color=color, fontweight='bold') ax1.set_title('相関ヒートマップ(主要変数・2022年度 都道府県別 N=47)', fontsize=12, fontweight='bold', pad=14) plt.tight_layout() fig1.savefig(os.path.join(FIG_DIR, '2022_U5_3_fig1_corr.png'), bbox_inches='tight', dpi=150) plt.close(fig1) print("\n図1保存: 2022_U5_3_fig1_corr.png") |
図1保存: 2022_U5_3_fig1_corr.png
fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。RandomForestRegressor(500本の決定木, max_features='sqrt', random_state=42)を学習し、MDI(Mean Decrease Impurity:不純度の平均減少量)に基づく変数重要度を算出した。
RandomForestは多数の決定木をアンサンブルした機械学習モデル。各木はブートストラップサンプルで学習し、特徴量のランダムサブセットから分岐を選ぶ。MDI(不純度の平均減少量)は各変数が全ての木でどれだけ不純度を下げたかを集計した指標。
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | sorted_names = [VAR_NAMES[i] for i in order_idx] sorted_imp = importances[order_idx] colors_rf = ['#C62828' if imp >= sorted_imp[0] * 0.7 else '#FF8F00' if imp >= sorted_imp[0] * 0.4 else '#1565C0' for imp in sorted_imp] fig2, ax2 = plt.subplots(figsize=(8, 5)) bars = ax2.barh(range(len(sorted_names)), sorted_imp[::-1], color=colors_rf[::-1], alpha=0.80, edgecolor='white', height=0.6) ax2.set_yticks(range(len(sorted_names))) ax2.set_yticklabels(sorted_names[::-1], fontsize=11) ax2.set_xlabel('変数重要度(MDI: 不純度の平均減少量)', fontsize=11) ax2.set_title('RandomForest 変数重要度\n(建物火災リスク代理変数 = 着工建築物密度)', fontsize=12, fontweight='bold') ax2.axvline(0, color='gray', linewidth=0.8) ax2.grid(axis='x', alpha=0.3) for i, (imp, idx) in enumerate(zip(sorted_imp[::-1], order_idx[::-1])): ax2.text(imp + 0.003, i, f'{imp:.4f}', va='center', fontsize=9) plt.tight_layout() fig2.savefig(os.path.join(FIG_DIR, '2022_U5_3_fig2_importance.png'), bbox_inches='tight', dpi=150) plt.close(fig2) print("図2保存: 2022_U5_3_fig2_importance.png") |
図2保存: 2022_U5_3_fig2_importance.png
fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。x if cond else y は三項演算子。リスト内包表記と組み合わせると、forとifを1行で書けます。全変数を標準化(StandardScaler)した上でOLS重回帰を実行し、標準化係数(ベータ係数)を算出した。標準化係数は各変数が「同じ尺度」で比較できるため、効果量の大小を直接比較できる。
| 変数 | 標準化係数 | 方向 | 有意性 | 解釈 |
|---|---|---|---|---|
| 消費支出 | 正(大) | + | 有意 | 消費水準が高いほど建物活動が活発 |
| 人口密度 | 正 | + | 有意 | 都市密集地ほど着工建築物が多い |
| 高齢化率 | 負 | − | 有意 | 高齢化が進むほど新築・建物活動が低下 |
| 年平均気温 | 正 or 負 | ± | n.s. | 気候の単独効果は限定的 |
| 病院密度 | 負 | − | 有意 | 医療資源の充実した地域は農村的・高齢化傾向 |
OLSは係数の解釈が明確で統計的有意性(p値)の算出ができる「説明」向きのモデル。RandomForestは非線形関係・交互作用も自動的に捉え予測精度が高い「予測」向きのモデル。変数重要度の比較を通じて、両者の一致・不一致を確認することで分析の信頼性が高まる。
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 146 147 148 149 150 151 152 153 154 155 156 157 | sig_colors = [] for p in std_pvals: if p < 0.01: sig_colors.append('#C62828') elif p < 0.05: sig_colors.append('#FF8F00') else: sig_colors.append('#9E9E9E') fig3, ax3 = plt.subplots(figsize=(8, 5)) y_pos3 = np.arange(len(VAR_NAMES)) ax3.barh(y_pos3, std_coefs, color=sig_colors, alpha=0.78, edgecolor='white', height=0.6) ax3.errorbar(std_coefs, y_pos3, xerr=1.96 * std_ses, fmt='none', color='#333', capsize=4, linewidth=1.2) ax3.axvline(0, color='gray', linestyle='--', linewidth=1.0) ax3.set_yticks(y_pos3) ax3.set_yticklabels(VAR_NAMES, fontsize=11) ax3.set_xlabel('標準化回帰係数(±1.96 SE)', fontsize=11) ax3.set_title( f'OLS重回帰 標準化係数(N=47都道府県)\n' f'R²={model_ols.rsquared:.3f}, adj.R²={model_ols.rsquared_adj:.3f}', fontsize=12, fontweight='bold') ax3.invert_yaxis() ax3.grid(axis='x', alpha=0.3) from matplotlib.patches import Patch legend_items = [ Patch(color='#C62828', alpha=0.78, label='p<0.01'), Patch(color='#FF8F00', alpha=0.78, label='p<0.05'), Patch(color='#9E9E9E', alpha=0.78, label='n.s.'), ] ax3.legend(handles=legend_items, fontsize=9, loc='lower right') for i, (b, p) in enumerate(zip(std_coefs, std_pvals)): sig = '***' if p < 0.001 else '**' if p < 0.01 else '*' if p < 0.05 else '' offset = 0.02 if b >= 0 else -0.02 ha = 'left' if b >= 0 else 'right' ax3.text(b + offset, i, f'{b:.3f}{sig}', va='center', ha=ha, fontsize=8.5) plt.tight_layout() fig3.savefig(os.path.join(FIG_DIR, '2022_U5_3_fig3_coef.png'), bbox_inches='tight', dpi=150) plt.close(fig3) print("図3保存: 2022_U5_3_fig3_coef.png") |
図3保存: 2022_U5_3_fig3_coef.png
import pandas as pd など — 必要なライブラリをまとめて呼び出します。as pd は短い別名(alias)。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。df[col](1列)と df[[col1, col2]](複数列)でカッコの数が違います。リストを渡していると覚えるとミスを減らせます。OLS・RF両方で重要な変数として示された「病院密度」と「着工建築物密度」の関係を都道府県ラベル付きの散布図で可視化した。点の色は高齢化率を表し、地域の属性との関係を同時に確認できる。
地域比較では「件数」をそのまま使うと人口規模の差が支配的になる。「密度(÷人口 or ÷面積)」に変換することで地域間の公平な比較が可能になる。また第3の変数(ここでは高齢化率)を点の色で表現する「バブル散布図的アプローチ」は、2変数の関係の背後にある構造を視覚的に把握する有効な手法。
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | import numpy as np import pandas as pd import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt import statsmodels.api as sm from sklearn.ensemble import RandomForestRegressor from sklearn.preprocessing import StandardScaler from scipy import stats import warnings warnings.filterwarnings('ignore') plt.rcParams['font.family'] = 'Hiragino Sans' plt.rcParams['axes.unicode_minus'] = False plt.rcParams['figure.dpi'] = 150 import os FIG_DIR = 'html/figures' DATA_B = 'data/raw/SSDSE-B-2026.csv' os.makedirs(FIG_DIR, exist_ok=True) |
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」に標準化。単位が違う変数のβを比較可能に。f"...{x}..." はf-string。文字列の中に {変数} と書くだけで埋め込めて、{x:.2f} のように書式も指定できます。179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | print("\n" + "=" * 60) print("■ Step1. OLS重回帰分析(標準化係数)") print("=" * 60) scaler = StandardScaler() X_scaled = scaler.fit_transform(X_raw) y_scaled = (y_vals - y_vals.mean()) / y_vals.std() model_ols = sm.OLS(y_scaled, sm.add_constant(X_scaled)).fit() print(model_ols.summary2()) std_coefs = model_ols.params[1:] # 定数項を除く std_ses = model_ols.bse[1:] std_pvals = model_ols.pvalues[1:] print(f"\n R² = {model_ols.rsquared:.4f}, adj.R² = {model_ols.rsquared_adj:.4f}") print(f"\n {'変数':<14} {'標準化係数':>10} {'SE':>8} {'p値':>10} {'有意':>6}") print(" " + "-" * 52) for name, b, se, p in zip(VAR_NAMES, std_coefs, std_ses, std_pvals): sig = '***' if p < 0.001 else '**' if p < 0.01 else '*' if p < 0.05 else 'n.s.' print(f" {name:<14} {b:>10.4f} {se:>8.4f} {p:>10.4f} {sig:>6}") |
============================================================
■ Step1. OLS重回帰分析(標準化係数)
============================================================
Results: Ordinary least squares
=================================================================
Model: OLS Adj. R-squared: 0.264
Dependent Variable: y AIC: 125.5336
Date: 2026-05-18 11:24 BIC: 136.6345
No. Observations: 47 Log-Likelihood: -56.767
Df Model: 5 F-statistic: 4.308
Df Residuals: 41 Prob (F-statistic): 0.00305
R-squared: 0.344 Scale: 0.75149
-------------------------------------------------------------------
Coef. Std.Err. t P>|t| [0.025 0.975]
-------------------------------------------------------------------
const -0.0000 0.1264 -0.0000 1.0000 -0.2554 0.2554
x1 0.2774 0.1411 1.9665 0.0560 -0.0075 0.5622
x2 0.0308 0.2386 0.1289 0.8980 -0.4510 0.5125
x3 -0.1490 0.1720 -0.8661 0.3915 -0.4963 0.1984
x4 -0.5105 0.1674 -3.0507 0.0040 -0.8485 -0.1726
x5 -0.1148 0.1844 -0.6226 0.5370 -0.4872 0.2576
-----------------------------------------------------------------
Omnibus: 14.134 Durbin-Watson: 1.743
Prob(Omnibus): 0.001 Jarque-Bera (JB): 27.819
Skew: 0.735 Prob(JB): 0.000
Kurtosis: 6.471 Condition No.: 4
=================================================================
Notes:
[1] Standard Errors assume that the covariance matrix of the
errors is correctly specified.
R² = 0.3444, adj.R² = 0.2645
変数 標準化係数 SE p値 有意
----------------------------------------------------
消費支出 0.2774 0.1411 0.0560 n.s.
高齢化率 0.0308 0.2386 0.8980 n.s.
年平均気温 -0.1490 0.1720 0.3915 n.s.
人口密度 -0.5105 0.1674 0.0040 **
病院密度 -0.1148 0.1844 0.5370 n.s.StandardScaler().fit_transform(X) — 各列を「平均0・分散1」に標準化。単位が違う変数のβを比較可能に。sm.add_constant(X) — 切片項(定数1の列)を先頭に追加。statsmodelsで必須。sm.OLS(y, X).fit() — 最小二乗法でモデルを推定。model.params, model.pvalues, model.conf_int() で結果取得。.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 | print("\n" + "=" * 60) print("■ Step2. RandomForest 変数重要度(MDI)") print("=" * 60) rf = RandomForestRegressor(n_estimators=500, max_features='sqrt', random_state=42, n_jobs=-1) rf.fit(X_raw, y_vals) importances = rf.feature_importances_ order_idx = np.argsort(importances)[::-1] print(f"\n OOB R²(参考): RandomForestは交差検証が推奨") print(f"\n {'順位':>4} {'変数':<14} {'重要度':>10}") print(" " + "-" * 32) for rank, idx in enumerate(order_idx, 1): print(f" {rank:>4} {VAR_NAMES[idx]:<14} {importances[idx]:>10.4f}") |
============================================================
■ Step2. RandomForest 変数重要度(MDI)
============================================================
OOB R²(参考): RandomForestは交差検証が推奨
順位 変数 重要度
--------------------------------
1 年平均気温 0.2547
2 人口密度 0.2187
3 高齢化率 0.1862
4 消費支出 0.1742
5 病院密度 0.1662[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。215 216 217 218 219 220 221 222 223 224 225 226 227 | x_hosp = df_main['病院密度'].values x_kouji = df_main['着工建築物密度'].values # 相関 r_sc, p_sc = stats.pearsonr(x_hosp, x_kouji) fig4, ax4 = plt.subplots(figsize=(11, 8)) sc = ax4.scatter(x_hosp, x_kouji, c=df_main['高齢化率'].values, cmap='RdYlBu_r', s=60, alpha=0.75, zorder=3, edgecolors='gray', linewidths=0.5) cbar4 = plt.colorbar(sc, ax=ax4, shrink=0.75, pad=0.02) cbar4.set_label('高齢化率(%)', fontsize=10) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。stats.pearsonr(x, y) — Pearson相関係数 r と p値を同時に返します。s[:-n]「末尾n文字を除く」/s[n:]「先頭n文字を除く」。スライス [start:stop:step] はリスト・タプル・文字列共通の基本ワザです。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 | # 回帰直線 z4 = np.polyfit(x_hosp, x_kouji, 1) xs4 = np.linspace(x_hosp.min(), x_hosp.max(), 200) ax4.plot(xs4, np.poly1d(z4)(xs4), 'r--', linewidth=1.5, alpha=0.7, label=f'回帰直線 (r={r_sc:.3f}, p={p_sc:.3f})') # 都道府県ラベル for i, pref in enumerate(pref_names): label_short = pref.replace('都', '').replace('道', '').replace('府', '').replace('県', '') ax4.annotate(label_short, (x_hosp[i], x_kouji[i]), textcoords='offset points', xytext=(3, 3), fontsize=7.5, alpha=0.85) ax4.set_xlabel('一般病院密度(人口1万人あたり)', fontsize=12) ax4.set_ylabel('着工建築物密度(人口1万人あたり)', fontsize=12) ax4.set_title('着工建築物密度 vs 一般病院密度\n(都道府県別 2022年度, 色=高齢化率)', fontsize=13, fontweight='bold') ax4.legend(fontsize=10) ax4.grid(True, alpha=0.25) plt.tight_layout() fig4.savefig(os.path.join(FIG_DIR, '2022_U5_3_fig4_scatter.png'), bbox_inches='tight', dpi=150) plt.close(fig4) print("図4保存: 2022_U5_3_fig4_scatter.png") print("\n" + "=" * 60) print("全図の生成完了(4枚)") print(f" 2022_U5_3_fig1_corr.png : 相関ヒートマップ") print(f" 2022_U5_3_fig2_importance.png: RF変数重要度棒グラフ") print(f" 2022_U5_3_fig3_coef.png : OLS標準化係数プロット") print(f" 2022_U5_3_fig4_scatter.png : 着工建築物密度 vs 病院密度 散布図") print("=" * 60) |
図4保存: 2022_U5_3_fig4_scatter.png ============================================================ 全図の生成完了(4枚) 2022_U5_3_fig1_corr.png : 相関ヒートマップ 2022_U5_3_fig2_importance.png: RF変数重要度棒グラフ 2022_U5_3_fig3_coef.png : OLS標準化係数プロット 2022_U5_3_fig4_scatter.png : 着工建築物密度 vs 病院密度 散布図 ============================================================
np.cumsum(arr) は累積和、np.linspace(a, b, n) は「aからbを等間隔でn個」。NumPyの定石です。SSDSE-B(都道府県別)2022年度データ(N=47)を用いたOLS重回帰・RandomForest分析の結果:
| データ | 出典 |
|---|---|
| SSDSE-B-2026.csv(都道府県別データ) | 統計数理研究所 SSDSE(社会・人口統計体系)― 実データ使用 |
| 都道府県別面積データ | 国土地理院 全国都道府県市区町村別面積調(令和5年) |
| 着工建築物数・消費支出・病院数・気温 | SSDSE-B 2022年度 都道府県データより抽出 |
本スクリプトはSSDSE-B実データ(N=47都道府県)を使用。合成データは一切使用していない。
統計分析の解釈で初心者がやりがちな勘違いをまとめます。特に「相関と因果の混同」「p値の過信」は研究現場でもよく起きる落とし穴です。本文を読む前にも、読んだ後にも、目を通してみてください。
統計の基本用語を初心者向けに解説します。本文中で見慣れない言葉が出てきたら、ここに戻って確認してください。
統計手法について「何のためか」「結果をどう読むか」を初心者向けに解説します。
この研究をさらに発展させるための3つの方向性を示します。「今回わかったこと(X)」から「次に検証すべき仮説(Y)」を立て、「具体的に何をするか(Z)」まで考えてみましょう。
学んだだけでは身につきません。実際に手を動かすのが最強の学習方法です。本論文のスクリプトをベースに、以下のチャレンジに挑戦してみてください。難易度別に5つ用意しました。
本論文で学んだ手法は、研究の世界だけでなく、行政・企業・NPO の現場でも様々に活用されています。具体的なシーンを紹介します。
この論文を読んで初心者が抱きやすい疑問に、教育的観点から答えます。