このページの分析を自分で再現するには、以下の手順でデータを準備してください。コードの編集は不要です。
data/raw/ フォルダに入れます。html/figures/ に自動保存されます。
気温・降水量といった気候要因が都道府県の死亡率にどのような影響を与えるか—— この問いは、地球温暖化が進む現代において公衆衛生・健康政策の観点から重要性を増している。 特に近年の猛暑や熱中症による死亡増加は、気温と健康の関係を社会的な関心事として押し上げている。
まず「気候と健康:気温・降水量が死亡率に与える影響の分析」を統計的にとらえることが有効だと考えられる。 その理由は感覚や経験則だけでは、複雑な社会要因の中で「何が本当に効いているか」を見極めにくいからである。 本研究では公開データと統計手法を組み合わせ、この問いに定量的な答えを出すことを目指す。
本研究では、都道府県レベルの公的統計(SSDSE-B)を用い、 相関分析・OLS重回帰・時系列分析・箱ひげ図を組み合わせて、 気象要因が死亡率に与える影響を定量的に検討する。 特に「高齢化率という交絡変数を制御した上で、気象要因の純効果を評価する」 点が本分析の方法論的な核心である。
SSDSE-B 相関分析 OLS重回帰 時系列分析 箱ひげ図
統計数理研究所・統計センターが公表する SSDSE-B(社会・人口統計体系 都道府県データ)2026年版を使用。 対象年度は2012〜2023年、対象地域は47都道府県(全国)。
| 変数名 | SSDSEコード | 定義・計算式 | 単位 |
|---|---|---|---|
| 粗死亡率(目的変数) | A4200 / A1101 | 死亡数 / 総人口 × 1000 | 人口千人あたり |
| 年平均気温 | B4101 | (直接利用) | ℃ |
| 最高気温 | B4102 | 日最高気温の月平均の最高値 | ℃ |
| 降水日数(年間) | B4106 | (直接利用) | 日 |
| 高齢化率 | A1303 / A1101 | 65歳以上人口 / 総人口 × 100 | % |
| 保健医療費割合 | L322106 / L3221 | 保健医療費 / 消費支出(二人以上の世帯)× 100 | % |
データ分析において、指標の「定義」を正確に理解することは出発点である。 粗死亡率は計算が簡単だが、年齢構成の違いを反映してしまう。 この「交絡」を制御する方法として、統計学には2つのアプローチがある。
方法1:年齢調整死亡率(直接法)—— 基準人口(例:2015年の全国人口)を用いて、各地域の年齢別死亡率から 仮想的な死亡率を計算。地域間の純粋な健康リスクを比較できる。
方法2:重回帰で高齢化率を統制(本分析)—— 粗死亡率を目的変数とし、高齢化率を説明変数に加えることで、 「高齢化率が同じ水準なら」という条件下の偏回帰係数を推定できる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | df_raw = pd.read_csv(DATA_B, encoding='shift-jis', header=0) # 行0がラベル行、行1以降が実データ df_all = df_raw.iloc[1:].copy() df_all.columns = df_raw.columns # 数値列に変換 NUM_COLS = ['A1101', 'A1303', 'A4200', 'B4101', 'B4102', 'B4106', 'L3221', 'L322106'] for c in NUM_COLS: df_all[c] = pd.to_numeric(df_all[c], errors='coerce') df_all['年度'] = df_all['SSDSE-B-2026'].astype(int) df_all['都道府県'] = df_all['Prefecture'].astype(str) print("=" * 65) print("■ SSDSE-B-2026 読み込み完了") print(f" 年度範囲: {df_all['年度'].min()}〜{df_all['年度'].max()}") print(f" 都道府県数(各年): {df_all.groupby('年度')['都道府県'].count().iloc[0]}件") print("=" * 65) |
================================================================= ■ SSDSE-B-2026 読み込み完了 年度範囲: 2012〜2023 都道府県数(各年): 47件 =================================================================
pd.read_csv(...) でCSVを読み込みます。encoding='cp932' は日本語Windows由来の文字コード、header=1 は「2行目を列名として使う」。.astype(int) — 列を整数に変換(年度などを数値比較するため)。df.groupby('列').apply(関数) — グループごとに関数を適用。時系列や地域別の集計でよく使います。df['A'] / df['B'] — pandasの列同士の四則演算は要素ごと(element-wise)。forループ不要なのが強み。19 20 21 22 23 24 25 26 27 28 29 30 | df_all['粗死亡率'] = df_all['A4200'] / df_all['A1101'] * 1000 df_all['高齢化率'] = df_all['A1303'] / df_all['A1101'] * 100 df_all['保健医療費割合'] = df_all['L322106'] / df_all['L3221'] * 100 # 2022年データ抽出 df_2022 = df_all[df_all['年度'] == 2022].copy().reset_index(drop=True) print(f"\n■ 2022年データ: {len(df_2022)}都道府県") print(f" 粗死亡率(平均): {df_2022['粗死亡率'].mean():.2f} ‰") print(f" 高齢化率(平均): {df_2022['高齢化率'].mean():.1f}%") print(f" 年平均気温(平均): {df_2022['B4101'].mean():.1f}℃") print(f" 降水日数(平均): {df_2022['B4106'].mean():.1f}日") |
■ 2022年データ: 47都道府県 粗死亡率(平均): 13.85 ‰ 高齢化率(平均): 31.4% 年平均気温(平均): 16.1℃ 降水日数(平均): 111.4日
.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。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 | REGION_MAP = { '北海道': '北海道・東北', '青森県': '北海道・東北', '岩手県': '北海道・東北', '宮城県': '北海道・東北', '秋田県': '北海道・東北', '山形県': '北海道・東北', '福島県': '北海道・東北', '茨城県': '関東', '栃木県': '関東', '群馬県': '関東', '埼玉県': '関東', '千葉県': '関東', '東京都': '関東', '神奈川県': '関東', '新潟県': '中部', '富山県': '中部', '石川県': '中部', '福井県': '中部', '山梨県': '中部', '長野県': '中部', '岐阜県': '中部', '静岡県': '中部', '愛知県': '中部', '三重県': '近畿', '滋賀県': '近畿', '京都府': '近畿', '大阪府': '近畿', '兵庫県': '近畿', '奈良県': '近畿', '和歌山県': '近畿', '鳥取県': '中国・四国', '島根県': '中国・四国', '岡山県': '中国・四国', '広島県': '中国・四国', '山口県': '中国・四国', '徳島県': '中国・四国', '香川県': '中国・四国', '愛媛県': '中国・四国', '高知県': '中国・四国', '福岡県': '九州・沖縄', '佐賀県': '九州・沖縄', '長崎県': '九州・沖縄', '熊本県': '九州・沖縄', '大分県': '九州・沖縄', '宮崎県': '九州・沖縄', '鹿児島県': '九州・沖縄', '沖縄県': '九州・沖縄', } REGION_ORDER = ['北海道・東北', '関東', '中部', '近畿', '中国・四国', '九州・沖縄'] REGION_COLORS = { '北海道・東北': '#1565C0', '関東': '#E65100', '中部': '#2E7D32', '近畿': '#6A1B9A', '中国・四国': '#795548', '九州・沖縄': '#00695C', } df_all['地方'] = df_all['都道府県'].map(REGION_MAP) df_2022['地方'] = df_2022['都道府県'].map(REGION_MAP) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。まず2012〜2023年にかけての粗死亡率の推移を地方別に確認する。 全地方で一貫した上昇傾向が見られるが、これは主に高齢化の進展を反映している。 地方によって死亡率の水準と上昇速度が異なることも観察される。
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | print("\n" + "=" * 65) print("■ 図1: 死亡率の時系列推移(地域別平均)") print("=" * 65) df_region_ts = ( df_all.groupby(['年度', '地方'])['粗死亡率'] .mean() .reset_index() ) fig1, ax1 = plt.subplots(figsize=(11, 6)) for region in REGION_ORDER: sub = df_region_ts[df_region_ts['地方'] == region].sort_values('年度') if len(sub) == 0: continue ax1.plot(sub['年度'], sub['粗死亡率'], marker='o', linewidth=2, markersize=5, color=REGION_COLORS[region], label=region) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。df.groupby('列').apply(関数) — グループごとに関数を適用。時系列や地域別の集計でよく使います。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。sort_values('列名', ascending=False) — 指定列で並べ替え(降順)。x if cond else y は三項演算子。リスト内包表記と組み合わせると、forとifを1行で書けます。79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | # 2022年に垂直線(分析基準年) ax1.axvline(2022, color='#C62828', linestyle='--', linewidth=1.5, alpha=0.8) ax1.text(2022.05, ax1.get_ylim()[1] * 0.98, '2022年\n(分析基準年)', ha='left', va='top', fontsize=9, color='#C62828', fontweight='bold') ax1.set_xlabel('年度', fontsize=12) ax1.set_ylabel('粗死亡率(人口千人あたり)', fontsize=12) ax1.set_title('粗死亡率の時系列推移(地方別平均, 2012〜2023年)\n〜高齢化の進展に伴い全地域で上昇傾向〜', fontsize=13, fontweight='bold') ax1.set_xticks(sorted(df_all['年度'].unique())) ax1.xaxis.set_tick_params(rotation=45) ax1.legend(loc='upper left', fontsize=9.5, framealpha=0.88) ax1.grid(True, alpha=0.3) plt.tight_layout() fig1.savefig(os.path.join(FIG_DIR, '2022_H5_12_fig1_timeseries.png'), bbox_inches='tight', dpi=150) plt.close(fig1) print("図1保存: 2022_H5_12_fig1_timeseries.png") |
================================================================= ■ 図1: 死亡率の時系列推移(地域別平均) ================================================================= 図1保存: 2022_H5_12_fig1_timeseries.png
ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。df[col](1列)と df[[col1, col2]](複数列)でカッコの数が違います。リストを渡していると覚えるとミスを減らせます。2022年の47都道府県データを用いて、高齢化率と粗死亡率の散布図を描く。 点の色は年平均気温を表しており、気温の地理的分布と死亡率の関係も同時に確認できる。
| 相関の組み合わせ | 相関係数 r | p値 | 解釈 |
|---|---|---|---|
| 高齢化率 ↔ 粗死亡率 | 0.972 | < 0.001 | 非常に強い正の相関(***) |
| 年平均気温 ↔ 粗死亡率 | -0.381 | 0.008 | 中程度の負の相関(**) |
| 降水日数 ↔ 粗死亡率 | -0.19 | ~0.20 | 弱い相関(n.s.) |
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | print("\n■ 図2: 高齢化率 vs 死亡率 散布図(2022年)") fig2, ax2 = plt.subplots(figsize=(11, 7)) x2 = df_2022['高齢化率'].values y2 = df_2022['粗死亡率'].values temp2 = df_2022['B4101'].values pref2 = df_2022['都道府県'].values sc2 = ax2.scatter(x2, y2, c=temp2, cmap='RdYlBu_r', s=75, alpha=0.85, edgecolors='#333', linewidth=0.5, zorder=3, vmin=10, vmax=24) cbar2 = fig2.colorbar(sc2, ax=ax2, label='年平均気温(℃)', shrink=0.8) cbar2.ax.tick_params(labelsize=9) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。df[col](1列)と df[[col1, col2]](複数列)でカッコの数が違います。リストを渡していると覚えるとミスを減らせます。113 114 115 116 117 118 | # 回帰直線 valid2 = np.isfinite(x2) & np.isfinite(y2) z2 = np.polyfit(x2[valid2], y2[valid2], 1) xs2 = np.linspace(x2[valid2].min(), x2[valid2].max(), 100) ax2.plot(xs2, np.poly1d(z2)(xs2), 'b--', linewidth=1.8, alpha=0.7, label=f'回帰直線 (r={r_aging:.3f}, p={p_aging:.4f})') |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。s[:-n]「末尾n文字を除く」/s[n:]「先頭n文字を除く」。スライス [start:stop:step] はリスト・タプル・文字列共通の基本ワザです。119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | # 都道府県ラベル(注目都市) LABEL_PREFS = { '東京都', '北海道', '沖縄県', '秋田県', '山形県', '青森県', '高知県', '島根県', '愛知県', '神奈川県', '埼玉県', '鹿児島県', '長崎県', '和歌山県', '奈良県', } for xi, yi, pref in zip(x2, y2, pref2): if pref in LABEL_PREFS: short = pref.replace('県', '').replace('都', '').replace('道', '').replace('府', '') ax2.annotate(short, (xi, yi), xytext=(4, 3), textcoords='offset points', fontsize=7.5, color='#1A237E', fontweight='bold') ax2.set_xlabel('高齢化率(65歳以上人口割合 %)', fontsize=12) ax2.set_ylabel('粗死亡率(人口千人あたり)', fontsize=12) ax2.set_title('高齢化率と粗死亡率の関係(2022年, 都道府県別)\n〜色:年平均気温。高齢化率が粗死亡率を強く規定する〜', fontsize=13, fontweight='bold') ax2.legend(fontsize=9.5) ax2.grid(True, alpha=0.3) plt.tight_layout() fig2.savefig(os.path.join(FIG_DIR, '2022_H5_12_fig2_scatter.png'), bbox_inches='tight', dpi=150) plt.close(fig2) print("図2保存: 2022_H5_12_fig2_scatter.png") |
■ 図2: 高齢化率 vs 死亡率 散布図(2022年) 図2保存: 2022_H5_12_fig2_scatter.png
np.cumsum(arr) は累積和、np.linspace(a, b, n) は「aからbを等間隔でn個」。NumPyの定石です。高齢化率・年平均気温・最高気温・降水日数・保健医療費割合を説明変数とした OLS(最小二乗法)重回帰を実施。各変数を標準化して係数を比較可能にする。 これにより「高齢化率が同一水準の都道府県間で」気候変数が死亡率にどう影響するかを推定できる。
| 変数 | 標準化係数(β) | p値 | 有意性 | 解釈 |
|---|---|---|---|---|
| 高齢化率(%) | +1.831 | < 0.001 | *** | 最も強い正の効果。交絡変数として必須 |
| 年平均気温(℃) | +0.165 | 0.122 | n.s. | 高齢化制御後は有意でない |
| 最高気温(℃) | -0.153 | 0.161 | n.s. | 高齢化制御後は有意でない |
| 降水日数(日) | +0.091 | 0.226 | n.s. | 有意な関連なし |
| 保健医療費割合(%) | -0.123 | 0.103 | n.s. | p≒0.10(限界的) |
モデル全体: R² = 0.958, 自由度修正済 R² = 0.953, F(5,41) = 187.4, p < 0.001
「A と B に相関がある」という事実は、「A が B の原因である」ことを意味しない。 第三の変数 C(交絡変数)が A にも B にも影響を与えている場合、 A と B の見かけ上の相関(擬似相関)が生じることがある。
本分析では: 気温(低い) → 高齢化が進みやすい地域(東北・北陸)→ 粗死亡率が高い という交絡の可能性がある。重回帰で高齢化率を統制することで、 この交絡を除去し、気候の「純粋な」効果を推定できる。
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 170 171 172 173 174 175 176 177 178 | print("\n■ 図3: OLS回帰係数プロット") coefs3 = np.asarray(ols_model.params[1:]) ses3 = np.asarray(ols_model.bse[1:]) pvals3 = np.asarray(ols_model.pvalues[1:]) COEF_COLORS = [] for p in pvals3: if p < 0.01: COEF_COLORS.append('#C62828') elif p < 0.05: COEF_COLORS.append('#FF8F00') elif p < 0.10: COEF_COLORS.append('#FDD835') else: COEF_COLORS.append('#9E9E9E') fig3, ax3 = plt.subplots(figsize=(9, 5.5)) y_pos3 = np.arange(len(REG_LABELS)) ax3.barh(y_pos3, coefs3, color=COEF_COLORS, alpha=0.82, edgecolor='white', height=0.55) ax3.errorbar(coefs3, y_pos3, xerr=1.96 * ses3, fmt='none', color='#222', capsize=5, linewidth=1.5) ax3.axvline(0, color='gray', linestyle='--', linewidth=1.0) ax3.set_yticks(y_pos3) ax3.set_yticklabels(REG_LABELS, fontsize=11) ax3.set_xlabel('標準化回帰係数(±1.96SE)', fontsize=11) ax3.set_title( f'粗死亡率の決定要因 — OLS重回帰係数(2022年, N={len(df_reg)}都道府県)\n' f'R²={ols_model.rsquared:.3f}(adj. R²={ols_model.rsquared_adj:.3f})', fontsize=12, fontweight='bold' ) ax3.invert_yaxis() ax3.grid(axis='x', alpha=0.3) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。s[:-n]「末尾n文字を除く」/s[n:]「先頭n文字を除く」。スライス [start:stop:step] はリスト・タプル・文字列共通の基本ワザです。179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | # p値ラベル for i, (c, p) in enumerate(zip(coefs3, pvals3)): sig = '***' if p < 0.001 else '**' if p < 0.01 else '*' if p < 0.05 else 'n.s.' offset = 0.015 ha = 'left' if c >= 0 else 'right' ax3.text(c + (offset if c >= 0 else -offset), i, f' {c:.3f} {sig}', va='center', ha=ha, fontsize=8.5) from matplotlib.patches import Patch ax3.legend(handles=[ Patch(color='#C62828', alpha=0.85, label='p<0.01'), Patch(color='#FF8F00', alpha=0.85, label='p<0.05'), Patch(color='#FDD835', alpha=0.85, label='p<0.10'), Patch(color='#9E9E9E', alpha=0.85, label='n.s.'), ], fontsize=9, loc='lower right') plt.tight_layout() fig3.savefig(os.path.join(FIG_DIR, '2022_H5_12_fig3_coef.png'), bbox_inches='tight', dpi=150) plt.close(fig3) print("図3保存: 2022_H5_12_fig3_coef.png") |
■ 図3: OLS回帰係数プロット 図3保存: 2022_H5_12_fig3_coef.png
import pandas as pd など — 必要なライブラリをまとめて呼び出します。as pd は短い別名(alias)。np.cumsum(arr) は累積和、np.linspace(a, b, n) は「aからbを等間隔でn個」。NumPyの定石です。47都道府県を年平均気温の三分位点(三分位数)で3グループ(低温帯・中温帯・高温帯)に分類し、 グループ間で粗死亡率の分布を箱ひげ図で比較する。 一元配置分散分析(ANOVA)で群間差の有意性を検定する。
| 気温帯 | N | 年平均気温(平均) | 粗死亡率(平均) | 粗死亡率(中央値) | 高齢化率(平均) |
|---|---|---|---|---|---|
| 低温帯(寒冷地) | 18 | 14.0℃ | 14.45‰ | 14.30‰ | 32.2% |
| 中温帯(温暖地) | 13 | 16.5℃ | 12.63‰ | 12.66‰ | 29.5% |
| 高温帯(温暖地) | 16 | 18.1℃ | 14.17‰ | 14.60‰ | 31.8% |
一元配置ANOVA: F(2,44)=4.22, p=0.021 (群間差は有意)
連続変数(気温)をグループ変数に変換する手法として、
pd.qcut() と
pd.cut() がある。
両者の違いを理解することが重要。
注意:グループ化は情報の損失を伴う。 連続変数として扱えるなら回帰分析の方が検出力が高い。 グループ化は「可視化・直感的理解」のために用いるのが主目的である。
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 | 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 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)。f"...{x}..." はf-string。文字列の中に {変数} と書くだけで埋め込めて、{x:.2f} のように書式も指定できます。219 220 221 222 223 224 225 226 227 228 | print("\n" + "=" * 65) print("■ OLS重回帰分析(2022年)") print("=" * 65) REG_VARS = ['高齢化率', 'B4101', 'B4102', 'B4106', '保健医療費割合'] REG_LABELS = ['高齢化率(%)', '年平均気温(℃)', '最高気温(℃)', '降水日数(日)', '保健医療費割合(%)'] df_reg = df_2022[['粗死亡率'] + REG_VARS].dropna() y_ols = df_reg['粗死亡率'].values X_ols = df_reg[REG_VARS].values |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。229 230 231 232 233 234 235 236 | # 標準化(係数の大きさを比較するため) X_std = (X_ols - X_ols.mean(axis=0)) / X_ols.std(axis=0) X_with_const = sm.add_constant(X_std) ols_model = sm.OLS(y_ols, X_with_const).fit() print(ols_model.summary2()) print(f"\n R² = {ols_model.rsquared:.4f}") print(f" 自由度修正済R² = {ols_model.rsquared_adj:.4f}") |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。sm.add_constant(X) — 切片項(定数1の列)を先頭に追加。statsmodelsで必須。sm.OLS(y, X).fit() — 最小二乗法でモデルを推定。model.params, model.pvalues, model.conf_int() で結果取得。r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。237 238 239 240 241 242 243 244 245 | # 相関分析(高齢化率 vs 粗死亡率) r_aging, p_aging = stats.pearsonr(df_2022['高齢化率'].dropna(), df_2022['粗死亡率'].dropna()) print(f"\n■ 相関分析(高齢化率 vs 粗死亡率): r={r_aging:.4f}, p={p_aging:.4f}") # 相関分析(年平均気温 vs 粗死亡率) r_temp, p_temp = stats.pearsonr(df_2022['B4101'].dropna(), df_2022['粗死亡率'].dropna()) print(f"■ 相関分析(年平均気温 vs 粗死亡率): r={r_temp:.4f}, p={p_temp:.4f}") |
=================================================================
■ OLS重回帰分析(2022年)
=================================================================
Results: Ordinary least squares
=================================================================
Model: OLS Adj. R-squared: 0.953
Dependent Variable: y AIC: 56.7146
Date: 2026-05-18 11:24 BIC: 67.8155
No. Observations: 47 Log-Likelihood: -22.357
Df Model: 5 F-statistic: 187.4
Df Residuals: 41 Prob (F-statistic): 4.13e-27
R-squared: 0.958 Scale: 0.17379
-------------------------------------------------------------------
Coef. Std.Err. t P>|t| [0.025 0.975]
-------------------------------------------------------------------
const 13.8479 0.0608 227.7322 0.0000 13.7251 13.9707
x1 1.8308 0.0714 25.6316 0.0000 1.6865 1.9751
x2 0.1650 0.1044 1.5809 0.1216 -0.0458 0.3757
x3 -0.1527 0.1070 -1.4264 0.1613 -0.3688 0.0635
x4 0.0914 0.0744 1.2282 0.2264 -0.0589 0.2417
x5 -0.1227 0.0735 -1.6698 0.1026 -0.2711 0.0257
-----------------------------------------------------------------
Omnibus: 0.491 Durbin-Watson: 2.098
Prob(Omnibus): 0.782 Jarque-Bera (JB): 0.120
Skew: -0.109 Prob(JB): 0.942
Kurtosis: 3.116 Condition No.: 4
=================================================================
Notes:
[1] Standard Errors assume that the covariance matrix of the
errors is correctly specified.
R² = 0.9581
自由度修正済R² = 0.9530
■ 相関分析(高齢化率 vs 粗死亡率): r=0.9722, p=0.0000
■ 相関分析(年平均気温 vs 粗死亡率): r=-0.3805, p=0.0083stats.pearsonr(x, y) — Pearson相関係数 r と p値を同時に返します。x if cond else y は三項演算子。リスト内包表記と組み合わせると、forとifを1行で書けます。246 247 248 249 250 251 | print("\n■ 図4: 気温帯別 死亡率箱ひげ図") # 2022年の全都道府県を年平均気温で3グループに分類(qcut) df_box = df_2022[['都道府県', 'B4101', '粗死亡率', '高齢化率']].dropna().copy() df_box['気温帯'] = pd.qcut(df_box['B4101'], q=3, labels=['低温帯\n(寒冷地)', '中温帯\n(温暖地)', '高温帯\n(温暖地)']) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。np.cumsum(arr) は累積和、np.linspace(a, b, n) は「aからbを等間隔でn個」。NumPyの定石です。252 253 254 255 256 257 258 259 260 261 | # グループ統計 print("\n 気温帯別 統計:") grp_stat = df_box.groupby('気温帯', observed=True).agg( n=('粗死亡率', 'count'), 気温平均=('B4101', 'mean'), 死亡率平均=('粗死亡率', 'mean'), 死亡率中央値=('粗死亡率', 'median'), 高齢化率平均=('高齢化率', 'mean'), ) print(grp_stat.to_string()) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。df.groupby('列').apply(関数) — グループごとに関数を適用。時系列や地域別の集計でよく使います。{値:.2f}(小数2桁)、{値:,}(3桁区切り)、{値:>10}(右寄せ10桁)など、覚えると出力が一気に整います。262 263 264 265 266 267 268 269 270 271 | # ANOVA検定 groups_by_temp = [g['粗死亡率'].values for _, g in df_box.groupby('気温帯', observed=True)] f_stat, p_anova = stats.f_oneway(*groups_by_temp) print(f"\n 一元配置ANOVA: F={f_stat:.3f}, p={p_anova:.4f}") # 気温帯ラベルと統計情報 temp_labels = df_box.groupby('気温帯', observed=True)['B4101'].mean() box_colors = ['#1565C0', '#43A047', '#E65100'] fig4, ax4 = plt.subplots(figsize=(9, 6)) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。df.groupby('列').apply(関数) — グループごとに関数を適用。時系列や地域別の集計でよく使います。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。plt.subplots(figsize=(W, H)) で図サイズ指定、fig.savefig(..., bbox_inches='tight') で余白を自動で詰めて保存。272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 | # 気温帯ごとにデータ収集 categories = df_box['気温帯'].cat.categories data_groups = [df_box[df_box['気温帯'] == cat]['粗死亡率'].values for cat in categories] n_groups = [len(g) for g in data_groups] mean_vals = [g.mean() for g in data_groups] temp_means = [df_box[df_box['気温帯'] == cat]['B4101'].mean() for cat in categories] bp = ax4.boxplot(data_groups, labels=[str(c) for c in categories], patch_artist=True, widths=0.55, medianprops=dict(color='white', linewidth=2.5), boxprops=dict(linewidth=1.5), whiskerprops=dict(linewidth=1.5), capprops=dict(linewidth=1.5), flierprops=dict(marker='o', markersize=5, alpha=0.6)) for patch, color in zip(bp['boxes'], box_colors): patch.set_facecolor(color) patch.set_alpha(0.78) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。.dropna() は欠損行を除去、.copy() は独立したコピーを作る。pandasで警告を防ぐ定石。292 293 294 295 296 297 298 299 300 301 | # 平均値をダイヤモンドでプロット for i, mv in enumerate(mean_vals): ax4.scatter(i + 1, mv, marker='D', color='white', s=55, zorder=5, edgecolors=box_colors[i], linewidths=1.5) # 各グループの n数・平均気温を注記 for i, (n, tm) in enumerate(zip(n_groups, temp_means)): ax4.text(i + 1, ax4.get_ylim()[0] + 0.15, f'n={n}\n平均気温\n{tm:.1f}℃', ha='center', va='bottom', fontsize=8.5, color='#333') |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。f"...{x}..." はf-string。文字列の中に {変数} と書くだけで埋め込めて、{x:.2f} のように書式も指定できます。302 303 304 305 306 307 308 309 310 | # ANOVA p値 sig_txt = f'一元配置ANOVA: F={f_stat:.2f}, p={p_anova:.4f}' ax4.set_xlabel('気温帯(年平均気温の三分位)', fontsize=12) ax4.set_ylabel('粗死亡率(人口千人あたり)', fontsize=12) ax4.set_title( f'気温帯別 粗死亡率の分布(2022年, N=47都道府県)\n〜{sig_txt}〜', fontsize=12, fontweight='bold' ) ax4.grid(axis='y', alpha=0.3) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。df['A'] / df['B'] — pandasの列同士の四則演算は要素ごと(element-wise)。forループ不要なのが強み。311 312 313 314 315 316 317 318 319 320 321 | # 凡例(菱形=平均値) from matplotlib.lines import Line2D legend_el = [Line2D([0], [0], marker='D', color='w', markerfacecolor='gray', markeredgecolor='gray', markersize=8, label='平均値')] ax4.legend(handles=legend_el, fontsize=9, loc='upper right') plt.tight_layout() fig4.savefig(os.path.join(FIG_DIR, '2022_H5_12_fig4_boxplot.png'), bbox_inches='tight', dpi=150) plt.close(fig4) print("図4保存: 2022_H5_12_fig4_boxplot.png") |
■ 図4: 気温帯別 死亡率箱ひげ図
気温帯別 統計:
n 気温平均 死亡率平均 死亡率中央値 高齢化率平均
気温帯
低温帯\n(寒冷地) 18 13.955556 14.445164 14.296151 32.228856
中温帯\n(温暖地) 13 16.530769 12.629442 12.659420 29.525427
高温帯\n(温暖地) 16 18.062500 14.165964 14.601701 31.843822
一元配置ANOVA: F=4.217, p=0.0211
図4保存: 2022_H5_12_fig4_boxplot.pngimport pandas as pd など — 必要なライブラリをまとめて呼び出します。as pd は短い別名(alias)。.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。322 323 324 325 326 327 328 329 330 331 332 333 334 | print("\n" + "=" * 65) print("■ 全図生成完了(4枚)") print(f" fig1_timeseries.png : 死亡率の時系列推移(地方別平均)") print(f" fig2_scatter.png : 高齢化率 vs 死亡率 散布図(2022年)") print(f" fig3_coef.png : OLS重回帰係数プロット") print(f" fig4_boxplot.png : 気温帯別 死亡率箱ひげ図") print("=" * 65) print(f"\n OLS結果サマリ(2022年, N={len(df_reg)}):") print(f" R²={ols_model.rsquared:.3f}, adj.R²={ols_model.rsquared_adj:.3f}") print(f" 高齢化率 β={ols_model.params[1]:.3f} (p={ols_model.pvalues[1]:.4f})") print(f" 年平均気温 β={ols_model.params[2]:.3f} (p={ols_model.pvalues[2]:.4f})") print(f" 最高気温 β={ols_model.params[3]:.3f} (p={ols_model.pvalues[3]:.4f})") print(f" 降水日数 β={ols_model.params[4]:.3f} (p={ols_model.pvalues[4]:.4f})") |
=================================================================
■ 全図生成完了(4枚)
fig1_timeseries.png : 死亡率の時系列推移(地方別平均)
fig2_scatter.png : 高齢化率 vs 死亡率 散布図(2022年)
fig3_coef.png : OLS重回帰係数プロット
fig4_boxplot.png : 気温帯別 死亡率箱ひげ図
=================================================================
OLS結果サマリ(2022年, N=47):
R²=0.958, adj.R²=0.953
高齢化率 β=1.831 (p=0.0000)
年平均気温 β=0.165 (p=0.1216)
最高気温 β=-0.153 (p=0.1613)
降水日数 β=0.091 (p=0.2264){値:.2f}(小数2桁)、{値:,}(3桁区切り)、{値:>10}(右寄せ10桁)など、覚えると出力が一気に整います。熱中症・寒冷障害など気候関連の健康被害は、個人レベルでは明確だが、 集計統計から統計的に検出するには以下の工夫が必要となる。
より有効な分析設計のヒント:
実務・政策の場では、こうした統計的根拠の積み重ねが 熱中症予防計画・高温地域の医療資源配分・気候変動適応政策の立案に活用される。
| ファイル | 内容 | 出典 |
|---|---|---|
| SSDSE-B-2026.csv | 都道府県別 社会・人口統計体系データ(2012〜2023年) | 統計数理研究所・統計センター(SSDSE公式サイト) |
| 気象データ(B4101〜B4109) | 年平均気温・最高気温・降水日数など | 気象庁データを基に SSDSE-B に収録 |
| 人口・死亡統計(A系列) | 総人口・65歳以上人口・死亡数 | 総務省「住民基本台帳」「人口動態統計」 |
| 家計調査(L系列) | 消費支出・保健医療費(二人以上の世帯) | 総務省「家計調査」 |
本教育用コードは SSDSE-B-2026.csv の実データのみを使用(合成データ・乱数生成なし)。 データ取得:SSDSE公式サイト
統計分析の解釈で初心者がやりがちな勘違いをまとめます。特に「相関と因果の混同」「p値の過信」は研究現場でもよく起きる落とし穴です。本文を読む前にも、読んだ後にも、目を通してみてください。
統計の基本用語を初心者向けに解説します。本文中で見慣れない言葉が出てきたら、ここに戻って確認してください。
統計手法について「何のためか」「結果をどう読むか」を初心者向けに解説します。
この研究をさらに発展させるための3つの方向性を示します。「今回わかったこと(X)」から「次に検証すべき仮説(Y)」を立て、「具体的に何をするか(Z)」まで考えてみましょう。
学んだだけでは身につきません。実際に手を動かすのが最強の学習方法です。本論文のスクリプトをベースに、以下のチャレンジに挑戦してみてください。難易度別に5つ用意しました。
本論文で学んだ手法は、研究の世界だけでなく、行政・企業・NPO の現場でも様々に活用されています。具体的なシーンを紹介します。
この論文を読んで初心者が抱きやすい疑問に、教育的観点から答えます。