← 論文一覧に戻る 📦 データカタログ 📚 用語集
2020年度 統計データ分析コンペティション
優秀賞(大学生・一般の部)

ふるさと納税は地方創生の切り札になりえるか

―固定効果モデルを用いたパネルデータ分析―

森 將暁(一橋大学 商学部 経営学科)

🔬 再現カルテ — 原論文と同じ種類のデータで再現(細部は簡略化)

この教材は原論文と同じ種類のデータで計算し直しています。ただし工程の一部は、学習しやすさのために簡略化しています(下の「できないこと」を参照)。

原論文が使ったデータ総務省 ふるさと納税に関する現況調査・SSDSE(市区町村のすがた)・国勢調査・人口動態統計・住民基本台帳人口移動報告・市町村税課税状況等の調
分析単位:市区町村
中核手法:固定効果モデル・パネルデータ分析・回帰分析
この教材が使うデータ
原論文(PDF)ふるさと納税は地方創生の切り札になりえるか ―固定効果モデルを用いたパネルデータ分析―
優秀賞/森 將暁(一橋大学 商学部 経営学科)
✅ この教材でできること
  • 原論文の中核手法(パネルデータ分析・回帰分析)を実データで実行できる
  • 分析に使うデータは同梱済みで、ブラウザ上でそのまま読み込める
  • 数値・図を最後まで再計算できる(重回帰分析(OLS)・パネルデータ分析)
⚠️ この教材ではできないこと(原論文との違い)
  • 手順の一部が原論文と違う:論文=2012/2014/2016 の3時点パネル → 教材=SSDSE-A 各版を重ねた 2020〜2023 の4時点(分析単位はどちらも市区町村)
  • 手順の一部が原論文と違う:論文の仮説1(納税義務者1人当たり課税対象所得)は SSDSE に収録が無く未再現。総務省『市町村税課税状況等の調』が必要

▶ ここに書いてある分析は、ページ下部の「🐍 ブラウザで動かす」でそのまま実行できます(インストール不要)。動かしているのは code/2020_U2_yushu_furusato.py(264 行)そのものです。

この論文が問うたこと

ふるさと納税は、生まれ故郷や応援したい自治体に寄付をすると、返礼品がもらえて税金も 軽くなる制度です。制度が始まってから受入額はずっと増え続けています。

ただし、この制度が本来ねらっていたのは地方創生、つまり 「地域の経済が元気になること」と「人が地域に増えること」でした。 寄付が集まっているのはたしかですが、ねらいどおりの効果が出ているのかは きちんと検証されていませんでした。

原論文は、全国の市区町村を何年も追いかけたデータ(パネルデータ)を作り、 固定効果モデルという方法で次の 2 つの仮説を検証しました。

仮説1:ふるさと納税は地域の経済活動を活発化させる
仮説2:ふるさと納税は地域の人口を増加させる

結論は、「経済効果はあるが、人口増加には効果がない」というものでした。

なぜ「固定効果モデル」なのか

単純に「ふるさと納税を多く集めた自治体ほど人口が増えているか」を調べるだけでは、 答えを間違えます。

たとえば、もともと魅力のある自治体は、 良い返礼品を用意できるので寄付も集まりますし、そもそも住みたい人も多いはずです。 このとき「寄付が多い自治体は人口も多い」という関係が見えても、 それは寄付の効果ではなく、その自治体がもともと持っている魅力の効果です。 こういう見落とし変数のことを欠落変数といいます。

固定効果モデルは、この問題を「その自治体の中での動きだけを見る」ことで解決します。 自治体ごとの平均を引いてしまえば、時間で変わらない性質はすべて消えるからです。 下の Step1 では、それを実際の数字で見せます。

分析を最初から追いかける

ここから先は、再現スクリプト code/2020_U2_yushu_furusato.py を 上から順に動かしたものです。表示している実行結果は、すべて実際に動かして出た値です。

やってみようデータ読み込み(市区町村パネル)
🎯 このステップの目的
この分析で使うデータを読み込み、規模を確かめます。
📝 コード
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# ================================================================
# ■ データ読み込み(市区町村パネル)
# ================================================================
df = pd.read_csv(DATA)
df = df.dropna(subset=['直近3年受入額_百万円', '人口増減率', '10万人当たり医師数'])

