このページの分析を自分で再現するには、以下の手順でデータを準備してください。コードの編集は不要です。
data/raw/ フォルダに入れます。html/figures/ に自動保存されます。
日本は世界最高水準の高齢化率を誇る「超高齢社会」であり、65歳以上人口の割合は2022年に全国平均31.4%に達している。 高齢化は医療費の増大をもたらすと一般に言われるが、都道府県によって高齢化率にも医療費にも大きなばらつきがある。 本論文は、47都道府県 × 12年間(2012〜2023年)のパネルデータを用いて、 保健医療費(家計調査ベース)を目的変数とし、高齢化率・医療機関数・消費水準などの要因を パネルOLS固定効果モデルで分析した。
まず「高齢化と医療費都道府県別パネルデータによる要因分析」を統計的にとらえることが有効だと考えられる。 その理由は感覚や経験則だけでは、複雑な社会要因の中で「何が本当に効いているか」を見極めにくいからである。 本研究では公開データと統計手法を組み合わせ、この問いに定量的な答えを出すことを目指す。
SSDSE-B パネルデータ 固定効果モデル 高齢化率 保健医療費
分析には統計センター公表の SSDSE-B-2026(社会・人口統計体系 都道府県データ)を使用する。 47都道府県 × 12年(2012〜2023年)= 564観測のバランスドパネルデータである。
| 変数 | SSDSE-B コード | 定義・単位 | 役割 |
|---|---|---|---|
| 保健医療費 | L322106 | 二人以上世帯 月平均支出 [円] | 目的変数(医療費の家計負担代理) |
| 高齢化率 | A1303 / A1101 | 65歳以上人口 / 総人口 × 100 [%] | 主要説明変数 |
| 一般病院数 | I510120 | 都道府県内の一般病院数 [施設] | 医療供給側指標 |
| 一般診療所数 | I5102 | 都道府県内の一般診療所数 [施設] | 医療供給側指標 |
| 消費支出(log) | L3221(対数変換) | 二人以上世帯 消費支出の自然対数 | 生活水準・所得代理 |
| 保育所等数 | J2503 | 都道府県内の保育所等数 [施設] | 人口構造・子育て環境 |
| 順位 | 都道府県 | 高齢化率 [%] | 保健医療費 [円/月] | 備考 |
|---|---|---|---|---|
| 1位(最高) | 秋田県 | 38.6 | 11,692 | 高齢化率最大、医療費は低め |
| 2位 | 高知県 | 36.1 | 12,560 | |
| 3位 | 山口県 | 35.2 | 14,326 | |
| 4位 | 徳島県 | 34.9 | 15,319 | |
| 5位 | 島根県 | 34.8 | 12,515 | |
| ― 下位(高齢化率が低い都道府県)― | ||||
| 43位 | 滋賀県 | 26.8 | 16,193 | |
| 44位 | 神奈川県 | 25.8 | 16,320 | |
| 45位 | 愛知県 | 25.6 | 19,107 | 医療費は高め |
| 46位 | 沖縄県 | 23.4 | 11,587 | 若い人口構成 |
| 47位(最低) | 東京都 | 22.8 | 17,303 | 高齢化率最低 |
1 2 3 4 5 6 7 8 | 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_b['年度'] = df_b['年度'].astype(int) df_b = df_b.sort_values(['都道府県', '年度']).reset_index(drop=True) print(f"パネルデータ形状: {df_b.shape} " f"({df_b['都道府県'].nunique()} 都道府県 × {df_b['年度'].nunique()} 年度)") print(f"年度範囲: {df_b['年度'].min()} 〜 {df_b['年度'].max()}") |
パネルデータ形状: (564, 112) (47 都道府県 × 12 年度) 年度範囲: 2012 〜 2023
pd.read_csv(...) でCSVを読み込みます。encoding='cp932' は日本語Windows由来の文字コード、header=1 は「2行目を列名として使う」。df['地域コード'].str.match(r'^R\d{5}', ...) — 正規表現で「R+数字5桁」の行(47都道府県)だけTrueにし、真偽値で行をフィルタ。.astype(int) — 列を整数に変換(年度などを数値比較するため)。sort_values('列名', ascending=False) — 指定列で並べ替え(降順)。df['A'] / df['B'] — pandasの列同士の四則演算は要素ごと(element-wise)。forループ不要なのが強み。9 10 11 12 13 14 15 16 17 | df_b['高齢化率'] = df_b['65歳以上人口'] / df_b['総人口'] * 100 df_b['消費支出_log'] = np.log(df_b['消費支出(二人以上の世帯)'].clip(lower=1)) df_b['保健医療費'] = pd.to_numeric(df_b['保健医療費(二人以上の世帯)'], errors='coerce') df_b['一般病院数'] = pd.to_numeric(df_b['一般病院数'], errors='coerce') df_b['一般診療所数'] = pd.to_numeric(df_b['一般診療所数'], errors='coerce') df_b['保育所等数'] = pd.to_numeric(df_b['保育所等数'], errors='coerce') # 保健医療費の対数(分析用) df_b['保健医療費_log'] = np.log(df_b['保健医療費'].clip(lower=1)) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。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 | region_map = { '北海道': '北海道・東北', '青森県': '北海道・東北', '岩手県': '北海道・東北', '宮城県': '北海道・東北', '秋田県': '北海道・東北', '山形県': '北海道・東北', '福島県': '北海道・東北', '茨城県': '関東', '栃木県': '関東', '群馬県': '関東', '埼玉県': '関東', '千葉県': '関東', '東京都': '関東', '神奈川県': '関東', '新潟県': '中部', '富山県': '中部', '石川県': '中部', '福井県': '中部', '山梨県': '中部', '長野県': '中部', '岐阜県': '中部', '静岡県': '中部', '愛知県': '中部', '三重県': '近畿', '滋賀県': '近畿', '京都府': '近畿', '大阪府': '近畿', '兵庫県': '近畿', '奈良県': '近畿', '和歌山県': '近畿', '鳥取県': '中国・四国', '島根県': '中国・四国', '岡山県': '中国・四国', '広島県': '中国・四国', '山口県': '中国・四国', '徳島県': '中国・四国', '香川県': '中国・四国', '愛媛県': '中国・四国', '高知県': '中国・四国', '福岡県': '九州・沖縄', '佐賀県': '九州・沖縄', '長崎県': '九州・沖縄', '熊本県': '九州・沖縄', '大分県': '九州・沖縄', '宮崎県': '九州・沖縄', '鹿児島県': '九州・沖縄', '沖縄県': '九州・沖縄', } df_b['地域'] = df_b['都道府県'].map(region_map) region_order = ['北海道・東北', '関東', '中部', '近畿', '中国・四国', '九州・沖縄'] region_colors = { '北海道・東北': '#1565C0', '関東': '#C62828', '中部': '#2E7D32', '近畿': '#E65100', '中国・四国': '#6A1B9A', '九州・沖縄': '#00838F', } |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。46 47 48 49 50 | print("\n" + "=" * 60) print("■ 記述統計(全期間 2012〜2023年)") print("=" * 60) desc_cols = ['高齢化率', '保健医療費', '一般病院数', '一般診療所数', '保育所等数', '消費支出_log'] print(df_b[desc_cols].describe().round(2)) |
============================================================
■ 記述統計(全期間 2012〜2023年)
============================================================
高齢化率 保健医療費 一般病院数 一般診療所数 保育所等数 消費支出_log
count 564.00 564.00 564.00 564.00 564.00 564.00
mean 29.24 13057.55 155.32 2175.24 555.33 12.56
std 3.48 1897.53 124.43 2396.32 482.61 0.08
min 17.72 8314.00 37.00 474.00 132.00 12.26
25% 27.03 11810.75 79.00 879.00 275.00 12.52
50% 29.47 12788.50 113.00 1380.00 402.50 12.57
75% 31.76 14254.00 168.50 2453.75 608.50 12.63
max 39.06 21000.00 601.00 14894.00 3615.00 12.78.describe() — 件数・平均・標準偏差・四分位・最大/最小を一括計算。データの素性チェックに必須。r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。まず全国的な動向と地域差を把握するため、6地域区分の平均値と全国平均の時系列推移を確認する。 2012年から2023年にかけての12年間の動向を見ると、全国平均は概ね上昇傾向にある。
| 年度 | 2012 | 2014 | 2016 | 2018 | 2020 | 2022 | 2023 |
|---|---|---|---|---|---|---|---|
| 全国平均(円/月) | 12,258 | 12,133 | 12,694 | 12,852 | 13,740 | 14,390 | 14,423 |
国際的な定義では、高齢化率(65歳以上人口比率)が 7%超を「高齢化社会」、14%超を「高齢社会」、 21%超を「超高齢社会」と呼ぶ。日本は2007年に超高齢社会に突入した。
SSDSE-B では総人口(A1101)と65歳以上人口(A1303)から計算できる。 分母・分子の単位が一致していることを確認することが基本。
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 | print("\n[図1] 保健医療費 時系列プロット作成中...") ts_region = df_b.groupby(['年度', '地域'])['保健医療費'].mean().reset_index() ts_all = df_b.groupby('年度')['保健医療費'].mean().reset_index() ts_all.columns = ['年度', '全国平均'] fig1, ax1 = plt.subplots(figsize=(10, 5.5)) for region in region_order: rdata = ts_region[ts_region['地域'] == region] ax1.plot(rdata['年度'], rdata['保健医療費'], 'o-', color=region_colors[region], linewidth=1.8, markersize=5, label=region, alpha=0.85) ax1.plot(ts_all['年度'], ts_all['全国平均'], 'k--', linewidth=2.5, markersize=0, label='全国平均', zorder=10) ax1.set_xlabel('年度', fontsize=11) ax1.set_ylabel('保健医療費(円/月、二人以上世帯)', fontsize=11) ax1.set_title('保健医療費の時系列推移(地域別平均、2012〜2023年)\n' 'データ:SSDSE-B-2026 実データ(47都道府県)', fontsize=12, fontweight='bold') ax1.legend(fontsize=9, loc='upper left', framealpha=0.9) ax1.grid(axis='y', alpha=0.35) ax1.xaxis.set_major_locator(mticker.MultipleLocator(2)) ax1.yaxis.set_major_formatter(mticker.FuncFormatter(lambda x, _: f'{int(x):,}')) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。df.groupby('列').apply(関数) — グループごとに関数を適用。時系列や地域別の集計でよく使います。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。x if cond else y は三項演算子。リスト内包表記と組み合わせると、forとifを1行で書けます。77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | # コロナ注釈 y_range = ax1.get_ylim() try: covid_val = ts_all.loc[ts_all['年度'] == 2020, '全国平均'].values[0] ax1.annotate('コロナ禍\n(2020)', xy=(2020, covid_val), xytext=(2021, covid_val - 800), fontsize=8.5, color='#555', arrowprops=dict(arrowstyle='->', color='#555', lw=1)) except Exception: pass plt.tight_layout() fig1.savefig(os.path.join(FIG_DIR, '2022_H5_6_fig1_ts.png'), dpi=150, bbox_inches='tight') plt.close(fig1) print(" -> 保存: 2022_H5_6_fig1_ts.png") |
[図1] 保健医療費 時系列プロット作成中... -> 保存: 2022_H5_6_fig1_ts.png
df[col](1列)と df[[col1, col2]](複数列)でカッコの数が違います。リストを渡していると覚えるとミスを減らせます。2022年のクロスセクション(横断面)データを用いて、47都道府県の高齢化率と保健医療費の散布図を描く。 地域ブロック別に色分けし、都道府県名のラベルを付与することで地域パターンを可視化する。
| 変数(2022年) | 保健医療費との相関 r | p値 | 有意性 |
|---|---|---|---|
| 高齢化率 | −0.468 | 0.001 | ** 有意 |
| 一般病院数 | +0.267 | 0.069 | n.s. |
| 一般診療所数 | +0.221 | 0.135 | n.s. |
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | print("\n[図2] 高齢化率 vs 保健医療費 散布図(2022年)作成中...") df_2022 = df_b[df_b['年度'] == 2022].dropna(subset=['高齢化率', '保健医療費']).copy() fig2, ax2 = plt.subplots(figsize=(12, 8)) for region in region_order: rdf = df_2022[df_2022['地域'] == region] ax2.scatter(rdf['高齢化率'], rdf['保健医療費'], color=region_colors[region], s=60, alpha=0.85, label=region, zorder=4) for _, row in rdf.iterrows(): # 都道府県名を短縮(県・府・都・道を削除) short = row['都道府県'].replace('都', '').replace('道', '').replace('府', '').replace('県', '') ax2.annotate(short, xy=(row['高齢化率'], row['保健医療費']), xytext=(2, 2), textcoords='offset points', fontsize=6.5, alpha=0.8) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。for _, row in df.iterrows() — DataFrameを1行ずつ取り出すループ。1点ずつ描画したいときに使用。df[col](1列)と df[[col1, col2]](複数列)でカッコの数が違います。リストを渡していると覚えるとミスを減らせます。111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | # 回帰直線 x_fit = df_2022['高齢化率'].values y_fit = df_2022['保健医療費'].values mask_fit = np.isfinite(x_fit) & np.isfinite(y_fit) z = np.polyfit(x_fit[mask_fit], y_fit[mask_fit], 1) xs = np.linspace(x_fit[mask_fit].min(), x_fit[mask_fit].max(), 100) ax2.plot(xs, np.poly1d(z)(xs), 'k--', linewidth=1.5, alpha=0.7, label='回帰直線', zorder=5) r, p = stats.pearsonr(x_fit[mask_fit], y_fit[mask_fit]) ax2.set_xlabel('高齢化率(65歳以上人口比率 %)', fontsize=12) ax2.set_ylabel('保健医療費(円/月、二人以上世帯)', fontsize=12) ax2.set_title(f'高齢化率 vs 保健医療費(2022年、都道府県別)\n' f'相関係数 r = {r:.3f}(p = {p:.4f})', fontsize=13, fontweight='bold') ax2.legend(fontsize=9, loc='upper left', framealpha=0.9) ax2.grid(True, alpha=0.3) ax2.yaxis.set_major_formatter(mticker.FuncFormatter(lambda x, _: f'{int(x):,}')) plt.tight_layout() fig2.savefig(os.path.join(FIG_DIR, '2022_H5_6_fig2_scatter.png'), dpi=150, bbox_inches='tight') plt.close(fig2) print(f" -> 保存: 2022_H5_6_fig2_scatter.png (r={r:.3f}, p={p:.4f})") |
[図2] 高齢化率 vs 保健医療費 散布図(2022年)作成中... -> 保存: 2022_H5_6_fig2_scatter.png (r=-0.468, p=0.0009)
stats.pearsonr(x, y) — Pearson相関係数 r と p値を同時に返します。s[:-n]「末尾n文字を除く」/s[n:]「先頭n文字を除く」。スライス [start:stop:step] はリスト・タプル・文字列共通の基本ワザです。47都道府県 × 12年(2012〜2023年)の564観測を用いてパネルOLS固定効果モデルを推定する。 標準誤差は都道府県レベルのクラスター標準誤差を使用する。
αi:都道府県固定効果(時間不変の地域特性を吸収)、i=都道府県、t=年度
固定効果モデルでは「各都道府県の平均からの乖離」だけを使って回帰を行う。 例えば、秋田県は常に高齢化率が高く医療費は低い。固定効果はこの「常に高い/低い」という 都道府県ごとの水準(αi)を取り除いて、「高齢化率が上昇した年に医療費も上昇したか?」 という時間内変動に着目する。
| 都道府県 | 2012年 高齢化率 | 2022年 高齢化率 | 変化量(時間内変動) | 固定効果 αi |
|---|---|---|---|---|
| 秋田県 | 31.8% | 38.6% | +6.8ポイント | 低医療費の地域特性 |
| 東京都 | 20.4% | 22.8% | +2.4ポイント | 高医療費の地域特性 |
| 沖縄県 | 17.7% | 23.4% | +5.7ポイント | 低医療費の地域特性 |
| 変数 | 係数 (β) | 標準誤差 | t値 | p値 | 解釈 |
|---|---|---|---|---|---|
| 高齢化率 | +257.5 | 49.5 | 5.20 | <0.001 *** | 高齢化率1%上昇 → 保健医療費+258円/月 |
| 一般病院数 | −47.7 | 23.3 | −2.05 | 0.041 * | 病院増加が費用抑制に寄与(代替効果?) |
| 一般診療所数 | +0.3 | 1.2 | 0.28 | 0.782 n.s. | 統計的に有意でない |
| 消費支出(log) | +9,441 | 1,140 | 8.28 | <0.001 *** | 生活水準が高いほど医療支出も大 |
| 保育所等数 | +1.6 | 0.8 | 1.92 | 0.056 † | 境界有意(10%水準) |
注:†は10%水準で有意、*は5%水準、***は0.1%水準。クラスター標準誤差(都道府県単位)使用。
パネルデータは「個体(都道府県)× 時点(年度)」の2次元データ。 固定効果モデルは各個体の時間平均を引き算する「within 変換」を行い、 個体固有の時間不変な特性(気候・文化・制度)の影響を消去する。
Pythonの linearmodels パッケージを使うとPanelOLSが簡単に推定できる。 クラスター標準誤差は同一都道府県内の観測が相関することを考慮した頑健な標準誤差。
「医療費」には複数の測定方法がある。本論文が使用する保健医療費(家計調査ベース)は、 家計が実際に支払った医療費(保険診療の自己負担分+市販薬など)を家計調査から集計したものであり、 都道府県別・年次別に利用可能なSSSDSE-Bで得られる。
これに対し、厚生労働省の「国民医療費」は診療行為全体(保険給付分を含む)の総額を集計したものであり、 概念が異なる。家計調査ベースは「家計の負担」、国民医療費は「社会全体の医療費」に相当する。 代理変数の選択はリサーチクエスチョンに応じて慎重に判断する必要がある。
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | print("\n[図3] 係数プロット作成中...") # 係数の色(p値によって色分け) colors3 = ['#C62828' if p < 0.01 else '#FF8F00' if p < 0.05 else '#9E9E9E' for p in fe_pvalues] y_pos3 = np.arange(len(fe_var_names)) fig3, ax3 = plt.subplots(figsize=(9, max(4.5, len(fe_var_names) * 0.9))) ax3.barh(y_pos3, fe_params, color=colors3, alpha=0.78, edgecolor='white', height=0.6) ax3.errorbar(fe_params, y_pos3, xerr=1.96 * fe_bse, fmt='none', color='#333', capsize=4, linewidth=1.2) ax3.axvline(0, color='gray', linestyle='--', linewidth=1.2) ax3.set_yticks(y_pos3) ax3.set_yticklabels(fe_var_names, fontsize=11) ax3.set_xlabel('回帰係数(±1.96 SE)', fontsize=11) model_label = 'パネルOLS 固定効果(クラスター SE)' if fe_success else 'OLS(2022年クロスセクション)' ax3.set_title(f'保健医療費の要因分析 — {model_label}\n' f'R² = {fe_rsquared:.4f} | N = {len(df_panel)} 観測', fontsize=12, fontweight='bold') ax3.invert_yaxis() ax3.grid(axis='x', alpha=0.3) ax3.legend(handles=[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.')], fontsize=9, loc='lower right') |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。np.cumsum(arr) は累積和、np.linspace(a, b, n) は「aからbを等間隔でn個」。NumPyの定石です。161 162 163 164 165 166 167 168 169 170 171 172 | # 係数値と有意性ラベルを棒の外側に表示 for i, (coef, pval) in enumerate(zip(fe_params, fe_pvalues)): sig = '***' if pval < 0.001 else '**' if pval < 0.01 else '*' if pval < 0.05 else '' offset = abs(fe_bse[i]) * 2.2 if fe_bse[i] > 0 else 5 ha = 'left' if coef >= 0 else 'right' x_label = coef + offset if coef >= 0 else coef - offset ax3.text(x_label, i, f'{coef:.1f}{sig}', va='center', ha=ha, fontsize=8.5) plt.tight_layout() fig3.savefig(os.path.join(FIG_DIR, '2022_H5_6_fig3_coef.png'), dpi=150, bbox_inches='tight') plt.close(fig3) print(" -> 保存: 2022_H5_6_fig3_coef.png") |
[図3] 係数プロット作成中... -> 保存: 2022_H5_6_fig3_coef.png
{値:.2f}(小数2桁)、{値:,}(3桁区切り)、{値:>10}(右寄せ10桁)など、覚えると出力が一気に整います。供給側の要因として一般病院数と保健医療費の関係を2022年クロスセクションで確認する。 都道府県によって病院数の規模が大きく異なるため、地域ラベル付きで可視化する。
| 指標 | 最多都道府県 | 最少都道府県 | 全国平均 | 変動係数 |
|---|---|---|---|---|
| 一般病院数 | 東京都(601施設) | 鳥取県(37施設) | 155施設 | 80.1% |
| 一般診療所数 | 東京都(14,894施設) | 鳥取県(474施設) | 2,175施設 | 110.1% |
注:変動係数(標準偏差/平均×100%)が大きいほど都道府県間のばらつきが大きい。 医療機関数は人口規模を反映するため、解釈には人口対比の指標も必要。
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | import numpy as np import pandas as pd import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt import matplotlib.ticker as mticker from matplotlib.patches import Patch import warnings warnings.filterwarnings('ignore') import statsmodels.api as sm from scipy import stats 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} のように書式も指定できます。195 196 197 198 199 200 201 202 203 204 205 206 207 208 | print("\n[パネル分析] 固定効果モデル推定中...") EXOG_VARS = ['高齢化率', '一般病院数', '一般診療所数', '消費支出_log', '保育所等数'] ENDOG_VAR = '保健医療費' df_panel = df_b[['都道府県', '年度', ENDOG_VAR] + EXOG_VARS].dropna().copy() print(f" 分析対象: {len(df_panel)} 観測 " f"({df_panel['都道府県'].nunique()} 都道府県 × {df_panel['年度'].nunique()} 年度)") fe_success = False fe_params = None fe_bse = None fe_pvalues = None fe_rsquared = None |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。s[:-n]「末尾n文字を除く」/s[n:]「先頭n文字を除く」。スライス [start:stop:step] はリスト・タプル・文字列共通の基本ワザです。209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 | # PanelOLS 固定効果モデル try: from linearmodels.panel import PanelOLS df_pi = df_panel.set_index(['都道府県', '年度']) fe_model = PanelOLS( df_pi[ENDOG_VAR], sm.add_constant(df_pi[EXOG_VARS]), entity_effects=True, time_effects=False, ) fe_res = fe_model.fit(cov_type='clustered', cluster_entity=True) print("\n[固定効果モデル推定結果(クラスター標準誤差)]") print(fe_res.summary.tables[1]) # パラメータ抽出(const 除く) params_s = fe_res.params.drop('const', errors='ignore') bse_s = fe_res.std_errors.drop('const', errors='ignore') pvalues_s = fe_res.pvalues.drop('const', errors='ignore') fe_params = params_s.values fe_bse = bse_s.values fe_pvalues = pvalues_s.values fe_var_names = params_s.index.tolist() fe_rsquared = fe_res.rsquared_between if hasattr(fe_res, 'rsquared_between') else float('nan') fe_success = True print(f" 固定効果モデル成功(R²_within ≒ {fe_res.rsquared:.4f})") except ImportError: print(" linearmodels 未インストール -> クロスセクションOLSにフォールバック") |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。import pandas as pd など — 必要なライブラリをまとめて呼び出します。as pd は短い別名(alias)。sm.add_constant(X) — 切片項(定数1の列)を先頭に追加。statsmodelsで必須。np.cumsum(arr) は累積和、np.linspace(a, b, n) は「aからbを等間隔でn個」。NumPyの定石です。238 239 240 241 242 243 244 245 246 247 248 249 | # フォールバック: 2022年クロスセクションOLS if not fe_success: df_ols = df_b[df_b['年度'] == 2022][['保健医療費'] + EXOG_VARS].dropna() X_ols = sm.add_constant(df_ols[EXOG_VARS]) ols_res = sm.OLS(df_ols['保健医療費'], X_ols).fit() print("\n[クロスセクションOLS(2022年)]") print(ols_res.summary2()) fe_params = ols_res.params[EXOG_VARS].values fe_bse = ols_res.bse[EXOG_VARS].values fe_pvalues = ols_res.pvalues[EXOG_VARS].values fe_var_names = EXOG_VARS fe_rsquared = ols_res.rsquared |
[パネル分析] 固定効果モデル推定中...
分析対象: 564 観測 (47 都道府県 × 12 年度)
[固定効果モデル推定結果(クラスター標準誤差)]
Parameter Estimates
==============================================================================
Parameter Std. Err. T-stat P-value Lower CI Upper CI
------------------------------------------------------------------------------
const -1.073e+05 1.586e+04 -6.7625 0.0000 -1.385e+05 -7.612e+04
高齢化率 257.46 49.531 5.1979 0.0000 160.15 354.77
一般病院数 -47.725 23.269 -2.0510 0.0408 -93.440 -2.0097
一般診療所数 0.3357 1.2101 0.2774 0.7816 -2.0418 2.7131
消費支出_log 9440.9 1139.7 8.2833 0.0000 7201.7 1.168e+04
保育所等数 1.5702 0.8188 1.9177 0.0557 -0.0384 3.1788
==============================================================================
固定効果モデル成功(R²_within ≒ 0.3417)sm.add_constant(X) — 切片項(定数1の列)を先頭に追加。statsmodelsで必須。sm.OLS(y, X).fit() — 最小二乗法でモデルを推定。model.params, model.pvalues, model.conf_int() で結果取得。{値:.2f}(小数2桁)、{値:,}(3桁区切り)、{値:>10}(右寄せ10桁)など、覚えると出力が一気に整います。250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 | print("\n[図4] 一般病院数 vs 保健医療費 散布図(2022年)作成中...") df_2022b = df_b[df_b['年度'] == 2022].dropna(subset=['一般病院数', '保健医療費']).copy() fig4, ax4 = plt.subplots(figsize=(10, 6.5)) for region in region_order: rdf = df_2022b[df_2022b['地域'] == region] ax4.scatter(rdf['一般病院数'], rdf['保健医療費'], color=region_colors[region], s=65, alpha=0.85, label=region, zorder=4) for _, row in rdf.iterrows(): short = row['都道府県'].replace('都', '').replace('道', '').replace('府', '').replace('県', '') ax4.annotate(short, xy=(row['一般病院数'], row['保健医療費']), xytext=(2, 2), textcoords='offset points', fontsize=6.5, alpha=0.8) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。for _, row in df.iterrows() — DataFrameを1行ずつ取り出すループ。1点ずつ描画したいときに使用。{値:.2f}(小数2桁)、{値:,}(3桁区切り)、{値:>10}(右寄せ10桁)など、覚えると出力が一気に整います。267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 | # 回帰直線 x_h = df_2022b['一般病院数'].values y_h = df_2022b['保健医療費'].values mask_h = np.isfinite(x_h) & np.isfinite(y_h) z_h = np.polyfit(x_h[mask_h], y_h[mask_h], 1) xs_h = np.linspace(x_h[mask_h].min(), x_h[mask_h].max(), 100) ax4.plot(xs_h, np.poly1d(z_h)(xs_h), 'k--', linewidth=1.5, alpha=0.7, label='回帰直線') r_h, p_h = stats.pearsonr(x_h[mask_h], y_h[mask_h]) ax4.set_xlabel('一般病院数(施設)', fontsize=12) ax4.set_ylabel('保健医療費(円/月、二人以上世帯)', fontsize=12) ax4.set_title(f'一般病院数 vs 保健医療費(2022年、都道府県別)\n' f'相関係数 r = {r_h:.3f}(p = {p_h:.4f})', fontsize=13, fontweight='bold') ax4.legend(fontsize=9, loc='upper right', framealpha=0.9) ax4.grid(True, alpha=0.3) ax4.yaxis.set_major_formatter(mticker.FuncFormatter(lambda x, _: f'{int(x):,}')) plt.tight_layout() fig4.savefig(os.path.join(FIG_DIR, '2022_H5_6_fig4_hospital.png'), dpi=150, bbox_inches='tight') plt.close(fig4) print(f" -> 保存: 2022_H5_6_fig4_hospital.png (r={r_h:.3f}, p={p_h:.4f})") |
[図4] 一般病院数 vs 保健医療費 散布図(2022年)作成中... -> 保存: 2022_H5_6_fig4_hospital.png (r=0.267, p=0.0692)
stats.pearsonr(x, y) — Pearson相関係数 r と p値を同時に返します。plt.subplots(figsize=(W, H)) で図サイズ指定、fig.savefig(..., bbox_inches='tight') で余白を自動で詰めて保存。289 290 291 292 293 294 295 296 297 | print("\n" + "=" * 60) print("■ 全図の生成完了(4枚)") print("=" * 60) print(" fig1: 2022_H5_6_fig1_ts.png — 保健医療費 時系列(地域別平均)") print(" fig2: 2022_H5_6_fig2_scatter.png — 高齢化率 vs 保健医療費(2022年)") print(" fig3: 2022_H5_6_fig3_coef.png — パネルOLS 固定効果 係数プロット") print(" fig4: 2022_H5_6_fig4_hospital.png — 一般病院数 vs 保健医療費(2022年)") print() print(f" 保存先: {os.path.abspath(FIG_DIR)}") |
============================================================ ■ 全図の生成完了(4枚) ============================================================ fig1: 2022_H5_6_fig1_ts.png — 保健医療費 時系列(地域別平均) fig2: 2022_H5_6_fig2_scatter.png — 高齢化率 vs 保健医療費(2022年) fig3: 2022_H5_6_fig3_coef.png — パネルOLS 固定効果 係数プロット fig4: 2022_H5_6_fig4_hospital.png — 一般病院数 vs 保健医療費(2022年) 保存先: /Users/shimpei/Dropbox/Works_Researches/2026 統計・データ解析コンペ/html/figures
plt.subplots(figsize=(W, H)) で図サイズ指定、fig.savefig(..., bbox_inches='tight') で余白を自動で詰めて保存。47都道府県 × 12年のパネルデータと固定効果モデルを用いた分析から、以下の結果が得られた:
本分析が示すように、高齢化率の上昇は保健医療費を押し上げる。 この傾向が医療保険全体に当てはまるとすると、2040年頃に全国高齢化率が約35%に達した時、 医療費への圧力は現在より大幅に高まると予測される。
統計データからこうした将来予測を行う「エビデンスに基づく政策立案(EBPM)」は、 データサイエンスの重要な応用領域の一つ。高齢化率の将来推計(国立社会保障・人口問題研究所) と本分析の係数を組み合わせることで、都道府県ごとの医療費上昇シナリオを試算できる。
| データ・リソース | 出典・説明 |
|---|---|
| SSDSE-B-2026(都道府県データ) | 統計センター 社会・人口統計体系(47都道府県 × 12年, 2012〜2023年) |
| 保健医療費(L322106) | 家計調査(総務省統計局):二人以上世帯の月平均消費支出の内訳 |
| 65歳以上人口・総人口(A1303/A1101) | 住民基本台帳人口移動報告・推計人口(総務省統計局) |
| 一般病院数・一般診療所数(I510120/I5102) | 医療施設調査(厚生労働省) |
| linearmodels(PanelOLS) | Python パッケージ。pip install linearmodels でインストール可能 |
※ 本教育用コードは SSDSE-B-2026.csv の実データのみを使用(合成データは一切使用しない)。
統計分析の解釈で初心者がやりがちな勘違いをまとめます。特に「相関と因果の混同」「p値の過信」は研究現場でもよく起きる落とし穴です。本文を読む前にも、読んだ後にも、目を通してみてください。
統計の基本用語を初心者向けに解説します。本文中で見慣れない言葉が出てきたら、ここに戻って確認してください。
統計手法について「何のためか」「結果をどう読むか」を初心者向けに解説します。
この研究をさらに発展させるための3つの方向性を示します。「今回わかったこと(X)」から「次に検証すべき仮説(Y)」を立て、「具体的に何をするか(Z)」まで考えてみましょう。
学んだだけでは身につきません。実際に手を動かすのが最強の学習方法です。本論文のスクリプトをベースに、以下のチャレンジに挑戦してみてください。難易度別に5つ用意しました。
本論文で学んだ手法は、研究の世界だけでなく、行政・企業・NPO の現場でも様々に活用されています。具体的なシーンを紹介します。
この論文を読んで初心者が抱きやすい疑問に、教育的観点から答えます。