この教材は原論文と同じデータを読み込み、同じ手順で計算し直しています。画面に出る数値と図は、あなたのブラウザがその場で計算した結果です。
| 原論文が使ったデータ | 住民基本台帳人口移動報告 分析単位:市区町村 中核手法:修正重力モデル・Elastic Net 回帰 |
|---|---|
| この教材が使うデータ |
|
| 原論文(PDF) | 若者の大都市から地方への移動要因を探る ―修正重力モデルによる分析― 総務大臣賞/坂本 大樹(東京大学大学院情報理工学系研究科) 川本 晃大(早稲田大学大学院基幹理工学研究科) |
▶ ここに書いてある分析は、ページ下部の「🐍 ブラウザで動かす」でそのまま実行できます(インストール不要)。動かしているのは code/2021_U1_daijin.py(279 行)そのものです。
このページの説明変数の作成と大都市/地方の比較・相関行列(図1・図2)は、以下の手順で自分で再現できます。コードの編集は不要です。(修正重力モデルの回帰係数=表2・図3・図4は、住民基本台帳の移動フローと市区町村間の距離が必要で、これらは SSDSE に未収録のため、原論文の報告値を可視化します。)
data/raw/ フォルダに入れます。html/figures/ に自動保存されます。
日本は2008年をピークに人口減少に転じ、その影響は地方でとりわけ深刻だ。原因の一つが東京への一極集中で、進学・就職に加え「魅力・利便性・自由度」が若者を大都市に引き寄せている。政府は「まち・ひと・しごと創生」で地方への新しい人の流れを作ることを目標に掲げるが、三大都市圏の人口は増加・横ばいが続く。
地方創生の一手は「大都市から地方への人の流れ」を作ることだ。ではその流れを促す要因は何か。 本研究は、移動人口の約7割を占める20〜40代の若者にしぼり、地域間の人口移動を記述する修正重力モデルで、大都市から地方への移動を促す要因を定量的に探る。
大学生・一般の部 住民基本台帳人口移動報告 SSDSE-A / SSDSE-D 修正重力モデル(対数線形回帰) Elastic Net 回帰
| 役割 | 変数 | 出典(原論文) |
|---|---|---|
| 被説明変数 | 20〜40代の大都市→地方 移動人口量(10人未満の移動は除外) | 住民基本台帳人口移動報告(e-Stat, 平成27年) |
| 基本要因 | 総人口(移動元x・移動先y)、移動間距離 | 総人口=SSDSE-A / 距離=緯度経度+ヒュベニの公式(アマノ技研) |
| 所得 | 納税義務者1人あたり課税対象所得(x・y) | 総務省「市町村税課税状況等の調」 |
| 雇用機会 | 第1・2・3次産業就業者率、完全失業率(x・y) | SSDSE-Aから筆者算出 |
| 教育 | 学校数(10万人あたり)、子供を対象とした活動 | 学校数=SSDSE-A算出 / 活動=SSDSE-D(都道府県別) |
| 生活の質 | 安全な生活のための活動、スポーツ・文化・芸術・学術の活動 | SSDSE-D(都道府県別) |
| 行政サービス | 福祉施設数(10万人あたり) | SSDSE-A から筆者算出 |
まず、原論文が SSDSE-A から筆者算出したのと同じ手順で説明変数を作る。就業者数から産業別就業者率と完全失業率を、学校・福祉施設の数と総人口から「人口10万人あたりの数」を計算する。これは移動フローを使わないので実データで再現できる。
header=2(0行目=コード, 1行目=年度, 2行目=変数名)で読み込み、市区町村行だけ残す。原論文3.3節と同じ方法で産業別就業者率・完全失業率・人口10万人あたり学校数/福祉施設数を算出し、東京23区+政令市20市を「大都市(移動元)」、人口20万人未満を「地方(移動先)」に区分する。62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | print('=== SSDSE-A 読み込みと説明変数の算出【実再現】 ===') raw = pd.read_csv(DATA_A, encoding='cp932', header=2) raw = raw.rename(columns={raw.columns[0]: '地域コード', raw.columns[1]: '都道府県', raw.columns[2]: '市区町村'}) df = raw[raw['地域コード'].astype(str).str.match(r'^R\d{5}$', na=False)].copy() num_cols = ['総人口', '就業者数', '第1次産業就業者数', '第2次産業就業者数', '第3次産業就業者数', '完全失業者数', '幼稚園数', '小学校数', '中学校数', '高等学校数', '公民館数', '図書館数', '一般病院数', '保育所等数(基本票)'] for c in num_cols: df[c] = pd.to_numeric(df[c], errors='coerce') # 総人口0・就業者0の市町村(2020年国勢調査で人口0の避難区域等)は率が定義できないため除外 df = df[(df['総人口'] > 0) & (df['就業者数'] > 0)].copy() # 原論文と同じ変数の作り方(3.3 使用した説明変数)を SSDSE-A-2025 に適用 df['第1次産業就業者率'] = df['第1次産業就業者数'] / df['就業者数'] * 100 df['第2次産業就業者率'] = df['第2次産業就業者数'] / df['就業者数'] * 100 df['第3次産業就業者率'] = df['第3次産業就業者数'] / df['就業者数'] * 100 df['完全失業率'] = df['完全失業者数'] / (df['完全失業者数'] + df['就業者数']) * 100 df['学校数_10万人'] = (df['幼稚園数'] + df['小学校数'] + df['中学校数'] + df['高等学校数']) / df['総人口'] * 1e5 df['福祉施設数_10万人'] = (df['公民館数'] + df['図書館数'] + df['一般病院数'] + df['保育所等数(基本票)']) / df['総人口'] * 1e5 # 大都市(移動元)=東京23特別区+政令指定都市20市、地方(移動先)=総人口20万人未満 seirei = ['札幌市', '仙台市', 'さいたま市', '千葉市', '横浜市', '川崎市', '相模原市', '新潟市', '静岡市', '浜松市', '名古屋市', '京都市', '大阪市', '堺市', '神戸市', '岡山市', '広島市', '北九州市', '福岡市', '熊本市'] is_ward = (df['都道府県'] == '東京都') & df['市区町村'].str.endswith('区', na=False) is_seirei = df['市区町村'].isin(seirei) df['地域区分'] = np.where(is_ward | is_seirei, '大都市(移動元)', np.where(df['総人口'] < 200000, '地方(移動先)', 'その他(中核市等)')) n_big = (df['地域区分'] == '大都市(移動元)').sum() n_local = (df['地域区分'] == '地方(移動先)').sum() print(f'市区町村総数: {len(df)}') print(f'大都市(移動元): {n_big} 市区 / 地方(移動先): {n_local} 市町村 / その他(中核市等): {(df["地域区分"]=="その他(中核市等)").sum()}') print('※ 原論文(平成27年国勢調査)では 43 市区 / 1606 市町村。収録版は2020年国勢調査ベースのため件数は近似。') |
=== SSDSE-A 読み込みと説明変数の算出【実再現】 === 市区町村総数: 1740 大都市(移動元): 43 市区 / 地方(移動先): 1608 市町村 / その他(中核市等): 89 ※ 原論文(平成27年国勢調査)では 43 市区 / 1606 市町村。収録版は2020年国勢調査ベースのため件数は近似。
106 107 108 109 110 111 112 113 114 | show_vars = ['総人口', '第1次産業就業者率', '第2次産業就業者率', '第3次産業就業者率', '完全失業率', '学校数_10万人', '福祉施設数_10万人'] for grp in ['大都市(移動元)', '地方(移動先)']: sub = df[df['地域区分'] == grp] print(f'\n【{grp}】 N={len(sub)}') stat = sub[show_vars].agg(['mean', 'std', 'min', 'max']).T stat.columns = ['平均', '標準偏差', '最小', '最大'] print(stat.round(2).to_string()) print() |
=== 表1の再現:説明変数の基本統計量(大都市 vs 地方)【実再現・SSDSE-A-2025】 ===
【大都市(移動元)】 N=43
平均 標準偏差 最小 最大
総人口 872844.98 745683.00 66680.00 3777491.00
第1次産業就業者率 0.60 0.88 0.02 3.52
第2次産業就業者率 16.20 5.13 8.25 33.41
第3次産業就業者率 79.41 5.30 61.68 87.16
完全失業率 3.71 0.63 1.86 4.93
学校数_10万人 27.27 9.91 18.20 82.48
福祉施設数_10万人 34.12 7.90 23.52 67.49
【地方(移動先)】 N=1608
平均 標準偏差 最小 最大
総人口 36098.59 41467.46 169.00 199849.00
第1次産業就業者率 11.01 9.91 0.14 75.62
第2次産業就業者率 25.06 8.12 1.74 52.50
第3次産業就業者率 61.98 9.16 20.67 96.12
完全失業率 3.63 1.19 0.00 10.63
学校数_10万人 69.38 91.46 0.00 1891.89
福祉施設数_10万人 96.50 155.70 0.00 3169.01表1の数値を箱ひげ図にして、大都市(移動元)と地方(移動先)で説明変数がどう分かれるかを一目で確かめる。これは SSDSE-A-2025 からの実再現である(原論文は平成27年、本図は2020年国勢調査ベース)。
修正重力モデルは多数の説明変数を同時に扱う。ところが就業構造など互いに強く相関する変数があると、通常の重回帰は係数の推定が不安定になる(多重共線性)。その様子を相関行列で確かめる。
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | corr_vars = ['総人口', '第1次産業就業者率', '第2次産業就業者率', '第3次産業就業者率', '完全失業率', '学校数_10万人', '福祉施設数_10万人'] short = {'総人口': '総人口', '第1次産業就業者率': '第1次産業', '第2次産業就業者率': '第2次産業', '第3次産業就業者率': '第3次産業', '完全失業率': '完全失業率', '学校数_10万人': '学校数', '福祉施設数_10万人': '福祉施設数'} cmat = loc[corr_vars].corr() cmat_disp = cmat.rename(index=short, columns=short) print('(地方=移動先市町村 N={} での相関)'.format(len(loc))) print(cmat_disp.round(2).to_string()) # 最も強い相関ペア pairs = [] cols = corr_vars for i in range(len(cols)): for j in range(i + 1, len(cols)): pairs.append((abs(cmat.iloc[i, j]), short[cols[i]], short[cols[j]], cmat.iloc[i, j])) pairs.sort(reverse=True) print('\n相関の強い変数ペア上位3組(多重共線性の目安):') for a, x, y, v in pairs[:3]: |
=== 説明変数間の相関行列【実再現】:多重共線性と Elastic Net の必要性 ===
(地方=移動先市町村 N=1608 での相関)
総人口 第1次産業 第2次産業 第3次産業 完全失業率 学校数 福祉施設数
総人口 1.00 -0.47 0.12 0.34 0.15 -0.27 -0.24
第1次産業 -0.47 1.00 -0.46 -0.62 -0.36 0.27 0.23
第2次産業 0.12 -0.46 1.00 -0.40 0.12 -0.25 -0.10
第3次産業 0.34 -0.62 -0.40 1.00 0.28 -0.03 -0.14
完全失業率 0.15 -0.36 0.12 0.28 1.00 -0.25 -0.21
学校数 -0.27 0.27 -0.25 -0.03 -0.25 1.00 0.27
福祉施設数 -0.24 0.23 -0.10 -0.14 -0.21 0.27 1.00
相関の強い変数ペア上位3組(多重共線性の目安):
第1次産業 × 第3次産業: r=-0.62
総人口 × 第1次産業: r=-0.47
第1次産業 × 第2次産業: r=-0.46
M(i,j) = a0 · P(i)^a1 · P(j)^a2 / D(i,j)^a3log M = logβ0 + a1·logP(i) + a2·logP(j) − a3·logD + β1·logY(i) + β2·logY(j) + Σαn·logX(i,n) + Σγm·logX(j,m)
修正重力モデルを Elastic Net で推定した結果が原論文の表2である。移動フローと距離のデータが必要なので、本ページでは再計算せず、報告値をそのまま可視化する。符号(正負)と絶対値の大きさで読み解くのが原論文の方針だ。
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 | reported = [ ('総人口_x', 0.061648), ('総人口_y', 0.069374), ('移動間距離', -0.568857), ('第1次産業就業者率_x', 0.134868), ('第1次産業就業者率_y', -0.029745), ('第2次産業就業者率_x', -0.060645), ('第2次産業就業者率_y', -0.130457), ('第3次産業就業者率_x', 0.078206), ('完全失業率_x', 0.015544), ('完全失業率_y', -0.032623), ('学校数_x', 0.114437), ('学校数_y', 0.107113), ('福祉施設数_x', 0.134630), ('福祉施設数_y', 0.041808), ('子供を対象とした活動_x', -0.044284), ('子供を対象とした活動_y', 0.010989), ('スポーツ文化芸術学術活動_x', -0.076732), ('スポーツ文化芸術学術活動_y', -0.031711), ('安全な生活のための活動_x', 0.062168), ('安全な生活のための活動_y', -0.023933), ('課税対象所得_x', -0.086024), ('課税対象所得_y', -0.042550), ] rep = pd.DataFrame(reported, columns=['変数名', '回帰係数']) print('(原論文 表2 の報告値。x=移動元、y=移動先。※移動フロー・距離は SSDSE 未収録のため再計算不可)') rep_sorted = rep.reindex(rep['回帰係数'].abs().sort_values(ascending=False).index) for _, r in rep_sorted.iterrows(): print(f' {r["変数名"]:<24} {r["回帰係数"]:+.6f}') print(f'\n絶対値最大:移動間距離 {rep.loc[rep["変数名"]=="移動間距離","回帰係数"].iloc[0]:+.6f}(長距離移動が人口移動のネック)') |
=== 表2の転記:修正重力モデル×Elastic Net の回帰係数【報告値・再計算ではない】 === (原論文 表2 の報告値。x=移動元、y=移動先。※移動フロー・距離は SSDSE 未収録のため再計算不可) 移動間距離 -0.568857 第1次産業就業者率_x +0.134868 福祉施設数_x +0.134630 第2次産業就業者率_y -0.130457 学校数_x +0.114437 学校数_y +0.107113 課税対象所得_x -0.086024 第3次産業就業者率_x +0.078206 スポーツ文化芸術学術活動_x -0.076732 総人口_y +0.069374 安全な生活のための活動_x +0.062168 総人口_x +0.061648 第2次産業就業者率_x -0.060645 子供を対象とした活動_x -0.044284 課税対象所得_y -0.042550 福祉施設数_y +0.041808 完全失業率_y -0.032623 スポーツ文化芸術学術活動_y -0.031711 第1次産業就業者率_y -0.029745 安全な生活のための活動_y -0.023933 完全失業率_x +0.015544 子供を対象とした活動_y +0.010989 絶対値最大:移動間距離 -0.568857(長距離移動が人口移動のネック)
同じ指標でも、移動元(大都市)と移動先(地方)で係数の符号が変わることがある。その符号反転を左右の棒で対比する。移動フローが必要なので報告値の可視化である。
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 | pairs_xy = [ ('総人口', 0.061648, 0.069374), ('第1次産業就業者率', 0.134868, -0.029745), ('第2次産業就業者率', -0.060645, -0.130457), ('完全失業率', 0.015544, -0.032623), ('学校数', 0.114437, 0.107113), ('福祉施設数', 0.134630, 0.041808), ('子供を対象とした活動', -0.044284, 0.010989), ('安全な生活のための活動', 0.062168, -0.023933), ('課税対象所得', -0.086024, -0.042550), ] pxy = pd.DataFrame(pairs_xy, columns=['変数', '移動元x', '移動先y']) print('(同じ指標でも移動元と移動先で符号が変わる項目に注目。原論文 表2 の報告値)') for _, r in pxy.iterrows(): flip = ' ← 符号反転' if r['移動元x'] * r['移動先y'] < 0 else '' print(f' {r["変数"]:<20} x={r["移動元x"]:+.4f} y={r["移動先y"]:+.4f}{flip}') |
=== 移動元(x) と 移動先(y) の係数対比【報告値・再計算ではない】 === (同じ指標でも移動元と移動先で符号が変わる項目に注目。原論文 表2 の報告値) 総人口 x=+0.0616 y=+0.0694 第1次産業就業者率 x=+0.1349 y=-0.0297 ← 符号反転 第2次産業就業者率 x=-0.0606 y=-0.1305 完全失業率 x=+0.0155 y=-0.0326 ← 符号反転 学校数 x=+0.1144 y=+0.1071 福祉施設数 x=+0.1346 y=+0.0418 子供を対象とした活動 x=-0.0443 y=+0.0110 ← 符号反転 安全な生活のための活動 x=+0.0622 y=-0.0239 ← 符号反転 課税対象所得 x=-0.0860 y=-0.0425
原論文は Cadwallader(1996) の人口移動6要因のうち、若者に不要な「年齢」を除いた5要因に変数を対応づけて解釈した。以下の係数はすべて原論文 表2 の報告値である。
| 変数 | 係数(報告値) | 解釈 |
|---|---|---|
| 総人口(移動元x/移動先y) | +0.062/+0.069 | ともに正。人口が多い地域ほど移動が活発(重力モデルどおり)。 |
| 移動間距離 | −0.569 | 絶対値最大。長距離移動が最大のネック。 |
課税対象所得は移動元・移動先ともに負(x=−0.086、y=−0.043)。所得の高い地域ではなく生活コストの低い地域へ移動する傾向。荒川(2020)の主張を支持。
完全失業率は移動元で正(+0.016)・移動先で負(−0.033) = 失業率の低い地域へ移入(Herzog型)。第1次産業就業者率は移動元で正(+0.135)・移動先で負(−0.030) = 農林漁業の盛んな地方へ移出。第2次産業は移動元・移動先とも負。
移動先の学校数(+0.107)・子供を対象とした活動(+0.011)がともに正。地方への移動が必要になったとき、学校の多い・子供の活動が盛んな地域が選ばれる。Cadwalladerの指摘どおり。
スポーツ・文化・芸術・学術の活動は移動元・移動先とも負。安全な生活のための活動は移動元で正・移動先で負。この一部は「更なる検証が必要」と原論文も留保している。
移動先の福祉施設数(+0.042)が正。地方への移動時に、福祉施設の多い地域が選ばれる。荒川(2018)とも整合。
本研究は、移動人口の約7割を占める20〜40代の若者について、大都市から地方への人口移動要因を修正重力モデル+Elastic Netで定量分析した。その結果、移動先の学校数・子供を対象とした活動・福祉施設数が若者の移動に正の影響を与えること、そしてこれらの充実が地方創生の促進につながることが示唆された。国内で修正重力モデル×Elastic Net により若者の人口移動を分析した例はなく、変数間の関係を明らかにした点に貢献がある。
このページの説明変数の作成・大都市/地方比較・相関行列(図1・図2)は、以下から再現できます。
🐍 再現コード(.py) 📊 SSDSE-A-2025.csv
※ 修正重力モデルの回帰係数(表2・図3・図4)は、住民基本台帳の移動フローと市区町村間の距離が必要で SSDSE に未収録のため、原論文の報告値を可視化したものです(再計算ではありません)。図1・図2 の再計算値も、収録版が2020年国勢調査ベースのため原論文(平成27年)とは完全一致しません。
この研究を読むとき・まねするときに陥りやすい誤解を整理する。
クリックすると各用語の詳しい解説ページに移動できます。
この研究で使われている手法を、考え方の順に説明する。
この研究の「符号が一致しない要因があった/モデルは1つだけ検証」という限界は、次の研究の出発点になる。
再現コードを少し変えるだけで試せる課題を、易しい順に用意した。
200000 を 100000 に変え、大都市・地方・その他の件数がどう変わるか確かめよう。show_vars に「第2次産業就業者率」や新たに作った変数を足して、大都市/地方の基本統計量を比べよう。cmat = loc[...] の loc を big に変えて、大都市43市区での変数間相関が地方とどう違うか観察しよう。log M = a1·logP_i + a2·logP_j − a3·logD を scikit-learn の ElasticNet で推定し、対数線形回帰の感触をつかもう(本物の移動フローが無くても手順は学べる)。「地域間の移動・フローを人口と距離+社会経済要因で説明する」重力モデルの発想は、現場でも広く使われている。
この研究を読んで浮かびやすい疑問に答える。
この論文を理解できたか、クリックで確認しましょう。間違えても解説が出ます。
このページの分析は、この画面の中でそのまま実行できます。
Python をインストールする必要も、CSV をダウンロードする必要もありません。
下のセルの 「▶ ブラウザで実行」 を上から順に押すか、
「▶ 最初から全部実行」 で一気に流してください。
表示されるのは、本文の図表とまったく同じ計算の結果です
(動かしているのは再現スクリプト code/2021_U1_daijin.py そのもの)。
コードは書き換えられます。「✏️ 書き換える」で数字や変数を変えて ▶ を押すと、 結果がどう変わるかをその場で確かめられます(元に戻すボタンつき)。 ページを開いた時点で裏で実行環境の準備が始まっているので、待ち時間はほとんどありません。 初回だけ実行環境の取得にインターネット接続が必要です。