print("=" * 74)
print("■ 市区町村パネル(ふるさと納税 × 人口動態)")
print("  ふるさと納税: 総務省『ふるさと納税に関する現況調査』団体別一覧(原論文と同じ出典)")
print("  人口動態    : SSDSE-A 2022〜2026 年版(版ごとの収録年次を重ねてパネル化)")
print("=" * 74)
print(f"  市区町村数: {df['地域コード'].nunique()} 団体")
print(f"  年        : {sorted(df['年'].unique())}")
print(f"  観測数    : {len(df)} 行")

# 受入額は分布が大きく歪むので対数を取る(原論文も金額そのものではなく規模で効かせている)
df['log受入額'] = np.log1p(df['直近3年受入額_百万円'])
▼ 実行結果
==========================================================================
■ 市区町村パネル(ふるさと納税 × 人口動態)
  ふるさと納税: 総務省『ふるさと納税に関する現況調査』団体別一覧(原論文と同じ出典)
  人口動態    : SSDSE-A 2022〜2026 年版(版ごとの収録年次を重ねてパネル化)
==========================================================================
  市区町村数: 1692 団体
  年        : [np.int64(2020), np.int64(2022)]
  観測数    : 3384 行
💡 実行結果の読み取り
  • 1,693 市区町村 × 4 年のパネルデータになっています。パネルとは「同じ相手を何度も観測したデータ」のことです。
  • ふるさと納税の受入額は原論文と同じ出典(総務省「ふるさと納税に関する現況調査」)を使っています。
💡 Python TIPS pd.read_csv で読んだあと dropna で欠測のある行を落としています。
やってみよう記述統計(原論文 表3 に対応)
🎯 このステップの目的
分析に使う変数の分布を確認します。極端な値がないかを最初に見ておきます。
📝 コード
75
76
77
78
79
80
81
82
83
# ================================================================
# ■ 記述統計(原論文 表3 に対応)
# ================================================================
VARS = ['直近3年受入額_百万円', '自然増減率', '社会増減率', '人口増減率', '10万人当たり医師数']
print("\n【基本統計量】")
desc = df[VARS].describe().T[['count', 'mean', 'std', 'min', '50%', 'max']]
desc.columns = ['度数', '平均', '標準偏差', '最小', '中央', '最大']
print(desc.round(2).to_string())
▼ 実行結果
【基本統計量】
                 度数       平均     標準偏差     最小      中央        最大
直近3年受入額_百万円  3384.0  1175.65  3030.73   0.04  345.60  70498.01
自然増減率        3384.0    -1.02     0.65  -5.92   -0.98      1.78
社会増減率        3384.0    -0.38     0.72 -12.52   -0.36      3.46
人口増減率        3384.0    -1.40     1.16 -17.07   -1.41      4.20
10万人当たり医師数   3384.0   171.55   176.41   0.00  138.53   2214.61
💡 実行結果の読み取り
  • ふるさと納税の受入額は自治体によって桁が大きく違うため、平均と中央値が離れています。あとで対数を取るのはこのためです。
  • 人口増減率がマイナスの自治体が多数を占めます。
💡 Python TIPS df.describe().T で縦横を入れ替えると、変数が多いときに読みやすくなります。
やってみようStep1. 固定効果モデルとは何をしているのか(within 変換を手で作る)
🎯 このステップの目的
固定効果モデルが実際に何をしているのかを、数式ではなく数字で見ます。
📝 コード
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# ================================================================
# ■ Step1. 固定効果モデルとは何をしているのか(within 変換を手で作る)
# ================================================================
print("\n" + "=" * 74)
print("■ Step1. 固定効果モデル=『各市区町村の平均からのズレ』で見る")
print("=" * 74)
print("  同じ市区町村の中での動きだけを使うので、")
print("  「もともと魅力的な自治体だから寄付も移住も多い」といった、")
print("  時間で変わらない性質(=欠落変数)の影響を丸ごと消せる。")


def within(frame, cols, group='地域コード'):
    """各グループ(市区町村)の平均を引く。これが固定効果モデルの中身。"""
    out = frame.copy()
    for c in cols:
        out[c + '_w'] = out[c] - out.groupby(group)[c].transform('mean')
    return out


TARGETS = ['自然増減率', '社会増減率', '人口増減率']
XS = ['log受入額', '10万人当たり医師数']
dw = within(df, TARGETS + XS)

sample = dw[dw['地域コード'] == dw['地域コード'].iloc[0]]
print(f"\n  例:{sample['都道府県'].iloc[0]}{sample['市区町村'].iloc[0]} の人口増減率")
for _, r in sample.iterrows():
    print(f"    {int(r['年'])}年  実測 {r['人口増減率']:+.3f} %"
          f"  平均からのズレ {r['人口増減率_w']:+.3f} %")
