このページの分析を自分で再現するには、以下の手順でデータを準備してください。コードの編集は不要です。
data/raw/ フォルダに入れます。html/figures/ に自動保存されます。
日本の食料自給率(カロリーベース)は2021年度に38%と先進国最低水準にある。食料安全保障の観点から、農業生産基盤の強化は国の重要政策課題であるが、農業従事者の高齢化・後継者不足・農地の減少という構造問題が深刻化している。
まず「都道府県別農業生産と食料自給率の決定要因分析」を統計的にとらえることが有効だと考えられる。 その理由は感覚や経験則だけでは、複雑な社会要因の中で「何が本当に効いているか」を見極めにくいからである。 本研究では公開データと統計手法を組み合わせ、この問いに定量的な答えを出すことを目指す。
本研究は、SSDSE(社会・人口統計体系データセット)を用いて、47都道府県の農業生産集積の地域差とその決定要因を統計的に分析する。直接の農業就業者数データは時系列で入手できないため、農業センサスの農家密度・耕地率などを代理指標として活用する。
SSDSE-B・E 重回帰分析 相関分析 時系列分析
| データ | 出典 | 年度 | 使用変数 |
|---|---|---|---|
| SSDSE-B-2026 | 統計数理研究所 SSDSE | 2012〜2022年 | 総人口・65歳以上人口・気温・消費支出等 |
| SSDSE-E-2026 | 統計数理研究所 SSDSE | 2019/2024年 | 農家数(販売農家)・耕地面積・可住地面積・総面積 |
※ SSDSE-Bには直接の農業就業者数データが含まれないため、SSDSE-Eの農業センサスデータを代理指標として活用する(代理変数設計はDS LEARNING POINT参照)。
| 変数名 | 定義 | 単位 | 農業との関係 |
|---|---|---|---|
| 農家密度(目的変数) | 販売農家数 ÷ 可住地面積 | 戸/km² | 農業生産集積の主指標 |
| 耕地率 | 耕地面積 ÷ 総面積 × 100 | % | 農地の広がり(正の効果) |
| 高齢化率 | 65歳以上人口 ÷ 総人口 × 100 | % | 農業従事者高齢化の代理 |
| 人口密度 | 総人口 ÷ 総面積 | 人/km² | 都市化度(高いほど農業は縮小) |
| 食料費比率 | 食料費 ÷ 消費支出 × 100 | % | 食生活の地域特性 |
| 年平均気温 | SSDSE-B 年平均気温 | ℃ | 農業適地の気候条件 |
| 変数 | 平均 | 標準偏差 | 最小 | 最大 |
|---|---|---|---|---|
| 農家密度(戸/km²) | 10.00 | 2.84 | 1.42(北海道) | 15.86(香川県) |
| 耕地率(%) | 10.88 | 5.16 | 2.77 | 25.96 |
| 高齢化率(%) | 31.35 | 3.27 | 22.81(東京都) | 38.60(秋田県) |
| 人口密度(人/km²) | 652 | 1,219 | 65.6 | 6,381(東京都) |
| 食料費比率(%) | 26.47 | 1.39 | 23.30 | 30.51 |
| 年平均気温(℃) | 16.07 | 2.29 | 10.20 | 23.70 |
理想的な説明変数が入手できない場合、「理論的に関連する」別の変数で代替する。この変数を代理変数(proxy variable)という。
本研究では農業就業者の時系列データが入手できないため、農家密度(農業センサス、5年ごと)・高齢化率(人口統計から計算)を代理変数として活用した。代理変数使用時は「理論的根拠」と「測定誤差の方向」を明示することが重要。
1 2 3 4 5 6 7 8 9 10 11 | plt.rcParams['font.family'] = 'Hiragino Sans' plt.rcParams['axes.unicode_minus'] = False plt.rcParams['figure.dpi'] = 150 PREFIX = '2021_H3' def save_fig(n): path = os.path.join(FIG_DIR, f'{PREFIX}_fig{n}.png') plt.savefig(path, bbox_inches='tight', dpi=150) plt.close() print(f' → {path} 保存完了') |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。plt.rcParams['font.family'] — グラフの日本語表示用フォント指定(Macは Hiragino Sans、Windowsなら Yu Gothic 等)。fig.savefig(..., bbox_inches='tight') — 余白を自動で詰めて保存。plt.close() でメモリ解放。df['A'] / df['B'] — pandasの列同士の四則演算は要素ごと(element-wise)。forループ不要なのが強み。12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | region_map = { '北海道': '北海道・東北', '青森県': '北海道・東北', '岩手県': '北海道・東北', '宮城県': '北海道・東北', '秋田県': '北海道・東北', '山形県': '北海道・東北', '福島県': '北海道・東北', '茨城県': '関東', '栃木県': '関東', '群馬県': '関東', '埼玉県': '関東', '千葉県': '関東', '東京都': '関東', '神奈川県': '関東', '新潟県': '中部', '富山県': '中部', '石川県': '中部', '福井県': '中部', '山梨県': '中部', '長野県': '中部', '岐阜県': '中部', '静岡県': '中部', '愛知県': '中部', '三重県': '近畿', '滋賀県': '近畿', '京都府': '近畿', '大阪府': '近畿', '兵庫県': '近畿', '奈良県': '近畿', '和歌山県': '近畿', '鳥取県': '中国・四国', '島根県': '中国・四国', '岡山県': '中国・四国', '広島県': '中国・四国', '山口県': '中国・四国', '徳島県': '中国・四国', '香川県': '中国・四国', '愛媛県': '中国・四国', '高知県': '中国・四国', '福岡県': '九州・沖縄', '佐賀県': '九州・沖縄', '長崎県': '九州・沖縄', '熊本県': '九州・沖縄', '大分県': '九州・沖縄', '宮崎県': '九州・沖縄', '鹿児島県': '九州・沖縄', '沖縄県': '九州・沖縄' } region_colors = { '北海道・東北': '#4e9af1', '関東': '#e05c5c', '中部': '#f0a500', '近畿': '#5cb85c', '中国・四国': '#9b59b6', '九州・沖縄': '#f39c12' } REGIONS = list(region_colors.keys()) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | print('=== データ読み込み ===') 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) # 数値列を変換 num_cols = ['総人口', '65歳以上人口', '15歳未満人口', '15~64歳人口', '年平均気温', '消費支出(二人以上の世帯)', '食料費(二人以上の世帯)', '一般旅券発行件数'] for c in num_cols: if c in df_b.columns: df_b[c] = pd.to_numeric(df_b[c], errors='coerce') # 高齢化率を計算 df_b['高齢化率'] = df_b['65歳以上人口'] / df_b['総人口'] * 100 print(f' SSDSE-B: {df_b.shape[0]}行 × {df_b.shape[1]}列') print(f' 年度: {sorted(df_b["年度"].unique())}') |
=== データ読み込み === SSDSE-B: 564行 × 113列 年度: [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)]
pd.read_csv(...) でCSVを読み込みます。encoding='cp932' は日本語Windows由来の文字コード、header=1 は「2行目を列名として使う」。df['地域コード'].str.match(r'^R\d{5}', ...) — 正規表現で「R+数字5桁」の行(47都道府県)だけTrueにし、真偽値で行をフィルタ。.astype(int) — 列を整数に変換(年度などを数値比較するため)。[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | df_e_raw = pd.read_csv(DATA_E, encoding='cp932', header=None) col_names = df_e_raw.iloc[2].tolist() df_e = df_e_raw.iloc[3:].copy() df_e.columns = col_names df_e = df_e[df_e['地域コード'].astype(str).str.match(r'^R\d{5}$', na=False)].copy() df_e = df_e[df_e['地域コード'] != 'R00000'].copy() df_e = df_e.reset_index(drop=True) # 数値変換 for c in ['農家数(販売農家)', '農家数(自給的農家)', '耕地面積', '総面積(北方地域及び竹島を除く)', '可住地面積', '総人口', '65歳以上人口', '県内総生産額(平成27年基準)', '従業者数(民営)']: if c in df_e.columns: df_e[c] = pd.to_numeric(df_e[c], errors='coerce') print(f' SSDSE-E: {df_e.shape[0]}行 × {df_e.shape[1]}列') |
SSDSE-E: 47行 × 95列
pd.read_csv(...) でCSVを読み込みます。encoding='cp932' は日本語Windows由来の文字コード、header=1 は「2行目を列名として使う」。df['地域コード'].str.match(r'^R\d{5}', ...) — 正規表現で「R+数字5桁」の行(47都道府県)だけTrueにし、真偽値で行をフィルタ。r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | df2022 = df_b[df_b['年度'] == 2022].copy().reset_index(drop=True) # 食料費比率(食料費/消費支出) df2022['食料費比率'] = pd.to_numeric(df2022['食料費(二人以上の世帯)'], errors='coerce') / \ pd.to_numeric(df2022['消費支出(二人以上の世帯)'], errors='coerce') * 100 # 地域コードの末尾3桁でマッチング # SSDSE-B: R01000, SSDSE-E: R01000 → 同形式 df_merge = df2022.merge( df_e[['地域コード', '農家数(販売農家)', '農家数(自給的農家)', '耕地面積', '総面積(北方地域及び竹島を除く)', '可住地面積']], on='地域コード', how='inner' ) print(f' マージ後: {df_merge.shape[0]}都道府県') |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。x if cond else y は三項演算子。リスト内包表記と組み合わせると、forとifを1行で書けます。87 88 89 90 91 92 93 94 | # ── 農業関連指標の計算 ── # 農家密度(販売農家数 / 可住地面積 [ha → km²=ha/100], 戸/km²) # 可住地面積はha単位 df_merge['農家密度'] = df_merge['農家数(販売農家)'] / (df_merge['可住地面積'] / 100) # 耕地率(耕地面積[ha] / 総面積[ha] × 100) # 総面積はha単位 df_merge['耕地率'] = df_merge['耕地面積'] / df_merge['総面積(北方地域及び竹島を除く)'] * 100 |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。df[col](1列)と df[[col1, col2]](複数列)でカッコの数が違います。リストを渡していると覚えるとミスを減らせます。95 96 97 98 99 100 | # 人口密度(人/km²):総面積はha → km²=÷100 df_merge['人口密度'] = df_merge['総人口'] / (df_merge['総面積(北方地域及び竹島を除く)'] / 100) # 地域列を付与 df_merge['地域'] = df_merge['都道府県'].map(region_map) df_e['地域'] = df_e['都道府県'].map(region_map) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。s[:-n]「末尾n文字を除く」/s[n:]「先頭n文字を除く」。スライス [start:stop:step] はリスト・タプル・文字列共通の基本ワザです。101 102 103 104 105 106 107 | # 分析用データ確認 print('\n=== 断面データ(2022年度)統計量 ===') for col in ['農家密度', '耕地率', '高齢化率', '人口密度', '食料費比率', '年平均気温']: if col in df_merge.columns: s = df_merge[col].describe() print(f' {col}: mean={s["mean"]:.2f}, std={s["std"]:.2f}, ' f'min={s["min"]:.2f}, max={s["max"]:.2f}') |
マージ後: 47都道府県 === 断面データ(2022年度)統計量 === 農家密度: mean=10.00, std=2.84, min=1.42, max=15.86 耕地率: mean=10.88, std=5.16, min=2.77, max=25.96 高齢化率: mean=31.35, std=3.27, min=22.81, max=38.60 人口密度: mean=652.43, std=1219.36, min=65.55, max=6381.08 食料費比率: mean=26.47, std=1.39, min=23.30, max=30.51 年平均気温: mean=16.07, std=2.29, min=10.20, max=23.70
.describe() — 件数・平均・標準偏差・四分位・最大/最小を一括計算。データの素性チェックに必須。np.cumsum(arr) は累積和、np.linspace(a, b, n) は「aからbを等間隔でn個」。NumPyの定石です。108 109 110 111 112 113 114 115 116 117 118 119 120 121 | print('\n=== 図2: 農家密度 vs 人口密度 散布図 ===') df_plot = df_merge[['都道府県', '地域', '農家密度', '人口密度', '耕地率']].dropna() fig, ax = plt.subplots(figsize=(10, 7)) # 回帰線 x_all = df_plot['人口密度'].values y_all = df_plot['農家密度'].values slope, intercept, r_val, p_val, se = stats.linregress(x_all, y_all) x_line = np.linspace(x_all.min(), x_all.max(), 200) ax.plot(x_line, slope * x_line + intercept, color='#333333', linestyle='--', linewidth=1.5, label=f'回帰直線 (r={r_val:.3f}, p={p_val:.3f})') |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。stats.linregress(x, y) — 単回帰の傾き・切片・r値・p値・標準誤差を返します。使わない値は _ で受け取り。s[:-n]「末尾n文字を除く」/s[n:]「先頭n文字を除く」。スライス [start:stop:step] はリスト・タプル・文字列共通の基本ワザです。122 123 124 125 126 127 128 129 130 131 | # 散布図 for region in REGIONS: mask = df_plot['地域'] == region if mask.sum() == 0: continue ax.scatter(df_plot.loc[mask, '人口密度'], df_plot.loc[mask, '農家密度'], color=region_colors[region], s=70, alpha=0.85, edgecolors='white', linewidth=0.5, label=region) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。np.cumsum(arr) は累積和、np.linspace(a, b, n) は「aからbを等間隔でn個」。NumPyの定石です。132 133 134 135 136 137 138 139 140 141 142 143 144 | # 都道府県ラベル(全47) for _, row in df_plot.iterrows(): ax.annotate(row['都道府県'].replace('県', '').replace('府', '').replace('都', '').replace('道', ''), xy=(row['人口密度'], row['農家密度']), fontsize=7, color='#333333', xytext=(3, 3), textcoords='offset points') ax.set_xlabel('人口密度(人/km²)', fontsize=12) ax.set_ylabel('農家密度(販売農家数/可住地面積, 戸/km²)', fontsize=12) ax.set_title('農家密度 × 人口密度(47都道府県)\n' '— 都市化が進むほど農家密度は低下 —', fontsize=13) ax.legend(loc='upper right', fontsize=9, ncol=2) ax.grid(True, alpha=0.3) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。for _, row in df.iterrows() — DataFrameを1行ずつ取り出すループ。1点ずつ描画したいときに使用。{値:.2f}(小数2桁)、{値:,}(3桁区切り)、{値:>10}(右寄せ10桁)など、覚えると出力が一気に整います。145 146 147 148 149 150 151 152 153 154 155 | # 相関係数注釈 ax.text(0.03, 0.97, f'r = {r_val:.3f} (p = {p_val:.4f})\nデータ: SSDSE-B 2022 + SSDSE-E 2019/2024', transform=ax.transAxes, fontsize=9, verticalalignment='top', bbox=dict(boxstyle='round,pad=0.4', facecolor='#EFF3FF', alpha=0.8)) fig.tight_layout() save_fig(2) print(f' 農家密度 × 人口密度: r={r_val:.3f}, p={p_val:.4f}') |
=== 図2: 農家密度 vs 人口密度 散布図 === → html/figures/2021_H3_fig2.png 保存完了 農家密度 × 人口密度: r=-0.430, p=0.0026
plt.subplots(figsize=(W, H)) で図サイズ指定、fig.savefig(..., bbox_inches='tight') で余白を自動で詰めて保存。156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | print('\n' + '='*60) print('=== HTML用 統計値まとめ ===') print('='*60) # 高齢化率(2022年、地域別) print('\n【地域別高齢化率(2022年)】') df_b22 = df_b[df_b['年度'] == 2022].copy() df_b22['地域'] = df_b22['都道府県'].map(region_map) for region in REGIONS: mask = df_b22['地域'] == region mean_val = df_b22.loc[mask, '高齢化率'].mean() print(f' {region}: {mean_val:.1f}%') print(f'\n 全国平均高齢化率(2022): {df_b22["高齢化率"].mean():.1f}%') print(f' 高齢化率最高: {df_b22.loc[df_b22["高齢化率"].idxmax(), "都道府県"]} ' f'({df_b22["高齢化率"].max():.1f}%)') print(f' 高齢化率最低: {df_b22.loc[df_b22["高齢化率"].idxmin(), "都道府県"]} ' f'({df_b22["高齢化率"].min():.1f}%)') print('\n【OLS回帰分析 主要結果】') print(f' N={len(df_ols)}, R²={model_raw.rsquared:.3f}, adj.R²={model_raw.rsquared_adj:.3f}') print(f' F={model_raw.fvalue:.2f}, p={model_raw.f_pvalue:.4f}') print('\n【農家密度 Top5・Bottom5】') print(' Top5:') for i in range(min(5, len(df_rank))): r = df_rank.iloc[i] print(f' {i+1}. {r["都道府県"]}: {r["農家密度"]:.3f} 戸/km²') print(' Bottom5:') for i in range(len(df_rank)-5, len(df_rank)): r = df_rank.iloc[i] print(f' {i+1}. {r["都道府県"]}: {r["農家密度"]:.3f} 戸/km²') print('\n【農家密度 × 人口密度 相関】') print(f' r = {r_val:.3f}, p = {p_val:.4f}') print('\n=== 全図保存完了 ===') |
============================================================
=== HTML用 統計値まとめ ===
============================================================
【地域別高齢化率(2022年)】
北海道・東北: 33.9%
関東: 27.9%
中部: 31.0%
近畿: 30.1%
中国・四国: 33.5%
九州・沖縄: 31.3%
全国平均高齢化率(2022): 31.4%
高齢化率最高: 秋田県 (38.6%)
高齢化率最低: 東京都 (22.8%)
【OLS回帰分析 主要結果】
N=47, R²=0.239, adj.R²=0.146
F=2.57, p=0.0410
【農家密度 Top5・Bottom5】
Top5:
1. 香川県: 15.862 戸/km²
2. 鳥取県: 15.385 戸/km²
3. 和歌山県: 15.356 戸/km²
4. 山梨県: 14.876 戸/km²
5. 徳島県: 13.834 戸/km²
Bottom5:
43. 石川県: 6.642 戸/km²
44. 富山県: 6.146 戸/km²
45. 大阪府: 5.557 戸/km²
46. 東京都: 3.224 戸/km²
47. 北海道: 1.420 戸/km²
【農家密度 × 人口密度 相関】
r = -0.430, p = 0.0026
=== 全図保存完了 ===.dropna() は欠損行を除去、.copy() は独立したコピーを作る。pandasで警告を防ぐ定石。農業生産の担い手である農業従事者の高齢化を把握するため、SSDSE-Bの2012〜2022年の高齢化率(65歳以上人口比率)の推移を地域別に分析する。農業が盛んな地域(北海道・東北、中国・四国)での高齢化加速が農業後継者問題の深刻さを示す。
| 地域 | 高齢化率(2022年) | 農業との関係 |
|---|---|---|
| 北海道・東北 | 33.9% | 大規模農業地帯。高齢化が最高水準。 |
| 関東 | 27.9% | 都市部中心。高齢化率は低い。 |
| 中部 | 31.0% | 農業と工業の混在地域。 |
| 近畿 | 30.1% | 都市部〜農村部混在。 |
| 中国・四国 | 33.5% | 中山間農業地帯。高齢化深刻。 |
| 九州・沖縄 | 31.3% | 畑作・果樹農業地帯。 |
全国平均高齢化率(2022年): 31.4%。最高: 秋田県(38.6%)、最低: 東京都(22.8%)。
農家密度(都道府県の可住地面積あたり販売農家数)を目的変数とし、OLS(最小二乗法)による重回帰分析で決定要因を特定する。
| 変数 | 標準化係数β | p値 | 有意性 | 解釈 |
|---|---|---|---|---|
| 年平均気温(℃) | +0.378 | 0.025 | * | 温暖な地域ほど農家密度が高い(南日本・施設園芸) |
| 高齢化率(%) | +0.357 | 0.161 | n.s. | 農村地域に農家が多い傾向(符号は正だが非有意) |
| 食料費比率(%) | −0.152 | 0.327 | n.s. | 食料費比率との関係は不明確 |
| 耕地率(%) | +0.051 | 0.721 | n.s. | 耕地率単独の効果は小さい |
| ln(人口密度) | −0.031 | 0.905 | n.s. | 他の変数と相関しており独立効果は小 |
N=47、R²=0.239、adj.R²=0.146、F(5,41)=2.57、p=0.041。モデル全体は有意(p<0.05)だが個別変数の有意性はモデルの多重共線性に影響を受けている。気温のみが個別有意(p=0.025)。
標準化偏回帰係数(β係数)は、各説明変数を標準化(平均0・分散1)した後の偏回帰係数。単位の異なる変数間で「どの変数が目的変数に最も強く影響しているか」を比較できる。
47都道府県の農家密度を比較し、農業生産集積の地域パターンを把握する。農業センサスの農家数(2019年)と面積データ(SSDSE-E)を用いて算出した。
| 順位 | 都道府県 | 農家密度(戸/km²) | 地域 | 特徴 |
|---|---|---|---|---|
| 1位 | 香川県 | 15.86 | 中国・四国 | 小面積で農業密集。温暖気候・施設農業。 |
| 2位 | 鳥取県 | 15.39 | 中国・四国 | 二十世紀梨など果樹農業が盛ん。 |
| 3位 | 和歌山県 | 15.36 | 近畿 | みかん等果樹農業の一大産地。 |
| 4位 | 山梨県 | 14.88 | 中部 | ぶどう・桃など果樹王国。 |
| 5位 | 徳島県 | 13.83 | 中国・四国 | 野菜・果樹農業が活発。 |
| … | … | … | … | … |
| 45位 | 大阪府 | 5.56 | 近畿 | 都市化率が高く農地は少ない。 |
| 46位 | 東京都 | 3.22 | 関東 | 日本最大の都市。農業用地は極めて限られる。 |
| 47位 | 北海道 | 1.42 | 北海道・東北 | 農家1戸あたり面積が大きい大規模農業地帯。農家密度は最低でも農業産出額は全国最大。 |
2変数間の線形関係の強さと方向を定量化するピアソン相関係数 r は、−1(完全な負の相関)から+1(完全な正の相関)の範囲をとる。
分析結果から、農業生産強化と食料自給率向上に向けた以下の政策が示唆される。
| 課題 | 統計的根拠 | 政策対応 |
|---|---|---|
| 農業地域の高齢化加速 | 北海道・東北・中国四国で高齢化率33〜34%(全国平均31%超) | 就農支援・農業大学校の充実、農業の魅力発信 |
| 都市部の農地消失 | 人口密度↑ → 農家密度↓(r=−0.430) | 農地保全法制の強化、都市農業の推進 |
| 温暖地の農業優位性 | 気温がβ=+0.378で唯一有意(p=0.025) | 温暖地域の施設園芸・高付加価値農業の支援 |
| 北海道の大規模農業 | 農家密度最低だが農業産出額全国1位 | 大規模・効率的農業モデルと小規模農家の両立支援 |
OLS重回帰分析は強力な分析手法だが、以下の点に注意が必要:
① 多重共線性(Multicollinearity):説明変数間の相関が高いと、個別の偏回帰係数が不安定になる。本分析では高齢化率・ln人口密度・耕地率の間に共線性がある。
② 因果関係 ≠ 相関関係:「気温が高いほど農家密度が高い」は「気温が農家密度を決める」という因果ではなく、温暖地域の歴史的・文化的農業パターンを反映している可能性がある。
③ N=47の小標本:都道府県データは観察数が47件に限られる。変数が多いとモデルの自由度が低下し、adj.R²が下がる。変数選択の際は「理論的根拠」を重視する。
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 | import os 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') FIG_DIR = 'html/figures' DATA_B = 'data/raw/SSDSE-B-2026.csv' DATA_E = 'data/raw/SSDSE-E-2026.csv' os.makedirs(FIG_DIR, exist_ok=True) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。import pandas as pd など — 必要なライブラリをまとめて呼び出します。as pd は短い別名(alias)。matplotlib.use('Agg') — グラフを画面表示せずファイルに保存するためのおまじない。os.makedirs('html/figures', exist_ok=True) — 図の保存先フォルダを作る(既にあってもOK)。f"...{x}..." はf-string。文字列の中に {変数} と書くだけで埋め込めて、{x:.2f} のように書式も指定できます。212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 | print('\n=== 図1: 地域別高齢化率の推移 ===') years = sorted(df_b['年度'].unique()) fig, ax = plt.subplots(figsize=(10, 6)) for region in REGIONS: prefs = [p for p, r in region_map.items() if r == region] region_data = df_b[df_b['都道府県'].isin(prefs)].groupby('年度')['高齢化率'].mean() region_data = region_data.reindex(years) ax.plot(years, region_data.values, color=region_colors[region], linewidth=2.2, marker='o', markersize=4, label=region) ax.set_xlabel('年度', fontsize=12) ax.set_ylabel('高齢化率(65歳以上人口比率, %)', fontsize=12) ax.set_title('地域別 高齢化率の推移(2012〜2022年)\n' '— 農業地域における後継者問題の背景 —', fontsize=13) ax.legend(loc='upper left', fontsize=10) ax.set_xticks(years) ax.set_xticklabels([str(y) for y in years], rotation=45) ax.grid(True, alpha=0.35) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。df.groupby('列').apply(関数) — グループごとに関数を適用。時系列や地域別の集計でよく使います。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。df[col](1列)と df[[col1, col2]](複数列)でカッコの数が違います。リストを渡していると覚えるとミスを減らせます。234 235 236 237 238 239 240 241 | # 農業高齢化の注釈 ax.annotate('農村部(北海道・東北、中国・四国)\nで高齢化が加速', xy=(2022, 33), xytext=(2018, 36), fontsize=9, color='#4e9af1', arrowprops=dict(arrowstyle='->', color='#4e9af1', lw=1.5)) fig.tight_layout() save_fig(1) |
=== 図1: 地域別高齢化率の推移 === → html/figures/2021_H3_fig1.png 保存完了
s[:-n]「末尾n文字を除く」/s[n:]「先頭n文字を除く」。スライス [start:stop:step] はリスト・タプル・文字列共通の基本ワザです。242 243 244 245 246 247 248 249 250 251 | print('\n=== OLS 重回帰分析 ===') df_ols = df_merge[['都道府県', '農家密度', '耕地率', '高齢化率', '人口密度', '食料費比率', '年平均気温']].dropna().copy() df_ols['ln_人口密度'] = np.log(df_ols['人口密度'] + 1) y_ols = df_ols['農家密度'] X_vars = ['耕地率', '高齢化率', 'ln_人口密度', '食料費比率', '年平均気温'] X_raw = df_ols[X_vars] |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。np.cumsum(arr) は累積和、np.linspace(a, b, n) は「aからbを等間隔でn個」。NumPyの定石です。252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 | # 標準化(標準化偏回帰係数のため) X_std_vals = (X_raw - X_raw.mean()) / X_raw.std() y_std_vals = (y_ols - y_ols.mean()) / y_ols.std() X_std_c = sm.add_constant(X_std_vals) model_std = sm.OLS(y_std_vals, X_std_c).fit() X_c = sm.add_constant(X_raw) model_raw = sm.OLS(y_ols, X_c).fit() print(f' N={len(df_ols)}, R²={model_raw.rsquared:.3f}, adj.R²={model_raw.rsquared_adj:.3f}') print(f' F統計量={model_raw.fvalue:.2f}, p={model_raw.f_pvalue:.4f}') print() var_labels = { '耕地率': '耕地率(%)', '高齢化率': '高齢化率(%)', 'ln_人口密度': 'ln(人口密度)', '食料費比率': '食料費比率(%)', '年平均気温': '年平均気温(℃)' } print(' 変数 | 標準化係数 | p値') print(' ' + '-'*45) for var in X_vars: coef = model_std.params[var] pval = model_std.pvalues[var] sig = '**' if pval < 0.01 else ('*' if pval < 0.05 else '') label = var_labels.get(var, var) print(f' {label:<20s} | {coef:+.4f} | {pval:.4f} {sig}') |
=== OLS 重回帰分析 === N=47, R²=0.239, adj.R²=0.146 F統計量=2.57, p=0.0410 変数 | 標準化係数 | p値 --------------------------------------------- 耕地率(%) | +0.0514 | 0.7208 高齢化率(%) | +0.3568 | 0.1610 ln(人口密度) | -0.0312 | 0.9053 食料費比率(%) | -0.1523 | 0.3271 年平均気温(℃) | +0.3776 | 0.0246 *
sm.add_constant(X) — 切片項(定数1の列)を先頭に追加。statsmodelsで必須。sm.OLS(y, X).fit() — 最小二乗法でモデルを推定。model.params, model.pvalues, model.conf_int() で結果取得。{値:.2f}(小数2桁)、{値:,}(3桁区切り)、{値:>10}(右寄せ10桁)など、覚えると出力が一気に整います。282 283 284 285 286 287 288 | print('\n=== 図3: 標準化偏回帰係数プロット ===') coefs = model_std.params[X_vars].values pvals = model_std.pvalues[X_vars].values ci_lo = model_std.conf_int().loc[X_vars, 0].values ci_hi = model_std.conf_int().loc[X_vars, 1].values labels = [var_labels.get(v, v) for v in X_vars] |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。{値:.2f}(小数2桁)、{値:,}(3桁区切り)、{値:>10}(右寄せ10桁)など、覚えると出力が一気に整います。289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 | # 係数の大きさでソート order = np.argsort(coefs) coefs_s = coefs[order] pvals_s = pvals[order] labels_s = [labels[i] for i in order] ci_lo_s = ci_lo[order] ci_hi_s = ci_hi[order] xerr_lo = coefs_s - ci_lo_s xerr_hi = ci_hi_s - coefs_s colors_bar = ['#e05c5c' if p < 0.05 else '#aaaaaa' for p in pvals_s] fig, ax = plt.subplots(figsize=(9, 5)) bars = ax.barh(range(len(labels_s)), coefs_s, color=colors_bar, edgecolor='white', height=0.55) ax.errorbar(coefs_s, range(len(labels_s)), xerr=[xerr_lo, xerr_hi], fmt='none', color='#444444', capsize=4, linewidth=1.5) ax.axvline(0, color='#333333', linewidth=1.2) for i, (c, p) in enumerate(zip(coefs_s, pvals_s)): sig_str = '**' if p < 0.01 else ('*' if p < 0.05 else 'n.s.') ax.text(c + (0.01 if c >= 0 else -0.01), i, f'{c:+.3f} ({sig_str})', va='center', ha='left' if c >= 0 else 'right', fontsize=9, color='#222222') ax.set_yticks(range(len(labels_s))) ax.set_yticklabels(labels_s, fontsize=11) ax.set_xlabel('標準化偏回帰係数(β)', fontsize=12) ax.set_title('農家密度の決定要因:標準化偏回帰係数\n' f'R²={model_raw.rsquared:.3f}, adj.R²={model_raw.rsquared_adj:.3f}, ' f'N={len(df_ols)}', fontsize=13) red_patch = mpatches.Patch(color='#e05c5c', label='有意(p < 0.05)') gray_patch = mpatches.Patch(color='#aaaaaa', label='非有意(p ≥ 0.05)') ax.legend(handles=[red_patch, gray_patch], fontsize=10, loc='lower right') ax.grid(True, axis='x', alpha=0.3) fig.tight_layout() save_fig(3) |
=== 図3: 標準化偏回帰係数プロット === → html/figures/2021_H3_fig3.png 保存完了
fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。plt.subplots(figsize=(W, H)) で図サイズ指定、fig.savefig(..., bbox_inches='tight') で余白を自動で詰めて保存。331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 | print('\n=== 図4: 農家密度ランキング ===') df_rank = df_merge[['都道府県', '地域', '農家密度', '耕地率']].dropna().copy() df_rank = df_rank.sort_values('農家密度', ascending=False).reset_index(drop=True) fig, ax = plt.subplots(figsize=(14, 7)) bar_colors = [region_colors.get(r, '#888888') for r in df_rank['地域']] ax.bar(range(len(df_rank)), df_rank['農家密度'], color=bar_colors, edgecolor='white', linewidth=0.5) ax.set_xticks(range(len(df_rank))) ax.set_xticklabels( [p.replace('県', '').replace('府', '').replace('都', '').replace('道', '') for p in df_rank['都道府県']], rotation=75, ha='right', fontsize=8.5 ) ax.set_ylabel('農家密度(販売農家数/可住地面積, 戸/km²)', fontsize=11) ax.set_title('都道府県別 農家密度ランキング\n' '— 農業生産集積の地域差(SSDSE-E 2019農業センサス + 面積データ)—', fontsize=13) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。sort_values('列名', ascending=False) — 指定列で並べ替え(降順)。plt.subplots(figsize=(W, H)) で図サイズ指定、fig.savefig(..., bbox_inches='tight') で余白を自動で詰めて保存。352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 | # 地域凡例 patches = [mpatches.Patch(color=region_colors[r], label=r) for r in REGIONS] ax.legend(handles=patches, loc='upper right', fontsize=9, ncol=2) ax.grid(True, axis='y', alpha=0.3) # 上位・下位のラベル for i in range(min(5, len(df_rank))): ax.text(i, df_rank['農家密度'].iloc[i] + 0.002, f"{df_rank['農家密度'].iloc[i]:.3f}", ha='center', va='bottom', fontsize=7.5, color='#333333') for i in range(len(df_rank)-5, len(df_rank)): ax.text(i, df_rank['農家密度'].iloc[i] + 0.002, f"{df_rank['農家密度'].iloc[i]:.3f}", ha='center', va='bottom', fontsize=7.5, color='#333333') fig.tight_layout() save_fig(4) print(f'\n 全国農家密度 平均: {df_rank["農家密度"].mean():.3f} 戸/km²') print(f' 最高: {df_rank.iloc[0]["都道府県"]} ({df_rank.iloc[0]["農家密度"]:.3f})') print(f' 最低: {df_rank.iloc[-1]["都道府県"]} ({df_rank.iloc[-1]["農家密度"]:.3f})') |
=== 図4: 農家密度ランキング === → html/figures/2021_H3_fig4.png 保存完了 全国農家密度 平均: 9.997 戸/km² 最高: 香川県 (15.862) 最低: 北海道 (1.420)
.dropna() は欠損行を除去、.copy() は独立したコピーを作る。pandasで警告を防ぐ定石。SSDSE-B(2012〜2022年度)とSSDSE-E(農業センサス)を用いた47都道府県の農業生産集積分析の結果:
| データ | 出典 | 備考 |
|---|---|---|
| SSDSE-B-2026 | 統計数理研究所 SSDSE(社会・人口統計体系) | 47都道府県、2012〜2022年度、年次パネル |
| SSDSE-E-2026 | 統計数理研究所 SSDSE(都道府県比較) | 47都道府県、断面データ(農業センサス2019等) |
本コードは実公的統計データ(SSDSE)のみを使用。合成データ・乱数生成(np.random.seed等)は一切使用しない。
統計分析の解釈で初心者がやりがちな勘違いをまとめます。特に「相関と因果の混同」「p値の過信」は研究現場でもよく起きる落とし穴です。本文を読む前にも、読んだ後にも、目を通してみてください。
統計の基本用語を初心者向けに解説します。本文中で見慣れない言葉が出てきたら、ここに戻って確認してください。
統計手法について「何のためか」「結果をどう読むか」を初心者向けに解説します。
この研究をさらに発展させるための3つの方向性を示します。「今回わかったこと(X)」から「次に検証すべき仮説(Y)」を立て、「具体的に何をするか(Z)」まで考えてみましょう。
学んだだけでは身につきません。実際に手を動かすのが最強の学習方法です。本論文のスクリプトをベースに、以下のチャレンジに挑戦してみてください。難易度別に5つ用意しました。
本論文で学んだ手法は、研究の世界だけでなく、行政・企業・NPO の現場でも様々に活用されています。具体的なシーンを紹介します。
この論文を読んで初心者が抱きやすい疑問に、教育的観点から答えます。