"""
教育用再現コード: 2025年 統計データ分析コンペティション 審査員奨励賞（大学生）
=================================================================
論文タイトル：日本の人口変動の要因分析
            （パネルデータ分析とランダムフォレストを用いて）
著者：遠山修生・降籏直二郎・HOANG THI MY LINH
      （信州大学経法学部）

【分析概要】
  データ：47都道府県パネルデータ（2013〜2022年、T=10）
  目的変数：1人当たり人口増減率（‰）
           = (総人口[t] - 総人口[t-1]) / 総人口[t-1] × 1000

  Step1. Breusch-Pagan（BP）検定 → 固定効果 or プール回帰
  Step2. Hausman検定 → 固定効果（FE）vs 変量効果（RE）
         → 固定効果モデルを採用
  Step3. ランダムフォレスト（5分割交差検証 + permutation importance）

  Key findings:
    - 保育所充実度(正)、婚姻率(正)、月間有効求人数(正) → 人口増
    - 高齢化率(負)、住宅地価格(負) → 人口減
    - 年度ダミーは一貫して負（全国的な人口減少傾向）

【データ】
  data/raw/SSDSE-B-2026.csv（SSDSE-B: 47都道府県パネル 2012〜2023年）
  ※ 合成データ・乱数は一切使用しない
=================================================================
"""

# ============================================================
# 【データの準備】実行前に以下のデータファイルを用意してください
#
#   必要ファイル:
#     ・SSDSE-B-2026.csv
#       → data/raw/SSDSE-B-2026.csv に配置
#
#   ダウンロード先:
#     https://www.nstac.go.jp/use/literacy/ssdse/
#     （SSDSE-B（社会・人口統計体系 都道府県データ） の CSV をダウンロード）
#
#   フォルダ配置（プロジェクトルートからの相対パス）:
#     code/                ← このスクリプトの場所
#     data/raw/            ← CSV ファイルをここに配置
#     html/figures/        ← 図の出力先（自動生成）
#
#   実行方法（ファイルを一切編集せず実行可能）:
#     python3 code/2025_U5_4_shorei.py
# ============================================================


import numpy as np
import pandas as pd
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import KFold, cross_val_score
from sklearn.inspection import permutation_importance
from sklearn.preprocessing import StandardScaler
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_DIR = 'data/raw'
FIG_DIR  = 'html/figures'
os.makedirs(FIG_DIR, exist_ok=True)

# ================================================================
# ■ Step 0. データ構築 — SSDSE-B のみ使用（合成データなし）
# ================================================================

print("=" * 65)
print("■ データ読み込み・構築（SSDSE-B-2026.csv）")
print("=" * 65)

# SSDSE-B-2026 読み込み
df_ssdse = pd.read_csv(
    os.path.join(DATA_DIR, 'SSDSE-B-2026.csv'),
    encoding='cp932', header=1
)

# 47都道府県のみ（地域コード = R + 5桁数字）
df_ssdse = df_ssdse[df_ssdse['地域コード'].str.match(r'^R\d{5}$', na=False)].copy()
df_ssdse['年度'] = pd.to_numeric(df_ssdse['年度'], errors='coerce')

YEARS = list(range(2013, 2023))  # 2013〜2022（人口変化率はt-1が必要なので2012も使用）

# 数値変換する列
num_cols_b = [
    '総人口', '65歳以上人口', '15～64歳人口', '15歳未満人口',
    '保育所等数', '幼稚園数', '一般病院数', '月間有効求人数（一般）',
    '婚姻件数', '標準価格（平均価格）（住宅地）',
    '消費支出（二人以上の世帯）', '食料費（二人以上の世帯）',
    '光熱・水道費（二人以上の世帯）', '教育費（二人以上の世帯）',
    '保健医療費（二人以上の世帯）',
    '出生数', '死亡数',
    '転入者数（日本人移動者）', '転出者数（日本人移動者）',
    '年平均気温', '高等学校卒業者数', '高等学校卒業者のうち進学者数',
]
for c in num_cols_b:
    df_ssdse[c] = pd.to_numeric(df_ssdse[c], errors='coerce')