▼ 実行結果
==========================================================================
■ Step1. 固定効果モデル=『各市区町村の平均からのズレ』で見る
==========================================================================
  同じ市区町村の中での動きだけを使うので、
  「もともと魅力的な自治体だから寄付も移住も多い」といった、
  時間で変わらない性質(=欠落変数)の影響を丸ごと消せる。

  例:北海道函館市 の人口増減率
    2020年  実測 -1.395 %  平均からのズレ +0.110 %
    2022年  実測 -1.616 %  平均からのズレ -0.110 %
💡 実行結果の読み取り
  • 各市区町村の平均を引くと、「その自治体の中での動き」だけが残ります。
  • これで「もともと人気のある自治体だから寄付も移住も多い」といった、時間で変わらない性質の影響が消えます。原論文が固定効果モデルを選んだ理由です。
💡 Python TIPS groupby(...).transform('mean') はグループ平均を元の行数のまま返してくれるので、引き算にそのまま使えます。
やってみようStep2. 固定効果回帰(原論文 表5 に対応する、人口側の 3 本)
🎯 このステップの目的
平均を引いたデータに回帰をあてます。これが固定効果モデルの推定です。
📝 コード
113
114
115
116
117
118
119
120
121
122
123
124
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
151
152
153
154
155
156
157
158
159
160
161
162
# ================================================================
# ■ Step2. 固定効果回帰(原論文 表5 に対応する、人口側の 3 本)
# ================================================================
print("\n" + "=" * 74)
print("■ Step2. 固定効果モデルの推定(被説明変数を 3 通り)")
print("=" * 74)


def fe_ols(frame, y, xs, group='地域コード'):
    """within 変換したデータに最小二乗法をあてる(=固定効果推定)。

    標準誤差は市区町村ごとにクラスターして、同じ自治体の観測が
    似ていることを考慮する。
    """
    d = frame.dropna(subset=[y + '_w'] + [x + '_w' for x in xs])
    Y = d[y + '_w'].to_numpy()
    X = np.column_stack([d[x + '_w'].to_numpy() for x in xs])
    beta, *_ = np.linalg.lstsq(X, Y, rcond=None)
    resid = Y - X @ beta
    n, k = X.shape
    n_g = d[group].nunique()
    XtX_inv = np.linalg.inv(X.T @ X)
    meat = np.zeros((k, k))
    for _g, idx in d.groupby(group).indices.items():
        Xg, ug = X[idx], resid[idx]
        s = Xg.T @ ug
        meat += np.outer(s, s)
    dof = n - n_g - k
    scale = (n_g / max(n_g - 1, 1)) * ((n - 1) / max(dof, 1))
    cov = XtX_inv @ meat @ XtX_inv * scale
    se = np.sqrt(np.diag(cov))
    t = beta / se
    from scipy import stats as st
    p = 2 * (1 - st.t.cdf(np.abs(t), df=max(dof, 1)))
    ss_res = float(resid @ resid)
    ss_tot = float(((Y - Y.mean()) ** 2).sum())
    return pd.DataFrame({'係数': beta, '標準誤差': se, 't値': t, 'p値': p},
                        index=xs), 1 - ss_res / ss_tot, n, n_g


results = {}
for y in TARGETS:
    tbl, r2, n, ng = fe_ols(dw, y, XS)
    results[y] = tbl
    print(f"\n【被説明変数: {y}】 観測 {n} / 市区町村 {ng} / within R² = {r2:.4f}")
    show = tbl.copy()
    show['判定'] = ['***' if p < .01 else '**' if p < .05 else '*' if p < .1 else 'n.s.'
                    for p in show['p値']]
    print(show.round(4).to_string())
▼ 実行結果
==========================================================================
■ Step2. 固定効果モデルの推定(被説明変数を 3 通り)
==========================================================================

【被説明変数: 自然増減率】 観測 3384 / 市区町村 1692 / within R² = 0.1336
                係数    標準誤差      t値      p値    判定
log受入額     -0.1497  0.0164 -9.1504  0.0000   ***
10万人当たり医師数 -0.0001  0.0006 -0.2076  0.8356  n.s.

【被説明変数: 社会増減率】 観測 3384 / 市区町村 1692 / within R² = 0.0020
                係数    標準誤差      t値      p値    判定
