このページの分析を自分で再現するには、以下の手順でデータを準備してください。コードの編集は不要です。
data/raw/ フォルダに入れます。html/figures/ に自動保存されます。
日本の47都道府県は、農業・林業中心の農村型地域から、製造業集積地、観光業・サービス業主体の地域、高度な知識・医療集積地まで、多様な産業構造を持つ。この産業構造の差異は長期的な経済成長とどのような関係があるのか。
まず「地域産業構造と経済成長主成分分析とクラスタリングによる類型化」を統計的にとらえることが有効だと考えられる。 その理由は感覚や経験則だけでは、複雑な社会要因の中で「何が本当に効いているか」を見極めにくいからである。 本研究では公開データと統計手法を組み合わせ、この問いに定量的な答えを出すことを目指す。
本研究では、産業特化度(LQ類似指標)によって各都道府県の産業構造を数値化し、主成分分析(PCA)で多次元データを圧縮した後、Ward法クラスタリングで都道府県を4つの産業類型に分類する。さらに、主成分スコアと経済成長の代理指標(消費支出水準)の関係を重回帰分析で定量化する。
SSDSE-B(都道府県) LQ産業特化指標 Ward法クラスタリング 重回帰分析
SSDSE(社会・人口統計体系データセット)-B の2022年度データを使用する。SSDSE-Bは都道府県レベルのデータを収録し、人口・教育・医療・住宅・消費などの分野にまたがる100以上の変数を含む。本分析では47都道府県 × 6産業構造指標の行列を構築する。
産業立地係数(Location Quotient, LQ)は「ある地域の産業特化度を全国平均との比で表す指標」であり、LQ > 1.0 なら全国平均より特化度が高い。本研究では以下の6指標を SSDSE-B の実測値から計算し、全国平均(47都道府県の平均)= 1.0に正規化する。
| 指標名 | 計算式 | 産業の類型 | 変動係数(CV) |
|---|---|---|---|
| 農村型高齢化指数 | 65歳以上人口 ÷ 総人口 | 第一次産業(農林漁業)地域の代理 | 0.104 |
| 建設・製造活発度 | 着工建築物数 ÷ 総人口 | 第二次産業(製造業・建設業) | 0.189 |
| サービス消費水準 | 消費支出(二人以上世帯) | 第三次産業(サービス・小売) | 0.066 |
| 医療特化度 | (一般病院数+診療所数)÷ 総人口 | 保健・医療・福祉産業 | 0.143 |
| 教育・知識集積度 | 教育費(二人以上世帯) | 知識産業・高等教育集積 | 0.407 |
| 観光特化度 | 旅館営業施設数 ÷ 総人口 | 観光・宿泊産業 | 0.667 |
LQは地域経済学の基本指標。ある地域の産業 i の特化度を全国・全体との比で表す。SSDSE-B のような単一の指標列がない場合、代理変数(proxy)でLQ類似指標を構築できる。
1 2 3 4 5 6 | lq_agri = df['65歳以上人口'] / df['総人口'] lq_agri = lq_agri / lq_agri.mean() # 指標2: 建設・着工密度(第二次産業・製造業系の代理) lq_mfg = df['着工建築物数'] / df['総人口'] lq_mfg = lq_mfg / lq_mfg.mean() |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。df['A'] / df['B'] — pandasの列同士の四則演算は要素ごと(element-wise)。forループ不要なのが強み。7 8 9 10 11 12 13 | # 指標3: 消費・サービス支出水準(第三次産業代理) lq_svc = df['消費支出(二人以上の世帯)'] lq_svc = lq_svc / lq_svc.mean() # 指標4: 医療密度(医療・福祉特化) lq_med = (df['一般病院数'] + df['一般診療所数']) / df['総人口'] lq_med = lq_med / lq_med.mean() |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。14 15 16 17 18 19 20 | # 指標5: 教育費支出(知識産業・高学歴地域代理) lq_edu = df['教育費(二人以上の世帯)'] lq_edu = lq_edu / lq_edu.mean() # 指標6: 観光施設密度(旅館・ホテル数/人口: 観光特化) lq_tour = df['旅館営業施設数(ホテルを含む)'] / df['総人口'] lq_tour = lq_tour / lq_tour.mean() |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | # 指標行列の作成 feat_names = [ '農村型高齢化指数', '建設・製造活発度', 'サービス消費水準', '医療特化度', '教育・知識集積度', '観光特化度', ] X_raw = pd.DataFrame({ feat_names[0]: lq_agri.values, feat_names[1]: lq_mfg.values, feat_names[2]: lq_svc.values, feat_names[3]: lq_med.values, feat_names[4]: lq_edu.values, feat_names[5]: lq_tour.values, }, index=prefs) print("\nLQ類似指標(全国平均=1.0)の記述統計:") print(X_raw.describe().round(3)) |
LQ類似指標(全国平均=1.0)の記述統計:
農村型高齢化指数 建設・製造活発度 サービス消費水準 医療特化度 教育・知識集積度 観光特化度
count 47.000 47.000 47.000 47.000 47.000 47.000
mean 1.000 1.000 1.000 1.000 1.000 1.000
std 0.104 0.189 0.066 0.143 0.407 0.667
min 0.728 0.639 0.846 0.712 0.520 0.167
25% 0.952 0.872 0.956 0.901 0.729 0.566
50% 1.002 1.012 0.994 1.015 0.919 0.902
75% 1.076 1.116 1.044 1.095 1.108 1.214
max 1.231 1.697 1.121 1.334 2.626 3.641.describe() — 件数・平均・標準偏差・四分位・最大/最小を一括計算。データの素性チェックに必須。r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | Z = linkage(X_scaled, method='ward') k = 4 labels = fcluster(Z, k, criterion='maxclust') df_result = X_raw.copy() df_result['PC1'] = X_pca[:, 0] df_result['PC2'] = X_pca[:, 1] df_result['PC3'] = X_pca[:, 2] df_result['Cluster'] = labels df_result['都道府県'] = prefs print(f"\nWard法 k={k} クラスター割当:") for c in sorted(df_result['Cluster'].unique()): prefs_in = df_result[df_result['Cluster'] == c]['都道府県'].tolist() print(f" クラスター{c} ({len(prefs_in)}府県): {', '.join(prefs_in)}") |
Ward法 k=4 クラスター割当: クラスター1 (9府県): 京都府, 大阪府, 兵庫県, 和歌山県, 島根県, 広島県, 徳島県, 福岡県, 長崎県 クラスター2 (20府県): 北海道, 青森県, 岩手県, 秋田県, 山形県, 福島県, 新潟県, 福井県, 山梨県, 長野県, 三重県, 鳥取県, 香川県, 愛媛県, 高知県, 佐賀県, 熊本県, 宮崎県, 鹿児島県, 沖縄県 クラスター3 (4府県): 埼玉県, 東京都, 神奈川県, 滋賀県 クラスター4 (14府県): 宮城県, 茨城県, 栃木県, 群馬県, 千葉県, 富山県, 石川県, 岐阜県, 静岡県, 愛知県, 奈良県, 岡山県, 山口県, 大分県
[式 for x in リスト] はリスト内包表記。forループでappendする代わりに1行でリストを作れます。56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | region_map = { '北海道': '北海道・東北', '青森県': '北海道・東北', '岩手県': '北海道・東北', '宮城県': '北海道・東北', '秋田県': '北海道・東北', '山形県': '北海道・東北', '福島県': '北海道・東北', '茨城県': '関東', '栃木県': '関東', '群馬県': '関東', '埼玉県': '関東', '千葉県': '関東', '東京都': '関東', '神奈川県': '関東', '新潟県': '中部', '富山県': '中部', '石川県': '中部', '福井県': '中部', '山梨県': '中部', '長野県': '中部', '岐阜県': '中部', '静岡県': '中部', '愛知県': '中部', '三重県': '近畿', '滋賀県': '近畿', '京都府': '近畿', '大阪府': '近畿', '兵庫県': '近畿', '奈良県': '近畿', '和歌山県': '近畿', '鳥取県': '中国・四国', '島根県': '中国・四国', '岡山県': '中国・四国', '広島県': '中国・四国', '山口県': '中国・四国', '徳島県': '中国・四国', '香川県': '中国・四国', '愛媛県': '中国・四国', '高知県': '中国・四国', '福岡県': '九州・沖縄', '佐賀県': '九州・沖縄', '長崎県': '九州・沖縄', '熊本県': '九州・沖縄', '大分県': '九州・沖縄', '宮崎県': '九州・沖縄', '鹿児島県': '九州・沖縄', '沖縄県': '九州・沖縄' } region_colors = { '北海道・東北': '#4e9af1', '関東': '#e05c5c', '中部': '#f0a500', '近畿': '#5cb85c', '中国・四国': '#9b59b6', '九州・沖縄': '#f39c12' } df_result['地方'] = df_result['都道府県'].map(region_map) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。r, p = stats.pearsonr(...) — Pythonは複数戻り値を同時に受け取れる(タプルアンパック)。78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | print("\n" + "=" * 60) print("【分析サマリー】") print(f"分析年度: 2022年度 / 対象: 47都道府県") print(f"PCA 第1主成分 寄与率: {explained[0]*100:.1f}%") print(f"PCA 第2主成分 寄与率: {explained[1]*100:.1f}%") print(f"PCA 第3主成分 寄与率: {explained[2]*100:.1f}%") print(f"PC1〜PC3 累積寄与率: {cumulative[2]*100:.1f}%") print() print("PC1 上位負荷量変数:") pc1_load = pd.Series(loadings[0], index=feat_names).abs().sort_values(ascending=False) for feat, val in pc1_load.items(): sign = '+' if loadings[0, feat_names.index(feat)] >= 0 else '-' print(f" {sign}{feat}: {val:.3f}") print() print("PC2 上位負荷量変数:") pc2_load = pd.Series(loadings[1], index=feat_names).abs().sort_values(ascending=False) for feat, val in pc2_load.items(): sign = '+' if loadings[1, feat_names.index(feat)] >= 0 else '-' print(f" {sign}{feat}: {val:.3f}") print() print(f"重回帰 R² = {ols_result.rsquared:.3f}, 調整済みR² = {ols_result.rsquared_adj:.3f}") print(f"F統計量 p値 = {ols_result.f_pvalue:.4f}") print() print("クラスター別都道府県数:") for c in sorted(df_result['Cluster'].unique()): n = (df_result['Cluster'] == c).sum() top = cluster_means[c].idxmax() print(f" クラスター{c} ({n}府県, {top}型)") print("=" * 60) |
============================================================ 【分析サマリー】 分析年度: 2022年度 / 対象: 47都道府県 PCA 第1主成分 寄与率: 38.6% PCA 第2主成分 寄与率: 24.7% PCA 第3主成分 寄与率: 16.8% PC1〜PC3 累積寄与率: 80.1% PC1 上位負荷量変数: +教育・知識集積度: 0.576 +サービス消費水準: 0.524 -農村型高齢化指数: 0.478 -観光特化度: 0.287 -医療特化度: 0.282 -建設・製造活発度: 0.055 PC2 上位負荷量変数: +建設・製造活発度: 0.693 -医療特化度: 0.563 +観光特化度: 0.412 +サービス消費水準: 0.146 -教育・知識集積度: 0.093 +農村型高齢化指数: 0.055 重回帰 R² = 0.667, 調整済みR² = 0.652 F統計量 p値 = 0.0000 クラスター別都道府県数: クラスター1 (9府県, 医療特化度型) クラスター2 (20府県, 観光特化度型) クラスター3 (4府県, 教育・知識集積度型) クラスター4 (14府県, 建設・製造活発度型) ============================================================
sort_values('列名', ascending=False) — 指定列で並べ替え(降順)。plt.subplots(figsize=(W, H)) で図サイズ指定、fig.savefig(..., bbox_inches='tight') で余白を自動で詰めて保存。6変数のLQ類似指標行列(47×6)を StandardScaler で標準化した後、主成分分析を適用する。PCAは多次元データを情報量(分散)を最大保持する方向に射影し、相関の高い変数群を少数の「主成分」に圧縮する手法である。
PC1は教育・知識集積度(+0.576)・サービス消費水準(+0.524)に正の高負荷を持ち、農村型高齢化指数(−0.478)に負の負荷を持つ。PC1スコアが高い都道府県(東京都・神奈川県・大阪府など)は都市型・高消費型で、PC1スコアが低い都道府県(秋田県・山形県・島根県など)は農村型・高齢化型である。
PC2は建設・製造活発度(+0.693)に強い正の負荷を持ち、医療特化度(−0.563)に負の負荷を持つ。製造業が盛んな愛知県・静岡県・滋賀県などがPC2で高スコアを示す一方、医療・福祉産業が特化した地域は低スコア傾向を示す。
PCAは標準化済みデータ行列 X(n×p)の共分散行列 C = XᵀX /(n−1) を固有値分解することで主成分を求める。固有値 λₖ が大きいほど、その主成分が元データの分散を多く説明する。
108 109 110 111 112 113 114 115 116 117 118 | fig, ax = plt.subplots(figsize=(11, 9)) regions_ordered = ['北海道・東北', '関東', '中部', '近畿', '中国・四国', '九州・沖縄'] for reg in regions_ordered: mask = df_result['地方'] == reg ax.scatter( df_result.loc[mask, 'PC1'], df_result.loc[mask, 'PC2'], c=region_colors[reg], label=reg, s=70, alpha=0.85, zorder=3 ) |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。x if cond else y は三項演算子。リスト内包表記と組み合わせると、forとifを1行で書けます。119 120 121 122 123 124 | # 都道府県ラベル(全47) for _, row in df_result.iterrows(): pref_short = row['都道府県'].replace('都', '').replace('道', '').replace('府', '').replace('県', '') ax.annotate(pref_short, (row['PC1'], row['PC2']), fontsize=6.5, ha='center', va='bottom', xytext=(0, 4), textcoords='offset points', color='#333333') |
print はしません。データや図が裏で更新されただけ。次のステップへ進みましょう。for _, row in df.iterrows() — DataFrameを1行ずつ取り出すループ。1点ずつ描画したいときに使用。df[col](1列)と df[[col1, col2]](複数列)でカッコの数が違います。リストを渡していると覚えるとミスを減らせます。125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | # 変数ベクトル(biplot arrows) scale = 2.5 for i, name in enumerate(feat_names): vx = loadings[0, i] * scale vy = loadings[1, i] * scale ax.annotate('', xy=(vx, vy), xytext=(0, 0), arrowprops=dict(arrowstyle='->', color='#CC0000', lw=1.6)) offset_x = 0.08 if vx >= 0 else -0.08 offset_y = 0.08 if vy >= 0 else -0.08 ax.text(vx + offset_x, vy + offset_y, name, fontsize=8, color='#CC0000', fontweight='bold', ha='center', va='center') ax.axhline(0, color='gray', lw=0.5, ls='--', alpha=0.5) ax.axvline(0, color='gray', lw=0.5, ls='--', alpha=0.5) ax.set_xlabel(f'PC1(寄与率 {explained[0]*100:.1f}%)', fontsize=12) ax.set_ylabel(f'PC2(寄与率 {explained[1]*100:.1f}%)', fontsize=12) ax.set_title('図1: PCAバイプロット(47都道府県 × 産業構造指標)', fontsize=13, pad=14) ax.legend(loc='lower right', fontsize=9, framealpha=0.85) ax.grid(True, alpha=0.2) plt.tight_layout() fig_path1 = os.path.join(FIG_DIR, '2020_U3_fig1.png') plt.savefig(fig_path1, dpi=150, bbox_inches='tight') plt.close() print(f"\n図1 保存: {fig_path1}") |
図1 保存: html/figures/2020_U3_fig1.png
ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。fig.savefig(..., bbox_inches='tight') — 余白を自動で詰めて保存。plt.close() でメモリ解放。s[:-n]「末尾n文字を除く」/s[n:]「先頭n文字を除く」。スライス [start:stop:step] はリスト・タプル・文字列共通の基本ワザです。主成分負荷量(loadings)は各変数が各主成分に対してどの程度寄与しているかを示す係数である。赤色(正)が強いほどその主成分が高い方向へ引っ張り、青色(負)が強いほど低い方向へ引っ張る。
| 変数 | PC1(38.6%) | PC2(24.7%) | PC3(16.8%) | 解釈 |
|---|---|---|---|---|
| 農村型高齢化指数 | −0.478 | +0.055 | −0.550 | 農村型 vs 都市型の主軸 |
| 建設・製造活発度 | −0.055 | +0.693 | −0.421 | 製造業集積地(PC2の主軸) |
| サービス消費水準 | +0.524 | +0.146 | −0.368 | 都市型消費経済 |
| 医療特化度 | −0.282 | −0.563 | −0.254 | 医療・福祉特化(製造業と逆方向) |
| 教育・知識集積度 | +0.576 | −0.093 | −0.005 | 都市・大都市圏(PC1最大寄与) |
| 観光特化度 | −0.287 | +0.412 | +0.566 | 観光地型(PC3の主軸) |
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | fig, ax = plt.subplots(figsize=(8, 5)) im = ax.imshow(loadings.T, cmap='RdBu_r', vmin=-1, vmax=1, aspect='auto') plt.colorbar(im, ax=ax, label='負荷量', shrink=0.8) ax.set_xticks(range(3)) ax.set_xticklabels(['PC1', 'PC2', 'PC3'], fontsize=12) ax.set_yticks(range(len(feat_names))) ax.set_yticklabels(feat_names, fontsize=10) for i in range(3): for j in range(len(feat_names)): val = loadings[i, j] text_color = 'white' if abs(val) > 0.5 else 'black' ax.text(i, j, f'{val:.3f}', ha='center', va='center', fontsize=9, color=text_color, fontweight='bold') ax.set_title('図2: 主成分負荷量ヒートマップ(PC1〜PC3)', fontsize=13, pad=12) plt.tight_layout() fig_path2 = os.path.join(FIG_DIR, '2020_U3_fig2.png') plt.savefig(fig_path2, dpi=150, bbox_inches='tight') plt.close() print(f"図2 保存: {fig_path2}") |
図2 保存: html/figures/2020_U3_fig2.png
fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。fig.savefig(..., bbox_inches='tight') — 余白を自動で詰めて保存。plt.close() でメモリ解放。df[col](1列)と df[[col1, col2]](複数列)でカッコの数が違います。リストを渡していると覚えるとミスを減らせます。主成分スコアではなく、標準化済みの元の6変数を用いてWard法による階層的クラスタリングを実行する。Ward法は合併時のクラスター内分散の増分を最小化するように結合するため、コンパクトで均等なクラスターを形成しやすい。
デンドログラムの「枝の長さの大きな段差(gap)」がある水準でカットすることで、自然なクラスター数が決まる。k=4では、各クラスターが地理的・産業的に解釈可能な意味を持つ。
scipy の linkage 関数は method='ward' を指定することでWard法を実行できる。fcluster で任意のクラスター数 k に切り出す。デンドログラムの可視化は dendrogram 関数が担う。
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 203 204 | fig, ax = plt.subplots(figsize=(14, 6)) # カット高さの取得 heights = Z[:, 2] sorted_h = np.sort(heights) cut_height = (sorted_h[-k] + sorted_h[-k+1]) / 2.0 dn = dendrogram( Z, labels=prefs, ax=ax, leaf_rotation=90, leaf_font_size=7.5, color_threshold=cut_height, ) ax.axhline(y=cut_height, color='red', lw=1.8, ls='--', label=f'k={k}カット線') ax.set_title('図3: Ward法デンドログラム(47都道府県 産業構造クラスタリング)', fontsize=12, pad=10) ax.set_ylabel('ユークリッド距離(Ward連結)', fontsize=10) ax.set_xlabel('都道府県', fontsize=10) ax.legend(fontsize=10) ax.grid(axis='y', alpha=0.2) plt.tight_layout() fig_path3 = os.path.join(FIG_DIR, '2020_U3_fig3.png') plt.savefig(fig_path3, dpi=150, bbox_inches='tight') plt.close() print(f"図3 保存: {fig_path3}") |
図3 保存: html/figures/2020_U3_fig3.png
fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。ax.axhline / ax.axvline — 水平/垂直の点線。平均線や基準線として定番。fig.savefig(..., bbox_inches='tight') — 余白を自動で詰めて保存。plt.close() でメモリ解放。s[:-n]「末尾n文字を除く」/s[n:]「先頭n文字を除く」。スライス [start:stop:step] はリスト・タプル・文字列共通の基本ワザです。Ward法 k=4 クラスタリングの結果、以下の4つの産業類型が得られた。各クラスターの「顔」となる変数(LQが最も高い指標)を産業類型名として解釈する。
主な府県: 京都府・大阪府・兵庫県・広島県・福岡県・長崎県 など
特徴: 医療・福祉施設の集積が全国平均を上回る。都市部に位置しながら大学病院・医療産業が強みで、高齢化に伴う医療需要が産業特化を促進。
主な府県: 北海道・青森・岩手・秋田・山形・新潟・沖縄 など
特徴: 旅館・観光施設密度が高く、農村型地域が多い。第一次産業が相対的に強く、製造業・知識産業の集積は低め。自然資源を活かした観光業が地域経済の柱。
主な府県: 埼玉県・東京都・神奈川県・滋賀県
特徴: 教育費・知識産業集積度が際立って高い大都市圏。大学・高等教育機関の集積と高所得層の教育投資が特徴。サービス消費水準も最高水準。
主な府県: 宮城・茨城・栃木・群馬・千葉・愛知・静岡・岡山 など
特徴: 着工建築物密度・製造業関連指標が高い。工業団地・製造業クラスターを抱える県が多く、安定した雇用と建設投資が経済基盤。
| 指標 | クラスター1 医療特化型 |
クラスター2 観光・農村型 |
クラスター3 教育・知識型 |
クラスター4 製造・建設型 |
|---|---|---|---|---|
| 農村型高齢化指数 | 1.05 | 1.09 | 0.80 | 0.96 |
| 建設・製造活発度 | 0.95 | 0.86 | 0.99 | 1.24 |
| サービス消費水準 | 1.02 | 0.96 | 1.09 | 1.01 |
| 医療特化度 | 1.11 | 0.95 | 0.98 | 0.99 |
| 教育・知識集積度 | 1.20 | 0.66 | 2.08 | 0.94 |
| 観光特化度 | 0.74 | 1.38 | 0.26 | 0.81 |
レーダーチャートはカテゴリ別の多変量プロファイルを直感的に比較するのに適している。matplotlibでは極座標系(polar projection)を使って実装する。
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | import os import numpy as np import pandas as pd import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt import statsmodels.api as sm from scipy import stats from sklearn.preprocessing import StandardScaler from sklearn.decomposition import PCA from scipy.cluster.hierarchy import dendrogram, linkage, fcluster plt.rcParams['font.family'] = 'Hiragino Sans' plt.rcParams['axes.unicode_minus'] = False plt.rcParams['figure.dpi'] = 150 FIG_DIR = 'html/figures' DATA_B = 'data/raw/SSDSE-B-2026.csv' os.makedirs(FIG_DIR, exist_ok=True) 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) |
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)。pd.read_csv(...) でCSVを読み込みます。encoding='cp932' は日本語Windows由来の文字コード、header=1 は「2行目を列名として使う」。df['地域コード'].str.match(r'^R\d{5}', ...) — 正規表現で「R+数字5桁」の行(47都道府県)だけTrueにし、真偽値で行をフィルタ。.astype(int) — 列を整数に変換(年度などを数値比較するため)。StandardScaler().fit_transform(X) — 各列を「平均0・分散1」に標準化。単位が違う変数のβを比較可能に。f"...{x}..." はf-string。文字列の中に {変数} と書くだけで埋め込めて、{x:.2f} のように書式も指定できます。229 230 231 232 | # 2022年断面データ(47都道府県) df = df_b[df_b['年度'] == 2022].copy().reset_index(drop=True) prefs = df['都道府県'].tolist() print(f"分析対象: {len(df)} 都道府県(2022年度)") |
分析対象: 47 都道府県(2022年度)
df['A'] / df['B'] — pandasの列同士の四則演算は要素ごと(element-wise)。forループ不要なのが強み。233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 | scaler = StandardScaler() X_scaled = scaler.fit_transform(X_raw) pca = PCA(n_components=3) X_pca = pca.fit_transform(X_scaled) explained = pca.explained_variance_ratio_ cumulative = np.cumsum(explained) loadings = pca.components_ # shape: (3, 6) print(f"\nPCA 累積寄与率:") for i, (e, c) in enumerate(zip(explained, cumulative)): print(f" PC{i+1}: {e*100:.1f}% 累積 {c*100:.1f}%") print(f"\n主成分負荷量 (PC1〜PC3):") load_df = pd.DataFrame(loadings.T, index=feat_names, columns=['PC1', 'PC2', 'PC3']) print(load_df.round(3)) |
PCA 累積寄与率:
PC1: 38.6% 累積 38.6%
PC2: 24.7% 累積 63.3%
PC3: 16.8% 累積 80.1%
主成分負荷量 (PC1〜PC3):
PC1 PC2 PC3
農村型高齢化指数 -0.478 0.055 -0.550
建設・製造活発度 -0.055 0.693 -0.421
サービス消費水準 0.524 0.146 -0.368
医療特化度 -0.282 -0.563 -0.254
教育・知識集積度 0.576 -0.093 -0.005
観光特化度 -0.287 0.412 0.566StandardScaler().fit_transform(X) — 各列を「平均0・分散1」に標準化。単位が違う変数のβを比較可能に。.map() は「1対1の置き換え」、.apply() は「関数を当てる」。辞書なら .map()、ロジックなら .apply()。251 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 282 283 284 285 286 287 288 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 | cluster_means = {} for c in sorted(df_result['Cluster'].unique()): mask = df_result['Cluster'] == c cluster_means[c] = X_raw[mask].mean() # クラスター名の解釈(値から自動設定) cluster_labels = {} for c, means in cluster_means.items(): top_feat = means.idxmax() cluster_labels[c] = f'クラスター{c}\n({top_feat}型)' N_feat = len(feat_names) angles = np.linspace(0, 2 * np.pi, N_feat, endpoint=False).tolist() angles += angles[:1] # 閉じる feat_short = ['農村型\n高齢化', '建設・\n製造', 'サービス\n消費', '医療\n特化', '教育・\n知識', '観光\n特化'] cluster_colors_radar = ['#4e9af1', '#e05c5c', '#5cb85c', '#f0a500'] fig, axes = plt.subplots(2, 2, figsize=(11, 9), subplot_kw=dict(projection='polar')) axes = axes.flatten() for idx, c in enumerate(sorted(cluster_means.keys())): ax = axes[idx] vals = cluster_means[c].values.tolist() vals += vals[:1] ax.plot(angles, vals, color=cluster_colors_radar[idx], lw=2, marker='o', markersize=5) ax.fill(angles, vals, color=cluster_colors_radar[idx], alpha=0.22) # 全国平均(LQ=1.0) ax.plot(angles, [1.0] * (N_feat + 1), color='gray', lw=1, ls='--', alpha=0.6) ax.set_xticks(angles[:-1]) ax.set_xticklabels(feat_short, fontsize=8.5) ax.set_ylim(0, 2.0) ax.set_yticks([0.5, 1.0, 1.5, 2.0]) ax.set_yticklabels(['0.5', '1.0', '1.5', '2.0'], fontsize=7, color='gray') n_pref = (df_result['Cluster'] == c).sum() ax.set_title( f'クラスター {c} ({n_pref}府県)\n{cluster_means[c].idxmax()} 型', fontsize=9, pad=12, fontweight='bold', color=cluster_colors_radar[idx] ) # 代表都道府県をタイトル下に表示 pref_list = df_result[df_result['Cluster'] == c]['都道府県'].tolist() pref_short_str = '・'.join( [p.replace('都', '').replace('道', '').replace('府', '').replace('県', '') for p in pref_list[:6]] ) if len(pref_list) > 6: pref_short_str += '…' ax.text(0, -0.18, pref_short_str, transform=ax.transAxes, fontsize=6.5, ha='center', va='top', color='#555555') fig.suptitle('図4: クラスター別産業特性レーダーチャート(全国平均=1.0)', fontsize=12, y=1.01, fontweight='bold') plt.tight_layout() fig_path4 = os.path.join(FIG_DIR, '2020_U3_fig4.png') plt.savefig(fig_path4, dpi=150, bbox_inches='tight') plt.close() print(f"図4 保存: {fig_path4}") |
図4 保存: html/figures/2020_U3_fig4.png
fig, ax = plt.subplots(...) — 図全体(fig)と軸(ax)を作る定番。以降は ax.bar(...) 等で操作。fig.savefig(..., bbox_inches='tight') — 余白を自動で詰めて保存。plt.close() でメモリ解放。np.cumsum(arr) は累積和、np.linspace(a, b, n) は「aからbを等間隔でn個」。NumPyの定石です。315 316 317 318 319 320 | y_reg = df['消費支出(二人以上の世帯)'].values.astype(float) # PC1, PC2, PC3 を説明変数 X_reg = sm.add_constant(X_pca[:, :2]) ols_result = sm.OLS(y_reg, X_reg).fit() print("\n重回帰分析結果(目的変数: 消費支出水準):") print(ols_result.summary()) |
重回帰分析結果(目的変数: 消費支出水準):
OLS Regression Results
==============================================================================
Dep. Variable: y R-squared: 0.667
Model: OLS Adj. R-squared: 0.652
Method: Least Squares F-statistic: 44.08
Date: Mon, 18 May 2026 Prob (F-statistic): 3.10e-11
Time: 11:23:43 Log-Likelihood: -503.85
No. Observations: 47 AIC: 1014.
Df Residuals: 44 BIC: 1019.
Df Model: 2
Covariance Type: nonrobust
==============================================================================
coef std err t P>|t| [0.025 0.975]
------------------------------------------------------------------------------
const 2.896e+05 1651.093 175.417 0.000 2.86e+05 2.93e+05
x1 9947.0180 1085.552 9.163 0.000 7759.232 1.21e+04
x2 2778.9839 1355.071 2.051 0.046 48.017 5509.950
==============================================================================
Omnibus: 7.148 Durbin-Watson: 1.787
Prob(Omnibus): 0.028 Jarque-Bera (JB): 7.120
Skew: -0.570 Prob(JB): 0.0284
Kurtosis: 4.529 Cond. No. 1.52
==============================================================================
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.sm.add_constant(X) — 切片項(定数1の列)を先頭に追加。statsmodelsで必須。sm.OLS(y, X).fit() — 最小二乗法でモデルを推定。model.params, model.pvalues, model.conf_int() で結果取得。{値:.2f}(小数2桁)、{値:,}(3桁区切り)、{値:>10}(右寄せ10桁)など、覚えると出力が一気に整います。47都道府県の産業構造を6つのLQ類似指標で捉え、PCA・Ward法クラスタリングによる分析の結果、以下の知見が得られた。
| データ | 出典 |
|---|---|
| SSDSE-B(都道府県別データ) | 統計数理研究所 SSDSE(社会・人口統計体系) https://www.ism.ac.jp/ssdse/ |
| 産業立地係数(LQ)の概念 | Isard, W. (1960). Methods of Regional Analysis. MIT Press. 地域経済学における基本指標 |
| Ward法クラスタリング | Ward, J. H. (1963). Hierarchical grouping to optimize an objective function. Journal of the American Statistical Association, 58, 236–244. |
本教育用コードはSSDSE-B-2026.csvの実データのみを使用(合成データなし)。47都道府県・2022年度断面データ。
統計分析の解釈で初心者がやりがちな勘違いをまとめます。特に「相関と因果の混同」「p値の過信」は研究現場でもよく起きる落とし穴です。本文を読む前にも、読んだ後にも、目を通してみてください。
統計の基本用語を初心者向けに解説します。本文中で見慣れない言葉が出てきたら、ここに戻って確認してください。
統計手法について「何のためか」「結果をどう読むか」を初心者向けに解説します。
この研究をさらに発展させるための3つの方向性を示します。「今回わかったこと(X)」から「次に検証すべき仮説(Y)」を立て、「具体的に何をするか(Z)」まで考えてみましょう。
学んだだけでは身につきません。実際に手を動かすのが最強の学習方法です。本論文のスクリプトをベースに、以下のチャレンジに挑戦してみてください。難易度別に5つ用意しました。
本論文で学んだ手法は、研究の世界だけでなく、行政・企業・NPO の現場でも様々に活用されています。具体的なシーンを紹介します。
この論文を読んで初心者が抱きやすい疑問に、教育的観点から答えます。