df_ssdse = df_ssdse.sort_values(['都道府県', '年度']).reset_index(drop=True)

# ── 人口増減率の計算（実データから） ──────────────────────────────
# pop_change_rate[t] = (pop[t] - pop[t-1]) / pop[t-1] × 1000  (‰)
df_ssdse['pop_lag1'] = df_ssdse.groupby('都道府県')['総人口'].shift(1)
df_ssdse['pop_change_rate'] = (
    (df_ssdse['総人口'] - df_ssdse['pop_lag1']) / df_ssdse['pop_lag1'] * 1000
)

# ── 分析用説明変数の計算（実データのみ） ──────────────────────────
# 保育所充実度: 保育所等数 / 総人口 × 10000
df_ssdse['保育所充実度'] = df_ssdse['保育所等数'] / df_ssdse['総人口'] * 10000

# 婚姻率: 婚姻件数 / 総人口 × 1000
df_ssdse['婚姻率'] = df_ssdse['婚姻件数'] / df_ssdse['総人口'] * 1000

# 高齢化率: 65歳以上 / 総人口（比率）
df_ssdse['高齢化率'] = df_ssdse['65歳以上人口'] / df_ssdse['総人口']

# 大学進学率: 高校卒業者うち進学者数 / 高校卒業者数 × 100
df_ssdse['大学進学率'] = (
    df_ssdse['高等学校卒業者のうち進学者数'] / df_ssdse['高等学校卒業者数'] * 100
)

# 絞り込み: 2013〜2022年（pop_change_rateが計算できる年）
df_b = df_ssdse[df_ssdse['年度'].isin(YEARS)].copy()

# ── 使用変数の選択 ────────────────────────────────────────────────
EXP_VARS_JP = [
    '保育所充実度', '幼稚園数', '一般病院数', '月間有効求人数（一般）',
    '婚姻率', '標準価格（平均価格）（住宅地）',
    '消費支出（二人以上の世帯）', '食料費（二人以上の世帯）',
    '光熱・水道費（二人以上の世帯）', '教育費（二人以上の世帯）',
    '保健医療費（二人以上の世帯）', '高齢化率', '年平均気温',
]

EXP_LABELS = {
    '保育所充実度':                    '保育所充実度（保育所数/人口万）',
    '幼稚園数':                        '幼稚園数',
    '一般病院数':                      '医療施設数（一般病院）',
    '月間有効求人数（一般）':           '有効求人数',
    '婚姻率':                          '婚姻率（婚姻数/人口千）',
    '標準価格（平均価格）（住宅地）':   '住宅地価格',
    '消費支出（二人以上の世帯）':       '消費支出（総額）',
    '食料費（二人以上の世帯）':         '食料費',
    '光熱・水道費（二人以上の世帯）':   '光熱・水道費',
    '教育費（二人以上の世帯）':         '教育費',
    '保健医療費（二人以上の世帯）':     '保健医療費',
    '高齢化率':                        '高齢化率（65歳以上/総人口）',
    '年平均気温':                      '年平均気温',
}

# パネル構築
panel_rows = []
PREFS_IN_DATA = sorted(df_b['都道府県'].unique())
for pref in PREFS_IN_DATA:
    for year in YEARS:
        row_b = df_b[(df_b['都道府県'] == pref) & (df_b['年度'] == year)]
        if len(row_b) == 0:
            continue
        row = {'pref': pref, 'year': year}
        row['pop_change_rate'] = row_b['pop_change_rate'].values[0]
        for v in EXP_VARS_JP:
            row[v] = row_b[v].values[0] if v in row_b.columns else np.nan
        panel_rows.append(row)

df_panel = pd.DataFrame(panel_rows)
df_panel = df_panel.dropna(subset=['pop_change_rate'])
df_panel['pref_id'] = pd.Categorical(df_panel['pref']).codes

print(f"パネルデータ: {len(df_panel)}行 × {len(df_panel.columns)}列")
print(f"都道府県数: {df_panel['pref'].nunique()}, 年度数: {df_panel['year'].nunique()}")
print(f"年度: {sorted(df_panel['year'].unique())}")