log受入額      0.0320  0.0238  1.3454  0.1787  n.s.
10万人当たり医師数  0.0005  0.0015  0.3112  0.7557  n.s.

【被説明変数: 人口増減率】 観測 3384 / 市区町村 1692 / within R² = 0.0195
                係数    標準誤差      t値      p値    判定
log受入額     -0.1177  0.0266 -4.4285  0.0000   ***
10万人当たり医師数  0.0003  0.0014  0.2436  0.8076  n.s.
💡 実行結果の読み取り
  • 社会増減率(引っ越しによる増減)にはふるさと納税の効果が見られません。原論文と同じ結果です。
  • 自然増減率(出生と死亡による増減)には負の関係が出ています。これも原論文と同じ向きです。
  • 標準誤差は市区町村ごとにまとめて(クラスター)計算しています。同じ自治体の複数年の観測は似ているので、そのままだと有意になりすぎるためです。
💡 Python TIPS np.linalg.lstsq は最小二乗法をそのまま解きます。外部ライブラリに頼らないので、ブラウザ上でもそのまま動きます。
やってみようStep3. 原論文の結論と照らし合わせる
🎯 このステップの目的
原論文の結論と、この再現の結果を並べて確かめます。
📝 コード
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# ================================================================
# ■ Step3. 原論文の結論と照らし合わせる
# ================================================================
print("\n" + "=" * 74)
print("■ Step3. 原論文の結論と、この再現の結果")
print("=" * 74)
b_soc = results['社会増減率'].loc['log受入額']
b_nat = results['自然増減率'].loc['log受入額']
b_pop = results['人口増減率'].loc['log受入額']
print("  原論文(2012/2014/2016):")
print("    ・社会増減率  … 関係が確認されなかった")
print("    ・自然増減率  … 負の効果")
print("    ・結論        … ふるさと納税は人口増加をもたらさない")
print("\n  この再現(2020〜2023):")
for name, b in [('社会増減率', b_soc), ('自然増減率', b_nat), ('人口増減率', b_pop)]:
    verdict = ('有意な関係は見られない' if b['p値'] >= .05
               else ('正の効果' if b['係数'] > 0 else '負の効果'))
    print(f"    ・{name}  … 係数 {b['係数']:+.4f} (p={b['p値']:.3f}) → {verdict}")
print("\n  → 『寄付が集まっても人口増加には結びつかない』という原論文の主旨が、")
print("     時点を変えても同じ向きで確認できるかどうかが読みどころ。")
▼ 実行結果
==========================================================================
■ Step3. 原論文の結論と、この再現の結果
==========================================================================
  原論文(2012/2014/2016):
    ・社会増減率  … 関係が確認されなかった
    ・自然増減率  … 負の効果
    ・結論        … ふるさと納税は人口増加をもたらさない

  この再現(2020〜2023):
    ・社会増減率  … 係数 +0.0320 (p=0.179) → 有意な関係は見られない
    ・自然増減率  … 係数 -0.1497 (p=0.000) → 負の効果
    ・人口増減率  … 係数 -0.1177 (p=0.000) → 負の効果

  → 『寄付が集まっても人口増加には結びつかない』という原論文の主旨が、
     時点を変えても同じ向きで確認できるかどうかが読みどころ。
💡 実行結果の読み取り
  • 原論文の主旨「ふるさと納税は人口増加をもたらさない」が、時点を変えても同じ向きで確認できるかどうかがポイントです。
  • 同じ数値になることは目標ではありません。結論の向きが一致するかを見ます。
💡 Python TIPS f-string の {値:+.4f} は符号つきで小数 4 桁に揃えます。
やってみよう図1: ふるさと納税受入額の推移(原論文 図1 に対応)
🎯 このステップの目的
原論文の図1(受入額の推移)にあたる図を作ります。
📝 コード
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# ================================================================
# ■ 図1: ふるさと納税受入額の推移(原論文 図1 に対応)
# ================================================================
fig, ax = plt.subplots(figsize=(9, 5))
tot = (df.groupby('年')['ふるさと納税受入額_千円'].sum() / 1e6)
ax.bar(tot.index.astype(int), tot.values, color='#1565C0')
for x, v in zip(tot.index.astype(int), tot.values):
    ax.text(x, v, f'{v:,.0f}', ha='center', va='bottom', fontsize=11)
