このページの分析を自分で再現するには、以下の手順でデータを準備してください。コードの編集は不要です。
data/raw/ フォルダに入れます。html/figures/ に自動保存されます。
食の「外部化」とは、家庭内での調理(内食)から外食や中食(調理済み食品)への移行を指す。近年、共働き世帯の増加やライフスタイルの変化により、食の外部化が進んでいる。本研究は都道府県庁所在市のデータを用い、食の外部化の地域特性を統計的に分析した。
まず「食の外部化における地域特性」を統計的にとらえることが有効だと考えられる。 その理由は感覚や経験則だけでは、複雑な社会要因の中で「何が本当に効いているか」を見極めにくいからである。 本研究では公開データと統計手法を組み合わせ、この問いに定量的な答えを出すことを目指す。
家計調査 相関分析 地域タイプ分類 時系列分析
| データ | 出典 | 内容 |
|---|---|---|
| 家計調査(品目別) | 総務省 | 県庁所在市別の食費支出内訳 |
| 国勢調査 | 総務省 | 共働き世帯割合・世帯構成 |
| 住宅・土地統計調査 | 総務省 | 持ち家住宅の延べ面積 |
| 区分 | 変数名 | 説明 |
|---|---|---|
| 目的変数 | 外食支出割合(%) | 飲食店利用の割合 |
| 中食支出割合(%) | 調理食品・惣菜の割合 | |
| 内食支出割合(%) | 生鮮食品等素材の割合 | |
| 説明変数 | 共働き世帯割合(%) | 夫婦とも就業している世帯の割合 |
| エンゲル係数(%) | 消費支出に占める食費の割合 | |
| 帰宅から夕食までの時間(分) | 平均的な夕食準備時間 | |
| 持ち家住宅の延べ面積(m²) | 住宅規模の代理変数 |
1 2 3 4 5 6 7 8 9 | print("=" * 65) print("■ データ読み込み(SSDSE-B-2026 実公的データ)") print("=" * 65) df_raw = pd.read_csv(DATA_PATH, encoding='cp932', header=1) # 都道府県レベル行のみ抽出(地域コード = R + 5桁数字) df_b = df_raw[df_raw['地域コード'].str.match(r'^R\d{5}$', na=False)].copy() df_b = df_b.reset_index(drop=True) |
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ループ不要なのが強み。10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | # 必要列を数値変換 numeric_cols = [ '年度', '総人口', '65歳以上人口', '合計特殊出生率', '消費支出(二人以上の世帯)', '食料費(二人以上の世帯)', '教養娯楽費(二人以上の世帯)', '被服及び履物費(二人以上の世帯)', 'その他の消費支出(二人以上の世帯)', '旅館営業施設数(ホテルを含む)', '延べ宿泊者数', ] for col in numeric_cols: df_b[col] = pd.to_numeric(df_b[col], errors='coerce') print(f"読み込み完了: {len(df_b)} 行({df_b['年度'].nunique()}年 × {df_b['地域コード'].nunique()}都道府県)") print(f"対象年度: {sorted(df_b['年度'].unique())}") |
================================================================= ■ データ読み込み(SSDSE-B-2026 実公的データ) ================================================================= 読み込み完了: 564 行(12年 × 47都道府県) 対象年度: [np.int64(2012), np.int64(2013), np.int64(2014), np.int64(2015), np.int64(2016), np.int64(2017), np.int64(2018), np.int64(2019), np.int64(2020), np.int64(2021), np.int64(2022), np.int64(2023)]
.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | df_b['food_ratio'] = df_b['食料費(二人以上の世帯)'] / df_b['消費支出(二人以上の世帯)'] df_b['leisure_ratio'] = df_b['教養娯楽費(二人以上の世帯)'] / df_b['消費支出(二人以上の世帯)'] df_b['clothing_ratio'] = df_b['被服及び履物費(二人以上の世帯)'] / df_b['消費支出(二人以上の世帯)'] df_b['other_ratio'] = df_b['その他の消費支出(二人以上の世帯)'] / df_b['消費支出(二人以上の世帯)'] # ホテル密度(施設数 / 人口 × 10,000) df_b['hotel_per_pop'] = df_b['旅館営業施設数(ホテルを含む)'] / df_b['総人口'] * 10000 # 高齢化率 df_b['aging_rate'] = df_b['65歳以上人口'] / df_b['総人口'] # 2023年断面データ(最新年で都道府県比較) df_2023 = df_b[df_b['年度'] == 2023].copy().reset_index(drop=True) print(f"\n2023年断面データ: {len(df_2023)} 都道府県") print("\n記述統計(主要変数・2023年):") desc_cols = ['food_ratio', 'leisure_ratio', 'clothing_ratio', 'hotel_per_pop', 'aging_rate'] print(df_2023[desc_cols].describe().round(4)) |
2023年断面データ: 47 都道府県
記述統計(主要変数・2023年):
food_ratio leisure_ratio clothing_ratio hotel_per_pop aging_rate
count 47.0000 47.0000 47.0000 47.0000 47.0000
mean 0.2732 0.0923 0.0316 5.7211 0.3159
std 0.0176 0.0102 0.0041 3.9658 0.0334
min 0.2389 0.0701 0.0246 0.9467 0.2275
25% 0.2638 0.0852 0.0286 3.2354 0.3005
50% 0.2717 0.0934 0.0312 5.3118 0.3178
75% 0.2844 0.0985 0.0336 6.8473 0.3401
max 0.3182 0.1200 0.0417 22.7452 0.3906.describe() — 件数・平均・標準偏差・四分位・最大/最小を一括計算。データの素性チェックに必須。.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | print("\n" + "=" * 65) print("■ 相関分析(2023年 47都道府県)") print("=" * 65) corr_cols = ['food_ratio', 'leisure_ratio', 'clothing_ratio', 'hotel_per_pop', 'aging_rate', '合計特殊出生率'] corr_labels = ['食料費\n割合', '教養娯楽\n費割合', '被服費\n割合', 'ホテル\n密度', '高齢化率', '合計特殊\n出生率'] corr_mat = df_2023[corr_cols].corr() print("\n相関行列(2023年):") print(corr_mat.round(3)) r_aging_food, p_aging_food = stats.pearsonr(df_2023['aging_rate'], df_2023['food_ratio']) r_aging_leis, p_aging_leis = stats.pearsonr(df_2023['aging_rate'], df_2023['leisure_ratio']) print(f"\n高齢化率 × 食料費割合: r={r_aging_food:.3f}, p={p_aging_food:.4f}") print(f"高齢化率 × 教養娯楽費割合: r={r_aging_leis:.3f}, p={p_aging_leis:.4f}") |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。stats.pearsonr(x, y) — Pearson相関係数 r と p値を同時に返します。[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。64 65 66 67 68 69 70 71 72 73 74 75 76 | # 都市規模グループ分類(人口三分位) pop_q33 = df_2023['総人口'].quantile(1/3) pop_q67 = df_2023['総人口'].quantile(2/3) df_2023['urban_group'] = pd.cut( df_2023['総人口'], bins=[0, pop_q33, pop_q67, float('inf')], labels=['地方(人口少)', '中規模', '都市(人口多)'], include_lowest=True ) print("\n都市規模グループ別平均(2023年):") grp_stats = df_2023.groupby('urban_group', observed=True)[['food_ratio', 'leisure_ratio', 'aging_rate']].mean() print(grp_stats.round(4)) |
=================================================================
■ 相関分析(2023年 47都道府県)
=================================================================
相関行列(2023年):
food_ratio leisure_ratio ... aging_rate 合計特殊出生率
food_ratio 1.000 0.048 ... -0.230 -0.057
leisure_ratio 0.048 1.000 ... -0.503 -0.460
clothing_ratio 0.018 0.543 ... -0.397 -0.313
hotel_per_pop -0.183 -0.339 ... 0.062 0.401
aging_rate -0.230 -0.503 ... 1.000 0.201
合計特殊出生率 -0.057 -0.460 ... 0.201 1.000
[6 rows x 6 columns]
高齢化率 × 食料費割合: r=-0.230, p=0.1199
高齢化率 × 教養娯楽費割合: r=-0.503, p=0.0003
都市規模グループ別平均(2023年):
food_ratio leisure_ratio aging_rate
urban_group
地方(人口少) 0.2695 0.0889 0.3390
中規模 0.2730 0.0894 0.3173
都市(人口多) 0.2771 0.0982 0.2914df.groupby('列').apply(関数) — グループごとに関数を適用。時系列や地域別の集計でよく使います。r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。外食・中食・内食支出割合の相互関係と、生活変数との相関を分析する。3区分の食支出割合は合計100%の制約があるため、一方が上がれば他方が下がる負の相関関係が生まれる。
外食・中食・内食は合計100%の「構成比データ」。一方が増えると他方が必ず減るため、3変数間には必然的に負の相関が生まれる。この「閉じたデータ(Compositional Data)」を扱う際は相関係数の解釈に注意が必要。
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | print("図1: 相関行列ヒートマップを作成中...") fig1, axes1 = plt.subplots(1, 2, figsize=(14, 6)) fig1.suptitle('家計消費費目割合と地域変数の相関分析(2023年 47都道府県)', fontsize=13, fontweight='bold') # 左: ヒートマップ ax1a = axes1[0] mat = corr_mat.values im = ax1a.imshow(mat, cmap='RdBu_r', vmin=-1, vmax=1, aspect='auto') plt.colorbar(im, ax=ax1a, shrink=0.8, label='Pearson r') ax1a.set_xticks(range(len(corr_cols))) ax1a.set_yticks(range(len(corr_cols))) ax1a.set_xticklabels(corr_labels, fontsize=9) ax1a.set_yticklabels(corr_labels, fontsize=9) for i in range(len(corr_cols)): for j in range(len(corr_cols)): v = mat[i, j] ax1a.text(j, i, f'{v:.2f}', ha='center', va='center', fontsize=9, color='white' if abs(v) > 0.6 else 'black', fontweight='bold') ax1a.set_title('相関行列(消費費目割合 × 地域変数)', fontsize=10, fontweight='bold') |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。x if cond else y は三項演算子。リスト内包表記と組み合わせると、forとifを1行で書けます。100 101 102 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 | # 右: 教養娯楽費割合との相関棒グラフ ax1b = axes1[1] target_vars = ['food_ratio', 'clothing_ratio', 'hotel_per_pop', 'aging_rate', '合計特殊出生率'] target_labels2 = ['食料費割合', '被服費割合', 'ホテル密度\n(施設/万人)', '高齢化率', '合計特殊出生率'] corr_vals = [df_2023['leisure_ratio'].corr(df_2023[v]) for v in target_vars] bar_cols = ['#E53935' if v < 0 else '#1565C0' for v in corr_vals] sorted_idx = sorted(range(len(corr_vals)), key=lambda i: corr_vals[i]) ax1b.barh( range(len(target_vars)), [corr_vals[i] for i in sorted_idx], color=[bar_cols[i] for i in sorted_idx], alpha=0.85, edgecolor='white' ) ax1b.set_yticks(range(len(target_vars))) ax1b.set_yticklabels([target_labels2[i] for i in sorted_idx], fontsize=10) ax1b.axvline(0, color='black', linewidth=1.0) ax1b.set_xlabel('Pearson 相関係数(教養娯楽費割合との相関)', fontsize=10) ax1b.set_title('教養娯楽費割合との相関係数\n(高齢化率との強い負の相関)', fontsize=10, fontweight='bold') ax1b.grid(axis='x', alpha=0.3) for i, idx in enumerate(sorted_idx): v = corr_vals[idx] r_val, p_val = stats.pearsonr(df_2023['leisure_ratio'], df_2023[target_vars[idx]]) sig = '***' if p_val < 0.001 else '**' if p_val < 0.01 else '*' if p_val < 0.05 else '' if sig: offset = 0.02 if v > 0 else -0.02 ha = 'left' if v > 0 else 'right' ax1b.text(v + offset, i, sig, va='center', ha=ha, fontsize=10, fontweight='bold') plt.tight_layout() fig1.savefig(os.path.join(FIG_DIR, '2024_H1_fig1_corr.png'), bbox_inches='tight', dpi=150) plt.close(fig1) print(" → 2024_H1_fig1_corr.png 保存完了") |
図1: 相関行列ヒートマップを作成中... → 2024_H1_fig1_corr.png 保存完了
ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。stats.pearsonr(x, y) — Pearson相関係数 r と p値を同時に返します。df[col](1列)と df[[col1, col2]](複数列)でカッコの数が違います。リストを渡していると覚えるとミスを減らせます。都市規模(都市部/地方)と食支出の特性(外食高・中食高・内食高・バランス型)を組み合わせ、47都市を7つのタイプに分類する。
| タイプ | 特徴 | 代表都市 |
|---|---|---|
| 地方内食高 | 地方で自炊文化が根付く | 秋田・鳥取・島根 |
| 地方中食高 | 地方の共働き率高い地域 | 富山・福井・山形 |
| 地方バランス | 食支出が平均的 | 多数の地方都市 |
| 都心内食高 | 都市でも自炊志向 | 一部の政令市 |
| 都心外食高 | 外食が盛ん | 東京・大阪 |
| 都心中食高 | 都市の共働き中食型 | 名古屋・横浜 |
| 都心バランス | 均衡型の都市 | 京都・神戸 |
クラスタリングアルゴリズム(k-means等)を使わなくても、散布図の視覚的な探索から意味のある「タイプ分け」が可能。ただし、タイプ数(7)や境界値の設定は分析者の判断が入るため、設定根拠を明示することが重要。
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 161 162 163 164 165 166 167 168 169 170 171 | print("図2: 散布図(高齢化率 vs 食料費割合)を作成中...") fig2, axes2 = plt.subplots(1, 2, figsize=(14, 6)) fig2.suptitle('高齢化率と家計消費費目割合の関係(2023年 47都道府県)', fontsize=13, fontweight='bold') # 左: 高齢化率 vs 食料費割合 ax2a = axes2[0] group_colors_map = {'地方(人口少)': '#1565C0', '中規模': '#43A047', '都市(人口多)': '#E65100'} colors2 = [group_colors_map.get(str(g), '#888888') for g in df_2023['urban_group']] ax2a.scatter(df_2023['aging_rate'] * 100, df_2023['food_ratio'] * 100, c=colors2, s=80, alpha=0.85, edgecolors='white', linewidth=0.5, zorder=3) # 回帰直線 x2a = df_2023['aging_rate'].values y2a = df_2023['food_ratio'].values sl2a, ic2a, rv2a, pv2a, _ = stats.linregress(x2a, y2a) x2a_line = [x2a.min(), x2a.max()] y2a_line = [ic2a + sl2a * x for x in x2a_line] ax2a.plot([v * 100 for v in x2a_line], [v * 100 for v in y2a_line], '--', color='#333333', linewidth=2, label=f'回帰直線 r={rv2a:.3f}{"*" if pv2a < 0.05 else ""}') # 注目都道府県ラベル highlight2 = ['秋田県', '東京都', '神奈川県', '愛媛県', '沖縄県'] for _, row in df_2023[df_2023['都道府県'].isin(highlight2)].iterrows(): ax2a.annotate(row['都道府県'], (row['aging_rate'] * 100, row['food_ratio'] * 100), fontsize=8.5, fontweight='bold', color='#333333', xytext=(5, 4), textcoords='offset points') ax2a.set_xlabel('高齢化率(65歳以上人口 / 総人口 × 100, %)', fontsize=10) ax2a.set_ylabel('食料費割合(食料費 / 消費支出 × 100, %)', fontsize=10) ax2a.set_title('高齢化率 vs 食料費割合\n(r={:.3f}, p={:.4f})'.format(rv2a, pv2a), fontsize=10, fontweight='bold') patches2a = [mpatches.Patch(color=c, alpha=0.85, label=g) for g, c in group_colors_map.items()] ax2a.legend(handles=patches2a + [plt.Line2D([0],[0], color='#333333', linestyle='--', label=f'回帰直線 r={rv2a:.3f}')], fontsize=8.5) ax2a.grid(True, alpha=0.3) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。stats.linregress(x, y) — 単回帰の傾き・切片・r値・p値・標準誤差を返します。使わない値は _ で受け取り。for _, row in df.iterrows() — DataFrameを1行ずつ取り出すループ。1点ずつ描画したいときに使用。df[col](1列)と df[[col1, col2]](複数列)でカッコの数が違います。リストを渡していると覚えるとミスを減らせます。172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 | # 右: 高齢化率 vs 教養娯楽費割合 ax2b = axes2[1] ax2b.scatter(df_2023['aging_rate'] * 100, df_2023['leisure_ratio'] * 100, c=colors2, s=80, alpha=0.85, edgecolors='white', linewidth=0.5, zorder=3) x2b = df_2023['aging_rate'].values y2b = df_2023['leisure_ratio'].values sl2b, ic2b, rv2b, pv2b, _ = stats.linregress(x2b, y2b) x2b_line = [x2b.min(), x2b.max()] y2b_line = [ic2b + sl2b * x for x in x2b_line] ax2b.plot([v * 100 for v in x2b_line], [v * 100 for v in y2b_line], '--', color='#333333', linewidth=2, label=f'回帰直線 r={rv2b:.3f}') highlight2b = ['秋田県', '神奈川県', '東京都', '長崎県', '沖縄県'] for _, row in df_2023[df_2023['都道府県'].isin(highlight2b)].iterrows(): ax2b.annotate(row['都道府県'], (row['aging_rate'] * 100, row['leisure_ratio'] * 100), fontsize=8.5, fontweight='bold', color='#333333', xytext=(5, 4), textcoords='offset points') ax2b.set_xlabel('高齢化率(65歳以上人口 / 総人口 × 100, %)', fontsize=10) ax2b.set_ylabel('教養娯楽費割合(教養娯楽費 / 消費支出 × 100, %)', fontsize=10) ax2b.set_title('高齢化率 vs 教養娯楽費割合\n(r={:.3f}, p={:.4f})'.format(rv2b, pv2b), fontsize=10, fontweight='bold') ax2b.legend(handles=patches2a + [plt.Line2D([0],[0], color='#333333', linestyle='--', label=f'回帰直線 r={rv2b:.3f}')], fontsize=8.5) ax2b.grid(True, alpha=0.3) plt.tight_layout() fig2.savefig(os.path.join(FIG_DIR, '2024_H1_fig2_scatter.png'), bbox_inches='tight', dpi=150) plt.close(fig2) print(" → 2024_H1_fig2_scatter.png 保存完了") |
図2: 散布図(高齢化率 vs 食料費割合)を作成中... → 2024_H1_fig2_scatter.png 保存完了
stats.linregress(x, y) — 単回帰の傾き・切片・r値・p値・標準誤差を返します。使わない値は _ で受け取り。for _, row in df.iterrows() — DataFrameを1行ずつ取り出すループ。1点ずつ描画したいときに使用。s[:-n]「末尾n文字を除く」/s[n:]「先頭n文字を除く」。スライス [start:stop:step] はリスト・タプル・文字列共通の基本ワザです。2020年のコロナ禍は「外食から中食へ」の大きなシフトをもたらした。このシフトが持続的かどうか、地域差があるかを時系列で検証する。
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 | import pandas as pd import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt import matplotlib.patches as mpatches 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 DATA_PATH = 'data/raw/SSDSE-B-2026.csv' FIG_DIR = 'html/figures' 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} のように書式も指定できます。221 222 223 | print("\n" + "=" * 65) print("■ 図の生成(4枚)") print("=" * 65) |
================================================================= ■ 図の生成(4枚) =================================================================
r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。224 225 226 227 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 260 261 262 263 264 265 266 | print("図3: 都市規模別の箱ひげ図を作成中...") GROUP_ORDER = ['地方(人口少)', '中規模', '都市(人口多)'] GROUP_COLORS = {'地方(人口少)': '#1565C0', '中規模': '#43A047', '都市(人口多)': '#E65100'} fig3, axes3 = plt.subplots(1, 2, figsize=(14, 6)) fig3.suptitle('都市規模グループ別 消費費目割合の分布比較(2023年 47都道府県)', fontsize=13, fontweight='bold') def boxplot_group(ax, target_col, ylabel, title): data_by_group = [ df_2023[df_2023['urban_group'] == g][target_col].dropna().values * 100 for g in GROUP_ORDER ] bp = ax.boxplot(data_by_group, patch_artist=True, notch=False, medianprops=dict(color='white', linewidth=2.5), whiskerprops=dict(linewidth=1.5), capprops=dict(linewidth=1.5)) for patch, g in zip(bp['boxes'], GROUP_ORDER): patch.set_facecolor(GROUP_COLORS[g]) patch.set_alpha(0.8) ax.set_xticklabels(GROUP_ORDER, fontsize=10) ax.set_ylabel(ylabel, fontsize=10) ax.set_title(title, fontsize=10, fontweight='bold') ax.grid(axis='y', alpha=0.3) # 個別データ点(ジッター代わりに等間隔描画) for k, (g, data) in enumerate(zip(GROUP_ORDER, data_by_group)): n = len(data) xs = [k + 1 + (i - n/2) * 0.03 for i in range(n)] ax.scatter(xs, data, color=GROUP_COLORS[g], s=30, alpha=0.6, edgecolors='white', linewidth=0.4, zorder=4) # 平均値マーク for k, data in enumerate(data_by_group): if len(data) > 0: ax.scatter(k + 1, data.mean(), marker='D', color='gold', s=60, zorder=5, edgecolors='#333', linewidth=0.8) boxplot_group(axes3[0], 'food_ratio', '食料費割合(食料費 / 消費支出 × 100, %)', '都市規模グループ別 食料費割合の分布\n(◆=平均値、箱内白線=中央値)') boxplot_group(axes3[1], 'leisure_ratio', '教養娯楽費割合(教養娯楽費 / 消費支出 × 100, %)', '都市規模グループ別 教養娯楽費割合の分布\n(都市部で高い外食・娯楽志向)') |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。s[:-n]「末尾n文字を除く」/s[n:]「先頭n文字を除く」。スライス [start:stop:step] はリスト・タプル・文字列共通の基本ワザです。267 268 269 270 271 272 273 274 275 276 277 278 | # 統計量を出力 print("\n都市規模グループ別 記述統計(%換算):") for col, name in [('food_ratio', '食料費割合'), ('leisure_ratio', '教養娯楽費割合')]: print(f"\n {name}:") for g in GROUP_ORDER: vals = df_2023[df_2023['urban_group'] == g][col].dropna() * 100 print(f" {g}: mean={vals.mean():.2f}%, median={vals.median():.2f}%, n={len(vals)}") plt.tight_layout() fig3.savefig(os.path.join(FIG_DIR, '2024_H1_fig3_type.png'), bbox_inches='tight', dpi=150) plt.close(fig3) print(" → 2024_H1_fig3_type.png 保存完了") |
図3: 都市規模別の箱ひげ図を作成中...
都市規模グループ別 記述統計(%換算):
食料費割合:
地方(人口少): mean=26.95%, median=27.03%, n=16
中規模: mean=27.30%, median=27.10%, n=15
都市(人口多): mean=27.71%, median=27.45%, n=16
教養娯楽費割合:
地方(人口少): mean=8.89%, median=8.74%, n=16
中規模: mean=8.94%, median=9.36%, n=15
都市(人口多): mean=9.82%, median=9.73%, n=16
→ 2024_H1_fig3_type.png 保存完了np.cumsum(arr) は累積和、np.linspace(a, b, n) は「aからbを等間隔でn個」。NumPyの定石です。279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 | print("図4: 時系列トレンドを作成中...") # 年別全国平均の算出 yearly_avg = df_b.groupby('年度')[['food_ratio', 'leisure_ratio', 'clothing_ratio']].mean() years_ts = yearly_avg.index.tolist() # 都市規模別の時系列:最新年(2023)の都市規模分類を全年に適用 urban_group_map = dict(zip(df_2023['都道府県'], df_2023['urban_group'])) df_b['urban_group_fixed'] = df_b['都道府県'].map(urban_group_map) yearly_urban = df_b.groupby(['年度', 'urban_group_fixed'], observed=True)[ ['food_ratio', 'leisure_ratio'] ].mean().reset_index() fig4, axes4 = plt.subplots(1, 2, figsize=(14, 6)) fig4.suptitle('食料費・教養娯楽費割合の推移(2012〜2023年)', fontsize=13, fontweight='bold') |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。df.groupby('列').apply(関数) — グループごとに関数を適用。時系列や地域別の集計でよく使います。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。np.cumsum(arr) は累積和、np.linspace(a, b, n) は「aからbを等間隔でn個」。NumPyの定石です。294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 | # 左: 全国平均の食料費・教養娯楽費・被服費割合 ax4a = axes4[0] ax4a.plot(years_ts, yearly_avg['food_ratio'] * 100, 'o-', color='#E53935', linewidth=2.5, markersize=7, label='食料費割合(内食志向)') ax4a.plot(years_ts, yearly_avg['leisure_ratio'] * 100, 's-', color='#1565C0', linewidth=2.5, markersize=7, label='教養娯楽費割合(外食・娯楽)') ax4a.plot(years_ts, yearly_avg['clothing_ratio'] * 100, '^-', color='#43A047', linewidth=2.0, markersize=6, label='被服費割合') ax4a.axvspan(2020, 2021.5, alpha=0.12, color='gray') ax4a.axvline(2020, color='gray', linestyle='--', linewidth=1.5, label='COVID-19 拡大(2020年)') ax4a.set_xlabel('年度', fontsize=11) ax4a.set_ylabel('割合(費目 / 消費支出 × 100, %)', fontsize=10) ax4a.set_title('全国平均:消費費目割合の推移\n(コロナ禍で食料費↑、教養娯楽費↓)', fontsize=10, fontweight='bold') ax4a.legend(fontsize=9) ax4a.grid(True, alpha=0.3) ax4a.set_xticks(years_ts) ax4a.set_xticklabels([str(y) for y in years_ts], rotation=45, fontsize=8) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。{値:.2f}(小数2桁)、{値:,}(3桁区切り)、{値:>10}(右寄せ10桁)など、覚えると出力が一気に整います。312 313 314 315 316 317 318 319 320 321 322 323 324 | # 2019→2020の変化をアノテーション food_2019 = yearly_avg.loc[2019, 'food_ratio'] * 100 food_2020 = yearly_avg.loc[2020, 'food_ratio'] * 100 leis_2019 = yearly_avg.loc[2019, 'leisure_ratio'] * 100 leis_2020 = yearly_avg.loc[2020, 'leisure_ratio'] * 100 ax4a.annotate(f'食料費\n+{food_2020 - food_2019:.1f}%pt', xy=(2020, food_2020), xytext=(2020.3, food_2020 + 0.5), fontsize=8, color='#E53935', arrowprops=dict(arrowstyle='->', color='#E53935')) ax4a.annotate(f'教養娯楽費\n{leis_2020 - leis_2019:+.1f}%pt', xy=(2020, leis_2020), xytext=(2018.5, leis_2020 - 0.5), fontsize=8, color='#1565C0', arrowprops=dict(arrowstyle='->', color='#1565C0')) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。plt.subplots(figsize=(W, H)) で図サイズ指定、fig.savefig(..., bbox_inches='tight') で余白を自動で詰めて保存。325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 | # 右: 都市規模別の教養娯楽費割合の推移 ax4b = axes4[1] style_map = { '地方(人口少)': ('-', '#1565C0', 'o', '地方(人口少)'), '中規模': ('--', '#43A047', 's', '中規模'), '都市(人口多)': ('-', '#E65100', '^', '都市(人口多)'), } for grp, (ls, col, mk, lbl) in style_map.items(): grp_data = yearly_urban[yearly_urban['urban_group_fixed'] == grp].sort_values('年度') ax4b.plot(grp_data['年度'], grp_data['leisure_ratio'] * 100, linestyle=ls, color=col, marker=mk, markersize=6, linewidth=2.0, label=lbl, alpha=0.9) ax4b.axvspan(2020, 2021.5, alpha=0.12, color='gray') ax4b.axvline(2020, color='gray', linestyle='--', linewidth=1.5, label='COVID-19 拡大') ax4b.set_xlabel('年度', fontsize=11) ax4b.set_ylabel('教養娯楽費割合(%)', fontsize=10) ax4b.set_title('都市規模別 教養娯楽費割合の推移\n(都市部でコロナ前後の変化が大きい)', fontsize=10, fontweight='bold') ax4b.legend(fontsize=9) ax4b.grid(True, alpha=0.3) ax4b.set_xticks(years_ts) ax4b.set_xticklabels([str(y) for y in years_ts], rotation=45, fontsize=8) plt.tight_layout() fig4.savefig(os.path.join(FIG_DIR, '2024_H1_fig4_trend.png'), bbox_inches='tight', dpi=150) plt.close(fig4) print(" → 2024_H1_fig4_trend.png 保存完了") print("\n" + "=" * 65) print("全図の生成完了(4枚)") print("=" * 65) print(f"\n保存先: {FIG_DIR}") print(" 2024_H1_fig1_corr.png - 相関行列と教養娯楽費割合との相関係数") print(" 2024_H1_fig2_scatter.png - 高齢化率 vs 食料費・教養娯楽費割合 散布図") print(" 2024_H1_fig3_type.png - 都市規模グループ別 費目割合の箱ひげ図") print(" 2024_H1_fig4_trend.png - 食料費・教養娯楽費割合の時系列推移") print(f"\nデータ出典: SSDSE-B-2026(政府統計の総合窓口 e-Stat / 統計数理研究所)") |
図4: 時系列トレンドを作成中... → 2024_H1_fig4_trend.png 保存完了 ================================================================= 全図の生成完了(4枚) ================================================================= 保存先: html/figures 2024_H1_fig1_corr.png - 相関行列と教養娯楽費割合との相関係数 2024_H1_fig2_scatter.png - 高齢化率 vs 食料費・教養娯楽費割合 散布図 2024_H1_fig3_type.png - 都市規模グループ別 費目割合の箱ひげ図 2024_H1_fig4_trend.png - 食料費・教養娯楽費割合の時系列推移 データ出典: SSDSE-B-2026(政府統計の総合窓口 e-Stat / 統計数理研究所)
sort_values('列名', ascending=False) — 指定列で並べ替え(降順)。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。.dropna() は欠損行を除去、.copy() は独立したコピーを作る。pandasで警告を防ぐ定石。| データ | 出典 |
|---|---|
| 家計調査(品目別・県庁所在市) | 総務省統計局 |
| 国勢調査(共働き世帯割合等) | 総務省統計局 |
| 住宅・土地統計調査 | 総務省統計局 |
本教育用コードは合成データを使用(np.random.seed(42))。実際の分析は総務省の実データによる。
統計分析の解釈で初心者がやりがちな勘違いをまとめます。特に「相関と因果の混同」「p値の過信」は研究現場でもよく起きる落とし穴です。本文を読む前にも、読んだ後にも、目を通してみてください。
統計の基本用語を初心者向けに解説します。本文中で見慣れない言葉が出てきたら、ここに戻って確認してください。
統計手法について「何のためか」「結果をどう読むか」を初心者向けに解説します。
この研究をさらに発展させるための3つの方向性を示します。「今回わかったこと(X)」から「次に検証すべき仮説(Y)」を立て、「具体的に何をするか(Z)」まで考えてみましょう。
学んだだけでは身につきません。実際に手を動かすのが最強の学習方法です。本論文のスクリプトをベースに、以下のチャレンジに挑戦してみてください。難易度別に5つ用意しました。
本論文で学んだ手法は、研究の世界だけでなく、行政・企業・NPO の現場でも様々に活用されています。具体的なシーンを紹介します。
この論文を読んで初心者が抱きやすい疑問に、教育的観点から答えます。