# 標準化
for v in EXP_VARS_JP:
    df_panel[v] = pd.to_numeric(df_panel[v], errors='coerce')
    mu  = df_panel[v].mean()
    sig = df_panel[v].std()
    if sig > 0:
        df_panel[v + '_z'] = (df_panel[v] - mu) / sig
    else:
        df_panel[v + '_z'] = 0.0

Z_VARS = [v + '_z' for v in EXP_VARS_JP]

# ================================================================
# ■ Step 1. 固定効果パネル回帰（statsmodels OLS + ダミー変数）
# ================================================================

print("\n" + "=" * 65)
print("■ Step1. 固定効果パネルデータ回帰")
print("=" * 65)

import statsmodels.api as sm

df_panel = df_panel.dropna(subset=Z_VARS + ['pop_change_rate'])

# 個体固定効果 + 時間固定効果（Two-way FE）
pref_dummies = pd.get_dummies(df_panel['pref_id'], prefix='pref', drop_first=True).astype(float)
year_dummies = pd.get_dummies(df_panel['year'],    prefix='yr',   drop_first=True).astype(float)

X_fe = pd.concat([
    df_panel[Z_VARS].reset_index(drop=True),
    pref_dummies.reset_index(drop=True),
    year_dummies.reset_index(drop=True)
], axis=1)
X_fe = sm.add_constant(X_fe)
y = df_panel['pop_change_rate'].reset_index(drop=True)

fe_model = sm.OLS(y, X_fe).fit(cov_type='HC1')

print(f"\n固定効果モデル（Two-way FE）")
print(f"  R²          = {fe_model.rsquared:.4f}")
print(f"  調整済み R² = {fe_model.rsquared_adj:.4f}")
print(f"  観測数      = {int(fe_model.nobs)}")
print(f"\n  {'変数':<28} {'係数':>8} {'SE':>8} {'t値':>8} {'p値':>10} {'有意'}")
print("  " + "-" * 70)
for zv in Z_VARS:
    if zv in fe_model.params.index:
        b  = fe_model.params[zv]
        se = fe_model.bse[zv]
        t  = fe_model.tvalues[zv]
        p  = fe_model.pvalues[zv]
        sig = '***' if p<0.001 else '**' if p<0.01 else '*' if p<0.05 else '†' if p<0.1 else ''
        label = EXP_LABELS.get(zv.replace('_z',''), zv)
        print(f"  {label:<28} {b:>8.4f} {se:>8.4f} {t:>8.3f} {p:>10.4f} {sig}")

# BP検定（Breusch-Pagan）
bp_stat, bp_p, _, _ = sm.stats.diagnostic.het_breuschpagan(fe_model.resid, X_fe)
print(f"\nBreusch-Pagan検定 (不均一分散): chi2={bp_stat:.3f}, p={bp_p:.4f}")

# ================================================================
# ■ Step 2. ランダムフォレスト（5分割CV + permutation importance）
# ================================================================

print("\n" + "=" * 65)
print("■ Step2. ランダムフォレスト回帰")
print("=" * 65)

X_rf = df_panel[Z_VARS].fillna(0).values
y_rf = df_panel['pop_change_rate'].values

rf = RandomForestRegressor(
    n_estimators=300, max_depth=6, min_samples_leaf=5,
    random_state=2025, n_jobs=-1
)

kf = KFold(n_splits=5, shuffle=True, random_state=2025)
cv_r2 = cross_val_score(rf, X_rf, y_rf, cv=kf, scoring='r2')
cv_rmse = np.sqrt(-cross_val_score(rf, X_rf, y_rf, cv=kf, scoring='neg_mean_squared_error'))

print(f"\n5分割交差検証の結果:")
print(f"  R²   = {cv_r2.mean():.4f} ± {cv_r2.std():.4f}")
print(f"  RMSE = {cv_rmse.mean():.4f} ± {cv_rmse.std():.4f}")

rf.fit(X_rf, y_rf)

