このページの分析を自分で再現するには、以下の手順でデータを準備してください。コードの編集は不要です。
data/raw/ フォルダに入れます。html/figures/ に自動保存されます。
2020年初頭から日本全国に拡大した新型コロナウイルス感染症(COVID-19)は、都道府県ごとに異なる広がり方を示した。本研究は「なぜ地域によって感染規模が異なるのか」という問いに、周辺環境変数(人口密度・高齢化率・観光・消費行動・雇用・地価)を用いて統計的に迫ることを目的とする。
まず「周辺環境からみた新型コロナウイルス感染症の要因分析」を統計的にとらえることが有効だと考えられる。 その理由は感覚や経験則だけでは、複雑な社会要因の中で「何が本当に効いているか」を見極めにくいからである。 本研究では公開データと統計手法を組み合わせ、この問いに定量的な答えを出すことを目指す。
COVID-19 SSDSE-B 延べ宿泊者数(代理変数) OLS重回帰 都道府県別比較
感染拡大の主因は「人と人との接触」すなわち人流である。しかし COVID-19 感染者数をそのまま目的変数にすると、検査体制の地域差・報告遅延・過少申告などのバイアスが大きい。本研究では代わりに延べ宿泊者数(人口一人あたり)を接触密度の代理指標として採用した。
| 変数名 | SSDSE-B 原列名 | 想定効果 |
|---|---|---|
| 高齢化率(%) | 65歳以上人口 / 総人口 × 100 | 正(高齢者は活動範囲が限られる一方で重症化リスク) |
| 宿泊施設客室数 | 旅館営業施設客室数(ホテルを含む) | 正(観光需要・宿泊需要が高い地域) |
| 交通・通信費(万円) | 交通・通信費(二人以上の世帯) | 正(移動が多い世帯 → 人流増加) |
| 消費支出(万円) | 消費支出(二人以上の世帯) | 正(都市的生活 → 外出頻度増加) |
| 月間有効求人数(千件) | 月間有効求人数(一般) | 正(経済活動活発 → 人の流れ増加) |
| 住宅地地価(千円/m²) | 標準価格(平均価格)(住宅地) | 正(都市圏 → 密集・人流多) |
COVID-19 のような外生的ショックは、自然実験(Natural Experiment)の機会を提供する。「2019年(コロナ前)vs 2020年(コロナ後)」という比較は、政策介入なしには行えない大規模な制約実験に相当する。
ただし「都道府県によって宣言の時期・強度が異なる」「旅行者の自発的な自粛」なども混在するため、純粋な実験とは言えない。この「準実験」の発想が感染症疫学・経済学研究の基盤となっている。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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() # 数値列を変換 num_cols = [ '総人口', '65歳以上人口', '延べ宿泊者数', '外国人延べ宿泊者数', '旅館営業施設客室数(ホテルを含む)', '旅館営業施設数(ホテルを含む)', '年平均気温', '交通・通信費(二人以上の世帯)', '消費支出(二人以上の世帯)', '月間有効求人数(一般)', '標準価格(平均価格)(住宅地)', ] for c in num_cols: if c in df_b.columns: df_b[c] = pd.to_numeric(df_b[c], errors='coerce') print("=" * 60) print(f"■ SSDSE-B 都道府県データ: {df_b.shape[0]}行 × {df_b.shape[1]}列") print(f" 年度範囲: {df_b['年度'].min()} 〜 {df_b['年度'].max()}") print(f" 都道府県数: {df_b['都道府県'].nunique()}") print("=" * 60) |
============================================================ ■ SSDSE-B 都道府県データ: 564行 × 112列 年度範囲: 2012 〜 2023 都道府県数: 47 ============================================================
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ループ不要なのが強み。20 21 22 | df_b['宿泊密度'] = df_b['延べ宿泊者数'] / df_b['総人口'].clip(1) df_b['高齢化率'] = df_b['65歳以上人口'] / df_b['総人口'].clip(1) * 100 # % df_b['外国人宿泊率'] = df_b['外国人延べ宿泊者数'] / df_b['延べ宿泊者数'].clip(1) * 100 # % |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。23 24 25 26 27 | df_2019 = df_b[df_b['年度'] == 2019].copy() df_2020 = df_b[df_b['年度'] == 2020].copy() print(f"\n■ 2019年断面: N={len(df_2019)}") print(f" 宿泊密度 平均={df_2019['宿泊密度'].mean():.2f}, 中央値={df_2019['宿泊密度'].median():.2f}") |
■ 2019年断面: N=47 宿泊密度 平均=4.19, 中央値=3.71
r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。28 29 30 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 | ENV_VARS = { '高齢化率(%)': '高齢化率', '宿泊施設客室数': '旅館営業施設客室数(ホテルを含む)', '交通通信費(万円)': '交通・通信費(二人以上の世帯)', '消費支出(万円)': '消費支出(二人以上の世帯)', '有効求人数(千)': '月間有効求人数(一般)', '地価住宅地': '標準価格(平均価格)(住宅地)', } df_2019 = df_2019.copy() df_2019['高齢化率'] = df_2019['高齢化率'] df_2019['宿泊施設客室数'] = pd.to_numeric(df_2019['旅館営業施設客室数(ホテルを含む)'], errors='coerce') df_2019['交通通信費(万円)'] = pd.to_numeric(df_2019['交通・通信費(二人以上の世帯)'], errors='coerce') / 10000 df_2019['消費支出(万円)'] = pd.to_numeric(df_2019['消費支出(二人以上の世帯)'], errors='coerce') / 10000 df_2019['有効求人数(千)'] = pd.to_numeric(df_2019['月間有効求人数(一般)'], errors='coerce') / 1000 df_2019['地価住宅地'] = pd.to_numeric(df_2019['標準価格(平均価格)(住宅地)'], errors='coerce') XVARS = ['高齢化率', '宿泊施設客室数', '交通通信費(万円)', '消費支出(万円)', '有効求人数(千)', '地価住宅地'] XVAR_LABELS = { '高齢化率': '高齢化率(%)', '宿泊施設客室数': '宿泊施設客室数', '交通通信費(万円)': '交通・通信費(万円)', '消費支出(万円)': '消費支出(万円)', '有効求人数(千)': '月間有効求人数(千件)', '地価住宅地': '住宅地地価(千円/m²)', } df_2019_clean = df_2019[['都道府県', '宿泊密度'] + XVARS].dropna() N = len(df_2019_clean) print(f"\n■ 2019年 分析用データ: N={N}(欠損除外後)") |
■ 2019年 分析用データ: N=47(欠損除外後)
x if cond else y は三項演算子。リスト内包表記と組み合わせると、forとifを1行で書けます。58 59 60 61 62 63 64 65 66 67 68 69 | df_merge = pd.merge( df_2019[['都道府県', '宿泊密度']].rename(columns={'宿泊密度': '宿泊密度_2019'}), df_2020[['都道府県', '宿泊密度']].rename(columns={'宿泊密度': '宿泊密度_2020'}), on='都道府県' ) df_merge['変化率'] = (df_merge['宿泊密度_2020'] - df_merge['宿泊密度_2019']) / df_merge['宿泊密度_2019'].clip(0.001) * 100 df_merge = df_merge.sort_values('変化率').reset_index(drop=True) print("\n■ 2019→2020 宿泊密度変化率 上位・下位5件:") print(df_merge[['都道府県', '変化率']].tail(5).to_string()) print("...") print(df_merge[['都道府県', '変化率']].head(5).to_string()) |
■ 2019→2020 宿泊密度変化率 上位・下位5件: 都道府県 変化率 42 愛媛県 -30.589659 43 埼玉県 -29.635184 44 茨城県 -29.626611 45 福島県 -29.165637 46 山口県 -27.909291 ... 都道府県 変化率 0 大阪府 -63.235924 1 東京都 -60.207558 2 沖縄県 -57.467916 3 京都府 -55.261705 4 千葉県 -54.373239
sort_values('列名', ascending=False) — 指定列で並べ替え(降順)。np.cumsum(arr) は累積和、np.linspace(a, b, n) は「aからbを等間隔でn個」。NumPyの定石です。2012〜2023年の延べ宿泊者数(人口一人あたり)の全国平均推移を示す。2020年にCOVID-19が拡大し、人流指標がどれほど急落したかを視覚的に確認できる。
71 72 73 74 75 76 77 78 79 80 | fig1, ax1 = plt.subplots(figsize=(10, 5)) years = ts['年度'].values values = ts['宿泊密度_平均'].values # COVID期(2020〜2021)シェード ax1.axvspan(2019.5, 2021.5, alpha=0.18, color='#C62828', label='COVID-19 流行期(2020〜2021)') ax1.plot(years, values, 'o-', color='#1565C0', linewidth=2.2, markersize=6, label='延べ宿泊者数(人口一人あたり)全国平均', zorder=3) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。{値:.2f}(小数2桁)、{値:,}(3桁区切り)、{値:>10}(右寄せ10桁)など、覚えると出力が一気に整います。81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | # 2019→2020 矢印注釈 y19 = ts[ts['年度'] == 2019]['宿泊密度_平均'].values y20 = ts[ts['年度'] == 2020]['宿泊密度_平均'].values if len(y19) and len(y20): ax1.annotate('', xy=(2020, y20[0]), xytext=(2019, y19[0]), arrowprops=dict(arrowstyle='->', color='#C62828', lw=2.0)) ax1.text(2019.5, (y19[0] + y20[0]) / 2 + 0.05, '▼COVID衝撃', color='#C62828', fontsize=9, ha='center') ax1.set_xlabel('年度', fontsize=12) ax1.set_ylabel('延べ宿泊者数(人口一人あたり:泊/人)', fontsize=12) ax1.set_title('延べ宿泊者数(人口あたり)の推移 2012〜2023\n(都道府県平均・SSDSE-B実データ)', fontsize=13, fontweight='bold') ax1.set_xticks(years) ax1.set_xticklabels([str(y) for y in years], rotation=45, ha='right') ax1.legend(fontsize=10) ax1.grid(True, alpha=0.3) plt.tight_layout() fig1.savefig(os.path.join(FIG_DIR, '2022_U5_6_fig1_ts.png'), bbox_inches='tight', dpi=150) plt.close(fig1) print("\n図1保存: 2022_U5_6_fig1_ts.png") |
図1保存: 2022_U5_6_fig1_ts.png
plt.subplots(figsize=(W, H)) で図サイズ指定、fig.savefig(..., bbox_inches='tight') で余白を自動で詰めて保存。2019年の都道府県断面データ(N=47)を用いて、延べ宿泊密度と各環境変数の相関構造を把握する。ヒートマップにより変数間の多変量相関を一覧できる。
本研究の核心は「延べ宿泊者数 = 人流の代理」という仮定にある。代理変数(Proxy Variable)を用いる場合、その妥当性を論じる必要がある。
宿泊者数は旅行・出張を伴う移動を捉えるが、通勤・買い物・外食など日常的な接触は含まれない。 つまり「観光型の人流」に偏った指標であり、都市部の日常接触密度を過小評価しうる。 理想的には携帯電話の位置情報データ(実際のモビリティ指標)との比較検証が望ましい。
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | fig2, ax2 = plt.subplots(figsize=(8, 7)) # 列ラベルを短くする label_map = { '宿泊密度': '宿泊密度\n(一人あたり)', '高齢化率': '高齢化率\n(%)', '宿泊施設客室数': '宿泊施設\n客室数', '交通通信費(万円)': '交通・\n通信費', '消費支出(万円)': '消費支出', '有効求人数(千)': '有効求人数', '地価住宅地': '住宅地地価', } cm_data = df_2019_clean[corr_cols].rename(columns=label_map) cm = cm_data.corr() im = ax2.imshow(cm.values, cmap='RdBu_r', vmin=-1, vmax=1, aspect='auto') plt.colorbar(im, ax=ax2, fraction=0.046, pad=0.04, label='相関係数') labels = list(cm.columns) ax2.set_xticks(range(len(labels))) ax2.set_yticks(range(len(labels))) ax2.set_xticklabels(labels, fontsize=9) ax2.set_yticklabels(labels, fontsize=9) for i in range(len(labels)): for j in range(len(labels)): v = cm.values[i, j] tc = 'white' if abs(v) > 0.6 else 'black' ax2.text(j, i, f'{v:.2f}', ha='center', va='center', fontsize=9, color=tc) ax2.set_title('相関ヒートマップ:宿泊密度と環境変数\n(2019年断面, 都道府県 N=47)', fontsize=12, fontweight='bold') plt.tight_layout() fig2.savefig(os.path.join(FIG_DIR, '2022_U5_6_fig2_corr.png'), bbox_inches='tight', dpi=150) plt.close(fig2) print("図2保存: 2022_U5_6_fig2_corr.png") |
図2保存: 2022_U5_6_fig2_corr.png
fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。plt.subplots(figsize=(W, H)) で図サイズ指定、fig.savefig(..., bbox_inches='tight') で余白を自動で詰めて保存。2019年断面(N=47都道府県)を用い、延べ宿泊密度を目的変数として OLS 重回帰を実施する。全変数を標準化(平均0・分散1)した上で回帰することで、各説明変数の相対的な重要度(Beta係数)を比較できる。
| 項目 | 設定 |
|---|---|
| 目的変数 | 延べ宿泊者数(人口一人あたり)[標準化] |
| 説明変数 | 高齢化率、宿泊施設客室数、交通通信費、消費支出、有効求人数、住宅地地価(全て標準化) |
| サンプル | 2019年断面、都道府県 N=47(欠損除外後) |
| 推定法 | OLS(最小二乗法) |
| 係数 | 標準化Beta係数(効果量比較のため) |
| 有意水準 | p<0.05(赤色)、n.s.(グレー) |
都道府県集計データで得られた回帰係数を「個人レベルの効果」として解釈してはならない。 これを生態学的誤謬(Ecological Fallacy)と呼ぶ。
例:「宿泊密度が高い都道府県で感染者が多い」からといって、 「宿泊した個人が感染しやすい」とは言えない。都道府県単位の集計は 地域内の個人差を均してしまうため、都道府県間差が個人差を反映しているとは限らない。
140 141 142 143 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 | fig3, ax3 = plt.subplots(figsize=(8, 5)) betas = model_std.params[1:] ses = model_std.bse[1:] pvals = model_std.pvalues[1:] xlabels = [XVAR_LABELS.get(v, v) for v in XVARS] colors3 = ['#C62828' if p < 0.05 else '#90A4AE' for p in pvals] y_pos = np.arange(len(XVARS)) ax3.barh(y_pos, betas, color=colors3, alpha=0.78, edgecolor='white', height=0.55) ax3.errorbar(betas, y_pos, xerr=1.96 * ses, fmt='none', color='#333', capsize=4, linewidth=1.3, zorder=5) ax3.axvline(0, color='gray', linestyle='--', linewidth=1.0) ax3.set_yticks(y_pos) ax3.set_yticklabels(xlabels, fontsize=10) ax3.set_xlabel('標準化回帰係数 Beta(±1.96SE)', fontsize=11) ax3.set_title(f'OLS標準化係数(2019年断面, N={N}都道府県)\n' f'R²={model_std.rsquared:.3f} 目的変数:延べ宿泊者数(人口あたり)', fontsize=12, fontweight='bold') ax3.invert_yaxis() ax3.grid(axis='x', alpha=0.3) sig_patch = mpatches.Patch(color='#C62828', alpha=0.78, label='p < 0.05') insig_patch = mpatches.Patch(color='#90A4AE', alpha=0.78, label='n.s.') ax3.legend(handles=[sig_patch, insig_patch], fontsize=9, loc='lower right') for i, (b, p) in enumerate(zip(betas, pvals)): sig = '***' if p < 0.001 else '**' if p < 0.01 else '*' if p < 0.05 else '' ax3.text(b + (0.01 if b >= 0 else -0.01), i, f' {b:.3f}{sig}', va='center', ha='left' if b >= 0 else 'right', fontsize=8.5) plt.tight_layout() fig3.savefig(os.path.join(FIG_DIR, '2022_U5_6_fig3_coef.png'), bbox_inches='tight', dpi=150) plt.close(fig3) print("図3保存: 2022_U5_6_fig3_coef.png") |
図3保存: 2022_U5_6_fig3_coef.png
fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。.dropna() は欠損行を除去、.copy() は独立したコピーを作る。pandasで警告を防ぐ定石。COVID前(2019年)からCOVID後(2020年)の延べ宿泊者数変化率を都道府県別に算出し、横棒グラフで可視化する。どの都道府県が最も大きな打撃を受け、どの地域が比較的耐性を示したかを把握する。
「緊急事態宣言の厳しい都道府県ほど宿泊者が減少した」という解釈は一見正しそうだが、 内生性(Endogeneity)の問題がある。感染者が多い地域に厳しい宣言が出され、 そういった地域はもともと宿泊密度も高いため、「宣言の効果」と「もともとの感染しやすい環境」が交絡する。
この問題を解くには、操作変数法(IV推定)や差の差分析(DiD)など、 より高度な因果推論の手法が必要になる。本研究の回帰分析は相関・関連性の把握に留まり、 因果を主張する段階ではないことを明示することが科学的誠実さにつながる。
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | import numpy as np import pandas as pd import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt import matplotlib.patches as mpatches 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_DIR = 'data/raw' DATA_B = os.path.join(DATA_DIR, '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} のように書式も指定できます。198 199 200 201 202 | ts = df_b.groupby('年度')['宿泊密度'].mean().reset_index() ts.columns = ['年度', '宿泊密度_平均'] print("\n■ 宿泊密度の年次推移(全国平均):") print(ts.to_string(index=False)) |
■ 宿泊密度の年次推移(全国平均): 年度 宿泊密度_平均 2012 3.175210 2013 3.389794 2014 3.468142 2015 3.657671 2016 3.644923 2017 3.737425 2018 3.892746 2019 4.193385 2020 2.451346 2021 2.377362 2022 3.264720 2023 4.115010
df.groupby('列').apply(関数) — グループごとに関数を適用。時系列や地域別の集計でよく使います。[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。203 204 205 206 | corr_cols = ['宿泊密度'] + XVARS corr_mat = df_2019_clean[corr_cols].corr() print("\n■ 相関行列(2019年断面):") print(corr_mat.round(3)) |
■ 相関行列(2019年断面):
宿泊密度 高齢化率 宿泊施設客室数 交通通信費(万円) 消費支出(万円) 有効求人数(千) 地価住宅地
宿泊密度 1.000 -0.287 0.261 -0.386 -0.375 -0.016 0.038
高齢化率 -0.287 1.000 -0.560 0.313 -0.129 -0.594 -0.653
宿泊施設客室数 0.261 -0.560 1.000 -0.311 0.137 0.914 0.767
交通通信費(万円) -0.386 0.313 -0.311 1.000 0.703 -0.283 -0.314
消費支出(万円) -0.375 -0.129 0.137 0.703 1.000 0.236 0.243
有効求人数(千) -0.016 -0.594 0.914 -0.283 0.236 1.000 0.883
地価住宅地 0.038 -0.653 0.767 -0.314 0.243 0.883 1.000df[col](1列)と df[[col1, col2]](複数列)でカッコの数が違います。リストを渡していると覚えるとミスを減らせます。207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | y_reg = df_2019_clean['宿泊密度'].values X_reg = df_2019_clean[XVARS].values # 標準化(Betaコefのため) y_std = (y_reg - y_reg.mean()) / y_reg.std() X_std = (X_reg - X_reg.mean(axis=0)) / X_reg.std(axis=0) model_std = sm.OLS(y_std, sm.add_constant(X_std)).fit() model_raw = sm.OLS(y_reg, sm.add_constant(X_reg)).fit() print("\n■ OLS重回帰(標準化係数、2019年断面):") print(f" R² = {model_std.rsquared:.4f}") print(f" {'変数':<20} {'Beta':>8} {'p値':>10} {'有意':>6}") print(" " + "-" * 50) for i, var in enumerate(XVARS): beta = model_std.params[i + 1] p = model_std.pvalues[i + 1] sig = '***' if p < 0.001 else '**' if p < 0.01 else '*' if p < 0.05 else 'n.s.' print(f" {var:<20} {beta:>8.4f} {p:>10.4f} {sig:>6}") |
■ OLS重回帰(標準化係数、2019年断面): R² = 0.6632 変数 Beta p値 有意 -------------------------------------------------- 高齢化率 -0.3213 0.0142 * 宿泊施設客室数 1.6031 0.0000 *** 交通通信費(万円) -0.1046 0.5783 n.s. 消費支出(万円) -0.1827 0.3211 n.s. 有効求人数(千) -1.9532 0.0000 *** 地価住宅地 0.3342 0.1425 n.s.
sm.add_constant(X) — 切片項(定数1の列)を先頭に追加。statsmodelsで必須。sm.OLS(y, X).fit() — 最小二乗法でモデルを推定。model.params, model.pvalues, model.conf_int() で結果取得。s[:-n]「末尾n文字を除く」/s[n:]「先頭n文字を除く」。スライス [start:stop:step] はリスト・タプル・文字列共通の基本ワザです。226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 | fig4, ax4 = plt.subplots(figsize=(9, 13)) colors4 = ['#C62828' if v < 0 else '#1565C0' for v in df_merge['変化率']] y_pos4 = np.arange(len(df_merge)) ax4.barh(y_pos4, df_merge['変化率'], color=colors4, alpha=0.75, edgecolor='white', height=0.75) ax4.axvline(0, color='black', linewidth=0.8) ax4.set_yticks(y_pos4) ax4.set_yticklabels(df_merge['都道府県'], fontsize=9) ax4.set_xlabel('変化率(%)', fontsize=11) ax4.set_title('延べ宿泊者数(人口あたり)の変化率:2019年→2020年\n(都道府県別, SSDSE-B実データ)', fontsize=12, fontweight='bold') ax4.grid(axis='x', alpha=0.3) dec_patch = mpatches.Patch(color='#C62828', alpha=0.75, label='減少(赤)') inc_patch = mpatches.Patch(color='#1565C0', alpha=0.75, label='増加(青)') ax4.legend(handles=[dec_patch, inc_patch], fontsize=9, loc='lower right') |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。f"...{x}..." はf-string。文字列の中に {変数} と書くだけで埋め込めて、{x:.2f} のように書式も指定できます。244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 | # 国全体平均線 nat_mean = df_merge['変化率'].mean() ax4.axvline(nat_mean, color='#FF8F00', linestyle='--', linewidth=1.5, label=f'全国平均 {nat_mean:.1f}%') ax4.legend(handles=[dec_patch, inc_patch, mpatches.Patch(color='#FF8F00', label=f'全国平均 {nat_mean:.1f}%')], fontsize=9, loc='lower right') plt.tight_layout() fig4.savefig(os.path.join(FIG_DIR, '2022_U5_6_fig4_change.png'), bbox_inches='tight', dpi=150) plt.close(fig4) print("図4保存: 2022_U5_6_fig4_change.png") print("\n全図の生成完了(4枚)") print(" 2022_U5_6_fig1_ts.png : 宿泊密度の時系列(COVID期シェード)") print(" 2022_U5_6_fig2_corr.png : 相関ヒートマップ") print(" 2022_U5_6_fig3_coef.png : OLS標準化係数プロット(2019年断面)") print(" 2022_U5_6_fig4_change.png : 都道府県別変化率 2019→2020(横棒)") |
図4保存: 2022_U5_6_fig4_change.png 全図の生成完了(4枚) 2022_U5_6_fig1_ts.png : 宿泊密度の時系列(COVID期シェード) 2022_U5_6_fig2_corr.png : 相関ヒートマップ 2022_U5_6_fig3_coef.png : OLS標準化係数プロット(2019年断面) 2022_U5_6_fig4_change.png : 都道府県別変化率 2019→2020(横棒)
ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。df['A'] / df['B'] — pandasの列同士の四則演算は要素ごと(element-wise)。forループ不要なのが強み。SSDSE-B の都道府県別パネルデータ(2012〜2023年)を用い、延べ宿泊者数(人口一人あたり)を人流の代理指標として COVID-19 の地域環境要因を分析した結果の概要:
| データ | 出典 |
|---|---|
| SSDSE-B-2026.csv(都道府県別パネルデータ) | 統計数理研究所 SSDSE(社会・人口統計体系) |
| 延べ宿泊者数・旅館営業施設客室数 | 観光庁 宿泊旅行統計調査(SSDSE経由) |
| 交通通信費・消費支出 | 総務省 家計調査(SSDSE経由) |
| 月間有効求人数 | 厚生労働省 職業安定業務統計(SSDSE経由) |
| 標準価格(住宅地) | 国土交通省 地価公示(SSDSE経由) |
分析スクリプトは SSDSE-B-2026.csv(実データ)を使用。データは統計数理研究所より公表されている実公的統計データ。
統計分析の解釈で初心者がやりがちな勘違いをまとめます。特に「相関と因果の混同」「p値の過信」は研究現場でもよく起きる落とし穴です。本文を読む前にも、読んだ後にも、目を通してみてください。
統計の基本用語を初心者向けに解説します。本文中で見慣れない言葉が出てきたら、ここに戻って確認してください。
統計手法について「何のためか」「結果をどう読むか」を初心者向けに解説します。
この研究をさらに発展させるための3つの方向性を示します。「今回わかったこと(X)」から「次に検証すべき仮説(Y)」を立て、「具体的に何をするか(Z)」まで考えてみましょう。
学んだだけでは身につきません。実際に手を動かすのが最強の学習方法です。本論文のスクリプトをベースに、以下のチャレンジに挑戦してみてください。難易度別に5つ用意しました。
本論文で学んだ手法は、研究の世界だけでなく、行政・企業・NPO の現場でも様々に活用されています。具体的なシーンを紹介します。
この論文を読んで初心者が抱きやすい疑問に、教育的観点から答えます。