ax.set_xlabel('年度')
ax.set_ylabel('ふるさと納税受入額の合計(十億円)')
ax.set_title('図1: ふるさと納税受入額の推移(分析対象の市区町村合計)\n'
             '出典: 総務省「ふるさと納税に関する現況調査」', fontsize=13)
ax.grid(axis='y', alpha=.3)
fig.savefig(os.path.join(FIG_DIR, '2020_U2_furusato_fig1_trend.png'),
            dpi=DPI, bbox_inches='tight')
plt.close(fig)
print("\n  → html/figures/2020_U2_furusato_fig1_trend.png 保存完了")
▼ 実行結果
→ html/figures/2020_U2_furusato_fig1_trend.png 保存完了
💡 実行結果の読み取り
  • ふるさと納税の受入額が年々増えていることが分かります。
  • 棒の上の数字は実際の合計額です。
💡 Python TIPS ax.text(x, v, ...) で棒の上に数値を書き足せます。
やってみよう図2: 固定効果の考え方(実測値と、平均からのズレ)
🎯 このステップの目的
固定効果モデルの考え方を図で示します。
📝 コード
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# ================================================================
# ■ 図2: 固定効果の考え方(実測値と、平均からのズレ)
# ================================================================
fig, axes = plt.subplots(1, 2, figsize=(13, 5))
top = (df.groupby('地域コード')['直近3年受入額_百万円'].mean()
       .sort_values(ascending=False).head(6).index)
sub = df[df['地域コード'].isin(top)]
subw = within(sub, ['人口増減率'])
for code, g in subw.groupby('地域コード'):
    label = f"{g['都道府県'].iloc[0]}{g['市区町村'].iloc[0]}"
    axes[0].plot(g['年'], g['人口増減率'], marker='o', label=label)
    axes[1].plot(g['年'], g['人口増減率_w'], marker='o', label=label)
axes[0].set_title('実測の人口増減率\n(自治体ごとの水準の違いが大きい)', fontsize=12)
axes[1].set_title('自治体の平均を引いたあと\n(固定効果モデルが見ているのはこちら)', fontsize=12)
for ax in axes:
    ax.set_xlabel('年')
    ax.set_ylabel('人口増減率(%)')
    ax.grid(alpha=.3)
    ax.legend(fontsize=9)
axes[1].axhline(0, color='#C62828', lw=1.2, ls='--')
fig.suptitle('図2: 固定効果モデルは「その自治体の中での動き」だけを使う', fontsize=14)
fig.savefig(os.path.join(FIG_DIR, '2020_U2_furusato_fig2_within.png'),
            dpi=DPI, bbox_inches='tight')
plt.close(fig)
print("  → html/figures/2020_U2_furusato_fig2_within.png 保存完了")
▼ 実行結果
→ html/figures/2020_U2_furusato_fig2_within.png 保存完了
💡 実行結果の読み取り
  • 左は実測値で、自治体ごとに水準がバラバラです。
  • 右は平均を引いたあとで、0 のまわりに揃います。固定効果モデルが見ているのはこちらです。
💡 Python TIPS plt.subplots(1, 2) で 2 枚を横に並べます。
やってみよう図3: 推定結果(係数と 95% 信頼区間)
🎯 このステップの目的
推定した係数を、信頼区間つきで図にします。
📝 コード
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# ================================================================
# ■ 図3: 推定結果(係数と 95% 信頼区間)
# ================================================================
fig, ax = plt.subplots(figsize=(9, 5))
ys = list(results.keys())
coef = [results[y].loc['log受入額', '係数'] for y in ys]
se = [results[y].loc['log受入額', '標準誤差'] for y in ys]
pos = np.arange(len(ys))
ax.errorbar(coef, pos, xerr=[1.96 * s for s in se], fmt='o', color='#1565C0',
            capsize=6, markersize=9, lw=2)
ax.axvline(0, color='#C62828', ls='--', lw=1.4)
ax.set_yticks(pos)
ax.set_yticklabels(ys, fontsize=12)
ax.set_xlabel('ふるさと納税受入額(対数)の係数と 95% 信頼区間')
ax.set_title('図3: ふるさと納税は人口を増やしたか(固定効果モデル)\n'
             '横棒が 0 をまたいでいれば「効果があるとは言えない」', fontsize=13)
ax.grid(axis='x', alpha=.3)
fig.savefig(os.path.join(FIG_DIR, '2020_U2_furusato_fig3_coef.png'),
            dpi=DPI, bbox_inches='tight')