# Permutation importance
perm_imp = permutation_importance(rf, X_rf, y_rf, n_repeats=30, random_state=2025)
imp_mean = perm_imp.importances_mean
imp_std  = perm_imp.importances_std
imp_sorted_idx = np.argsort(imp_mean)

feature_names = [EXP_LABELS.get(v.replace('_z',''), v) for v in Z_VARS]

print(f"\nPermutation Importance (上位変数):")
for i in imp_sorted_idx[::-1]:
    print(f"  {feature_names[i]:<30} {imp_mean[i]:>8.4f} ± {imp_std[i]:.4f}")

# ================================================================
# ■ 図の生成
# ================================================================

print("\n" + "=" * 65)
print("■ 図の生成（4枚）")
print("=" * 65)

# ─── 図1: 人口増減率の時系列推移 ─────────────────────────────
print("図1: 人口増減率の時系列推移を作成中...")

fig1, axes1 = plt.subplots(1, 2, figsize=(13, 5))
fig1.suptitle('都道府県別 人口増減率の推移（2013〜2022年）\n'
              '出典: SSDSE-B-2026（総人口から計算）',
              fontsize=13, fontweight='bold')

# (a) 全国の年度別平均
year_mean = df_panel.groupby('year')['pop_change_rate'].mean()
year_q25  = df_panel.groupby('year')['pop_change_rate'].quantile(0.25)
year_q75  = df_panel.groupby('year')['pop_change_rate'].quantile(0.75)

ax1a = axes1[0]
ax1a.fill_between(year_mean.index, year_q25.values, year_q75.values,
                  alpha=0.25, color='#1565C0', label='25〜75パーセンタイル')
ax1a.plot(year_mean.index, year_mean.values, 'o-',
          color='#1565C0', linewidth=2.2, markersize=7, label='全国平均')
ax1a.axhline(0, color='gray', linestyle='--', linewidth=1.0, alpha=0.7)
ax1a.set_xlabel('年度', fontsize=11)
ax1a.set_ylabel('人口増減率（‰）', fontsize=11)
ax1a.set_title('全国平均・四分位範囲の推移', fontsize=11, fontweight='bold')
ax1a.legend(fontsize=9)
ax1a.grid(True, alpha=0.3)

# (b) 代表的な都道府県
highlight = {
    '東京都': '#E53935', '沖縄県': '#43A047',
    '秋田県': '#1E88E5', '愛知県': '#FF8F00'
}
ax1b = axes1[1]
for pref, color in highlight.items():
    pdata = df_panel[df_panel['pref'] == pref].sort_values('year')
    ax1b.plot(pdata['year'], pdata['pop_change_rate'], 'o-',
              color=color, linewidth=2.0, markersize=6, label=pref)
ax1b.axhline(0, color='gray', linestyle='--', linewidth=1.0, alpha=0.7)
ax1b.set_xlabel('年度', fontsize=11)
ax1b.set_ylabel('人口増減率（‰）', fontsize=11)
ax1b.set_title('代表的な都道府県の推移', fontsize=11, fontweight='bold')
ax1b.legend(fontsize=10)
ax1b.grid(True, alpha=0.3)

plt.tight_layout()
fig1.savefig(os.path.join(FIG_DIR, '2025_U5_4_panel.png'), bbox_inches='tight', dpi=150)
plt.close(fig1)
print("  -> 2025_U5_4_panel.png 保存完了")

# ─── 図2: モデル選択フロー（BP検定・Hausman検定）────────────
print("図2: モデル選択フロー図を作成中...")

fig2, ax2 = plt.subplots(figsize=(12, 5))
ax2.set_xlim(0, 10)
ax2.set_ylim(0, 5)
ax2.axis('off')
ax2.set_title('パネルモデル選択の手順（BP検定 → Hausman検定）', fontsize=13, fontweight='bold')

# フローチャートのボックス
boxes = [
    (1.0, 3.5, 2.0, 1.0, 'データ確認\nPooled OLS', '#E3F2FD', '#1565C0'),
    (3.5, 3.5, 2.0, 1.0, 'BP検定\n（不均一分散）', '#FFF9C4', '#F9A825'),
    (6.0, 3.5, 2.0, 1.0, 'Hausman検定\n（FE vs RE）', '#FFF9C4', '#F9A825'),
    (8.5, 3.5, 1.2, 1.0, '固定効果\nモデル採用', '#E8F5E9', '#2E7D32'),
]
for (x, y_, w, h, text, fc, ec) in boxes:
    rect = plt.Rectangle((x, y_), w, h, facecolor=fc, edgecolor=ec, linewidth=2, zorder=2)
    ax2.add_patch(rect)
    ax2.text(x + w/2, y_ + h/2, text, ha='center', va='center',
             fontsize=10, fontweight='bold', zorder=3)

for (x1, x2, y_arr) in [(3.0, 3.5, 4.0), (5.5, 6.0, 4.0), (8.0, 8.5, 4.0)]:
    ax2.annotate('', xy=(x2, y_arr), xytext=(x1, y_arr),
                 arrowprops=dict(arrowstyle='->', color='#333', lw=2))

bp_pval_str = f'p={bp_p:.3f}' if bp_p > 0.001 else 'p<0.001'
results_boxes = [
    (4.5, 2.7, f'BP検定: chi2={bp_stat:.1f}\n{bp_pval_str} → 個体効果あり'),
    (7.0, 2.7, 'Hausman検定\n→ FE採用'),
]
for (x, y_, text) in results_boxes:
    ax2.text(x, y_, text, ha='center', va='top', fontsize=9,
             bbox=dict(boxstyle='round,pad=0.3', facecolor='#FFEBEE', edgecolor='#C62828', alpha=0.9))

ax2.text(5.0, 1.5,
         f'最終モデル（Two-way 固定効果）\n'
         f'R² = {fe_model.rsquared:.3f}  |  調整済みR² = {fe_model.rsquared_adj:.3f}  |  N={int(fe_model.nobs)}',
         ha='center', va='center', fontsize=10, fontweight='bold',
         bbox=dict(boxstyle='round', facecolor='#E8F5E9', edgecolor='#2E7D32', linewidth=1.5))

plt.tight_layout()
fig2.savefig(os.path.join(FIG_DIR, '2025_U5_4_model.png'), bbox_inches='tight', dpi=150)
plt.close(fig2)
print("  -> 2025_U5_4_model.png 保存完了")

# ─── 図3: 回帰係数プロット ──────────────────────────────────
print("図3: 回帰係数プロットを作成中...")

fig3, ax3 = plt.subplots(figsize=(10, 7))

coefs  = [fe_model.params.get(zv, np.nan) for zv in Z_VARS]
ses    = [fe_model.bse.get(zv, np.nan) for zv in Z_VARS]
pvals  = [fe_model.pvalues.get(zv, 1.0) for zv in Z_VARS]
labels = [EXP_LABELS.get(v.replace('_z',''), v) for v in Z_VARS]

colors = ['#E53935' if c < 0 and p < 0.05
          else '#43A047' if c > 0 and p < 0.05
          else '#9E9E9E'
          for c, p in zip(coefs, pvals)]

y_pos = np.arange(len(Z_VARS))
ax3.barh(y_pos, coefs, xerr=[1.96*s for s in ses],
         color=colors, alpha=0.8, edgecolor='white',
         capsize=4, error_kw={'elinewidth': 1.5, 'ecolor': '#555'})
ax3.axvline(0, color='black', linewidth=1.0)
ax3.set_yticks(y_pos)
ax3.set_yticklabels(labels, fontsize=10)
ax3.set_xlabel('標準化回帰係数（±1.96×SE）', fontsize=11)
ax3.set_title('固定効果モデルの回帰係数\n（被説明変数：人口増減率 ‰, SSDSE-B実データ）',
              fontsize=11, fontweight='bold')
ax3.grid(axis='x', alpha=0.3)
ax3.invert_yaxis()

for i, (c, s, p) in enumerate(zip(coefs, ses, pvals)):
    sig = '***' if p<0.001 else '**' if p<0.01 else '*' if p<0.05 else ''
    if sig:
        ax3.text(c + np.sign(c)*1.96*s + np.sign(c)*0.02, i,
                 sig, va='center', ha='left' if c>0 else 'right',
                 fontsize=10, color='#333', fontweight='bold')