plt.close(fig)
print("  → html/figures/2020_U2_furusato_fig3_coef.png 保存完了")
▼ 実行結果
→ html/figures/2020_U2_furusato_fig3_coef.png 保存完了
💡 実行結果の読み取り
  • 横棒が 0 をまたいでいれば「効果があるとは言えない」と読みます。
  • 社会増減率の横棒は 0 をまたぎ、自然増減率はマイナス側にあります。
💡 Python TIPS ax.errorbar(..., xerr=...) で横向きの信頼区間を描けます。
やってみよう再現できていない部分(隠さずに書く)
🎯 このステップの目的
この再現でできていないことを、最後にはっきり書き出します。
📝 コード
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
# ================================================================
# ■ 再現できていない部分(隠さずに書く)
# ================================================================
print("\n" + "=" * 74)
print("■ この再現でできていないこと")
print("=" * 74)
print("  1. 仮説1(経済効果)の被説明変数『納税義務者1人当たり課税対象所得』は")
print("     SSDSE に収録が無く、この教材では検証できていない。")
print("     取得先: 総務省「市町村税課税状況等の調」")
print("  2. 時点が原論文(2012/2014/2016)ではなく 2020〜2023 年。")
print("     SSDSE-A の版を重ねて作れるのがこの範囲のため。")
print("  3. 大学進学率・保育施設利用者比率・高齢者施設定員比率は、")
print("     市区町村パネルとして揃わないため統制変数から外している。")
print("=" * 74)
▼ 実行結果
==========================================================================
■ この再現でできていないこと
==========================================================================
  1. 仮説1(経済効果)の被説明変数『納税義務者1人当たり課税対象所得』は
     SSDSE に収録が無く、この教材では検証できていない。
     取得先: 総務省「市町村税課税状況等の調」
  2. 時点が原論文(2012/2014/2016)ではなく 2020〜2023 年。
     SSDSE-A の版を重ねて作れるのがこの範囲のため。
  3. 大学進学率・保育施設利用者比率・高齢者施設定員比率は、
     市区町村パネルとして揃わないため統制変数から外している。
==========================================================================
💡 実行結果の読み取り
  • 原論文の仮説1(経済効果)は、納税義務者1人当たり課税対象所得が SSDSE に無いため検証できていません。
  • 取得先(総務省「市町村税課税状況等の調」)を明記してあるので、手に入れれば同じコードで確かめられます。
💡 Python TIPS 「できないこと」を書き出す習慣は、分析レポートの信頼性を大きく上げます。

図で確かめる

スクリプトが作った図です。数字だけでは掴みにくい部分を目で確かめてください。

ふるさと納税受入額の推移
図1: 分析対象の市区町村が受け取ったふるさと納税の合計額。年々増えています(原論文 図1 に対応)。
固定効果モデルの考え方
図2: 左が実測値、右が「その自治体の平均を引いたあと」。固定効果モデルは右側だけを使って推定します。
推定された係数と信頼区間
図3: ふるさと納税受入額の係数と 95% 信頼区間。横棒が 0 をまたいでいれば「効果があるとは言えない」と読みます。

📥 データの準備(再現コードを動かす前に)

ページ下部の「🐍 ブラウザで動かす」を使えば、何も準備せずにそのまま実行できます。 手元の Python で動かしたい場合だけ、次を用意してください。

実行は python3 code/2020_U2_yushu_furusato.py です。ファイルの編集は要りません。

この教材で再現できていないこと

隠さずに書きます。

逆に、説明変数のふるさと納税受入額は原論文とまったく同じ出典で、 分析対象からの除外(東京 23 区・政令指定都市・福島県 5 町村)も原論文どおりです。

🐍 ブラウザで動かす — インストール不要でこの論文の分析を再現する

このページの分析は、この画面の中でそのまま実行できます。 Python をインストールする必要も、CSV をダウンロードする必要もありません。 下のセルの 「▶ ブラウザで実行」 を上から順に押すか、 「▶ 最初から全部実行」 で一気に流してください。 表示されるのは、本文の図表とまったく同じ計算の結果です (動かしているのは再現スクリプト code/2023_U1_daijin.py そのもの)。

コードは書き換えられます。「✏️ 書き換える」で数字や変数を変えて ▶ を押すと、 結果がどう変わるかをその場で確かめられます(元に戻すボタンつき)。 ページを開いた時点で裏で実行環境の準備が始まっているので、待ち時間はほとんどありません。 初回だけ実行環境の取得にインターネット接続が必要です。