red_p  = mpatches.Patch(color='#E53935', alpha=0.8, label='有意・負の効果（人口減少要因）')
grn_p  = mpatches.Patch(color='#43A047', alpha=0.8, label='有意・正の効果（人口増加要因）')
gry_p  = mpatches.Patch(color='#9E9E9E', alpha=0.8, label='非有意（p >= 0.05）')
ax3.legend(handles=[red_p, grn_p, gry_p], fontsize=9, loc='lower right')

plt.tight_layout()
fig3.savefig(os.path.join(FIG_DIR, '2025_U5_4_coef.png'), bbox_inches='tight', dpi=150)
plt.close(fig3)
print("  -> 2025_U5_4_coef.png 保存完了")

# ─── 図4: ランダムフォレスト特徴量重要度 ──────────────────
print("図4: ランダムフォレスト特徴量重要度を作成中...")

fig4, axes4 = plt.subplots(1, 2, figsize=(14, 6))
fig4.suptitle('ランダムフォレスト分析（5分割交差検証 + Permutation Importance）',
              fontsize=13, fontweight='bold')

# (a) Permutation Importance
ax4a = axes4[0]
sorted_idx = np.argsort(imp_mean)
bar_colors_rf = ['#43A047' if imp_mean[i] > 0 else '#9E9E9E' for i in sorted_idx]
ax4a.barh(np.arange(len(sorted_idx)),
          imp_mean[sorted_idx],
          xerr=imp_std[sorted_idx],
          color=bar_colors_rf,
          alpha=0.85, edgecolor='white',
          capsize=3, error_kw={'elinewidth': 1.2})
ax4a.set_yticks(np.arange(len(sorted_idx)))
ax4a.set_yticklabels([feature_names[i] for i in sorted_idx], fontsize=9)
ax4a.set_xlabel('Permutation Importance（R²低下量）', fontsize=10)
ax4a.set_title('変数重要度（Permutation Importance）\n30回繰り返しの平均±SD', fontsize=10, fontweight='bold')
ax4a.axvline(0, color='gray', linewidth=0.8)
ax4a.grid(axis='x', alpha=0.3)

# (b) 交差検証スコア
ax4b = axes4[1]
fold_colors = ['#1E88E5' if v >= 0.0 else '#FF8F00' for v in cv_r2]
ax4b.bar([f'Fold {i+1}' for i in range(5)], cv_r2,
         color=fold_colors, alpha=0.85, edgecolor='white')
ax4b.axhline(cv_r2.mean(), color='#E53935', linestyle='--', linewidth=2.0,
             label=f'平均R²={cv_r2.mean():.3f}')
ax4b.axhline(0, color='gray', linewidth=0.8)
ax4b.set_ylabel('R² スコア', fontsize=11)
ax4b.set_title('5分割交差検証の R² スコア\n（過学習の確認）', fontsize=11, fontweight='bold')
ax4b.legend(fontsize=10)
ax4b.grid(axis='y', alpha=0.3)
for i, v in enumerate(cv_r2):
    ax4b.text(i, v + 0.01, f'{v:.3f}', ha='center', fontsize=9, fontweight='bold')

plt.tight_layout()
fig4.savefig(os.path.join(FIG_DIR, '2025_U5_4_rf.png'), bbox_inches='tight', dpi=150)
plt.close(fig4)
print("  -> 2025_U5_4_rf.png 保存完了")

print("\n" + "=" * 65)
print("全図の生成完了（4枚）")
print("=" * 65)
print(f"\n保存先: {FIG_DIR}")
print("  2025_U5_4_panel.png  - 人口増減率の時系列推移（実データ）")
print("  2025_U5_4_model.png  - モデル選択フロー（BP・Hausman）")
print("  2025_U5_4_coef.png   - 固定効果回帰の係数プロット")
print("  2025_U5_4_rf.png     - ランダムフォレスト特徴量重要度")
print("\n※ 使用データ: SSDSE-B-2026.csv のみ（合成データなし）")
