Chapter 15 差の差法

15.1 差の差法とは

ここからは、より因果関係を推定できる可能性が高い疑似実験の手法を学んでいきます。

差の差法(Difference-in-Differences, DiD, 差分の差分法)は、文字通り2つのグループの差に差があるか、をみることであるグループに施行された政策や出来事の効果や影響を推定しようとするアプローチである。

15.1.1 差の差法のアイデア

パネルデータで説明したような状況でもあっても、目的変数と説明変数の両方に影響を与える変数の存在を完全に排除しきれるわけではない。 パネルデータで紹介した固定効果モデルでも、固定効果を除いた説明変数の効果を推定できているが、固定効果ではない変数が存在している可能性も否定はできない。

より因果関係により迫れる方法として、疑似実験が挙げられる。これはあたかも実験のような状況を見つけ出して、実験でいう処置群(政策などの影響を受けるグループ)と、対照群(効果・影響を受けないグループ)を比較する方法である。

本当の実験であれば、処置を行う前はいずれのグループも全く同じであり、処置を受けた後に2つを比較すれば、その差が処置の効果だと言うことになる。

実際の社会では、政策などが実施される前後で2つのグループが全く同じ、ということはまれであろう。 しかし、2つのグループが異なるけれど、同じような動きをする、と考えればもう少し可能性は広がる。

例えば、埼玉県と東京都の平均所得は当然異なるが、景気などによって東京都の所得が上がっているときは埼玉県の所得も上がっていそうだ。 このように平行に推移する目的変数であるならば、差だけをとっても比較はできないが、差の差、を取ることで比較することが可能になる。

下の図は、差の差法の概念を示した図である。

knitr::include_graphics("fig/did_concept_pos.png")

処置群は、なんらかの政策を受ける。効果があるならば、政策によって目的変数が変化する。 しかし、前後関係だけで比較することはできない。なぜならば、その前後の変化は政策以外による影響もあるかもしれないからだ。

仮に、東京都の中学校で1クラス40人学級だったものを、30人学級に変更するという政策が行われたとしよう。 担任の教師ひとりが担当する生徒数が減るので、より目が行き届くようになり、生徒にとって良い影響があると期待される。 例えば、平均的な試験のスコアが上がると期待される。 この変更を実際に施行して東京都の中学校の試験の平均スコアを比較してみたとしよう。すると、図のように前回よりも下がっていたとしたらどうだろうか? クラスの人数を少なくすることはよいことどころかスコアを悪化させる悪い政策だったのだろうか?

となりの埼玉県でも同じテストを実施しているとしよう。しかし、東京都と違って埼玉県ではこの時期にクラス人数の変更やその他テストスコアに影響しそうな施策を変更していないとする。この埼玉県の中学校を比較対象グループとする。 埼玉県のテストスコアを見てみると、大きく減少している。例えば、たまたま学級サイズ変更後のテストが前年のテストより難しかった可能性がある。

埼玉県と東京都は東京都のほうが上だが、普段は同じような動きをするとする(平行トレンドの家庭)。 そこで、埼玉県の「前後の差」を東京都の「もし学級サイズの変更がなかった場合の差」として考えてみる。 すると、東京都はもし学級サイズの変更がなければ図の点線のような動きになっていたことになる。すなわち、もっとテストスコアが悪かっただろう、ということだ。

そこで、「実際に観察された東京都の前後差」を埼玉県の前後差から引くと、「東京都がもし学級サイズを変更していなかった場合のテストスコアの差」から「東京都が学級サイズを変更した場合(つまり現実)のテストスコアの差」を引くことと同義になり、東京都で学級サイズを変更したことによるテストスコアへの影響を推定できることになる。

これが差の差法のアイデアである。

15.1.2 差の差法の仮定

因果推定としての差の差法による推定では、以下のような仮定を置いて推定する。以下の仮定が明らかに成り立たない場合は、この方法は使えないことに留意が必要だ。

  • 平行トレンドの仮定:処置グループと対照グループはもし政策の施行がなければ平行に推移する。
  • スピルオーバーがない:処置グループの効果や影響が対照グループに影響しない。

15.1.3 回帰分析を使って行う方法

比較するというアイデアは上の通りだが、それを実際にどうやって推定するのだろうか?

ここでは、回帰分析を使って差の差法を推定する方法を説明する。

差の差法が適用できるようなデータは、少なくとも2つの期間(政策施行前と後)があり、2つのグループ(政策が施行されたグループと対照グループ)がある。 データでは、それぞれ個々人や自治体などのユニットで値は異なるが、簡単に説明するためにそれぞれのグループの平均だけで話をすると、以下のように整理できる。

対照群 (A) 処置群 (B)
時点 t=1 \(Y_{1A}\) \(Y_{1B}\)
時点 t=2 \(Y_{2A}\) \(Y_{2B}\)

ここで、処置群の差は\(Y_{2B} - Y_{1B}\)で表され、対照群の差は\(Y_{2A} - Y_{1A}\)となる。 差の差で推定される効果は

\[ (Y_{2B} - Y_{1B}) - (Y_{2A} - Y_{1A}) \]

として表現することができる。平均値だけで言えば、これで推定された値が差の差法で推定された政策の効果である。

これを回帰分析で表現するとどうなるだろうか。 政策前後と対照・処置群両方が含まれたデータを使って、以下のような式を推定するとする。

\[ Y_{it} = \alpha + \beta_{1} Policy_{i} + \beta_{2} Time_{t} + \beta_{3} Policy_{i} \times Time_{t} + \varepsilon_{it} \] ここで、\(Policy_{i}\)\(Time_{t}\)はダミー変数である。\(Policy_{i}\)はもし、該当データ(個人\(i\))が、処置群に入るならば1、対照群ならば0を取る。 \(Time_{t}\)は該当データ(時点\(t\))のタイミングが政策実施後ならば1,政策実施前ならば0を取る。

それぞれのケースを見てみよう。

ケース1:政策実施前+対照群

もし政策実施前で、対照群に入っているデータならば \[ Y_{it} = \alpha + \beta_{1} 0 + \beta_{2} 0 + \beta_{3} 0 \times 0 + \varepsilon_{it} = \alpha + \varepsilon_{it} \]

このケースは、上の表でいうと\(Y_{1A}\)に当たる。このグループの平均を取ると、誤差項はゼロになるので、\(Y_{1A} = \alpha\)となる。

ケース2:政策実施前+処置群

もし政策実施前(\(Time_{t} = 0\))で、処置群(\(Policy_{i}=1\))に入っているデータならば \[ Y_{it} = \alpha + \beta_{1} 1 + \beta_{2} 0 + \beta_{3} 1 \times 0 + \varepsilon_{it} = \alpha + \beta_{1} + \varepsilon_{it} \] 同様に\(Y_{1B}=\alpha + \beta_1\)となる。

ケース3:政策実施後+対照群

もし政策実施後(\(Time_{t} = 1\))で、対照群(\(Policy_{i}=0\))に入っているデータならば \[ Y_{it} = \alpha + \beta_{1} \cdot0 + \beta_{2} \cdot 1 + \beta_{3} \cdot 0 \times 1 + \varepsilon_{it} = \alpha + \beta_{2} + \varepsilon_{it} \]

同様に\(Y_{2A}=\alpha + \beta_2\)となる。

ケース4:政策実施後+処置群

もし政策実施後(\(Time_{t} = 1\))で、対照群(\(Policy_{i}=0\))に入っているデータならば

\[ Y_{it} = \alpha + \beta_{1} \cdot 1 + \beta_{2} \cdot 1 + \beta_{3} \cdot 1 \times 1 + \varepsilon_{it} = \alpha + \beta_{1} + \beta_{2} + \beta_{3} + \varepsilon_{it} \]

同様に\(Y_2B=\alpha + \beta_1 + \beta_2 \beta_3\)となる。

15.1.4 演習問題:差の差法の回帰分析アプローチ

差の差法による推定効果が、回帰式の\(\beta_{3}\)と一致することを証明してみよう。

15.2 Rでの演習

15.2.1 スクリプトの準備

新しいスクリプトにコードを書いていく。 新しくスクリプトを作成し、did.Rという名前をつけて保存する。

15.2.2 パッケージの準備

以下のパッケージを使うので読み込んでおく。 インストールしてない場合は、インストールする。

# ライブラリ
library(tidyverse)
library(skimr)
library(fixest)

15.2.3 データの準備

データをダウンロードする。

# データのダウンロードとdataフォルダへの格納を自動的に行う
download.file("https://github.com/keita43a/regression_tutorial/blob/main/data/data_njmin.csv?raw=TRUE", 
              destfile = "data/data_njmin.csv")

# データを読み込む
data_njmin = read_csv("data/data_njmin.csv")

15.2.4 データの確認

このデータはアメリカの2つの州におけるファーストフード店の雇用者数や賃金が記録されている。 州のPAはペンシルベニア州、NJはニュージャージー州である。 このデータは差の差分析でもっとも有名な論文であるCard and Krueger (1994)で実際に用いられたデータである。 1992年にニュージャージ州では最低賃金が引き上げられた($4.25 ->$5.05)が、隣のペンシルベニア州ではそのような政策の変更がなかった。 この現象を利用して、差の差分析を用いて最低賃金が雇用に影響をもたらすかどうかを分析した研究である。

データの中身を見てみる。 まずは、どんなデータなのかhead()で確認する。

head(data_njmin)
## # A tibble: 6 × 9
##   state fulltime_before parttime_before wage_before fulltime_after parttime_after
##   <chr>           <dbl>           <dbl>       <dbl>          <dbl>          <dbl>
## 1 PA                 20              20        5                 0             36
## 2 PA                  6              26        5.5              28              3
## 3 PA                 50              35        5                15             18
## 4 PA                 10              17        5                26              9
## 5 PA                  2               8        5.25              3             12
## 6 PA                  2              10        5                 2              9
## # ℹ 3 more variables: wage_after <dbl>, full_prop_before <dbl>,
## #   full_prop_after <dbl>

15.2.5 データの記述統計

データの記述統計を確認しよう。 Chapter @ref(#rstats) で説明したように、ここではskimrパッケージのskim()関数を使った。

library(skimr)

skim(data_njmin)
Table 15.1: Data summary
Name data_njmin
Number of rows 358
Number of columns 9
_______________________
Column type frequency:
character 1
numeric 8
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
state 0 1 2 2 0 2 0

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
fulltime_before 0 1 8.47 8.70 0.00 2.12 6.00 12.00 60.00 ▇▂▁▁▁
parttime_before 0 1 18.75 10.29 0.00 11.00 16.25 25.00 60.00 ▆▇▅▁▁
wage_before 0 1 4.62 0.35 4.25 4.25 4.50 4.99 5.75 ▇▃▃▁▁
fulltime_after 0 1 8.36 7.81 0.00 2.00 6.00 12.00 40.00 ▇▃▁▁▁
parttime_after 0 1 18.69 10.57 0.00 11.00 17.00 25.00 60.00 ▆▇▅▁▁
wage_after 0 1 4.99 0.26 4.25 5.05 5.05 5.05 6.25 ▁▇▁▁▁
full_prop_before 0 1 0.30 0.23 0.00 0.11 0.26 0.47 1.00 ▇▆▃▂▁
full_prop_after 0 1 0.31 0.25 0.00 0.11 0.27 0.50 1.00 ▇▅▃▂▁

15.2.6 政策変更の確認

ここでは、実際に政策変更が行われたかを2 x 2 の表を作成することで確認しよう。

上の表のように、2x2の表では以下のような形となることを理解しよう。 ここでは目的変数(雇用)ではなく、処置を受けた場合に変化する変数(最低賃金)であることに注意しよう。 賃金(wage)なので\(w\)で表記する。

対照群 (PA) 処置群 (NJ)
時点 t=1 \(w_{1,PA}\) \(w_{1,NJ}\)
時点 t=2 \(w_{2,PA}\) \(w_{2,NJ}\)

グループごとの記述統計(セクション6.11.12参照)を用いて、上のような表を作ってみよう。

njmin_table <- data_njmin |>
  group_by(state) |> # 州ごとにグループ分けを行う
  summarise(before = mean(wage_before), 
            after = mean(wage_after),  
            .groups = "drop") # 混乱を避けるためgroup_byで指定したグループ化を解除

njmin_table
## # A tibble: 2 × 3
##   state before after
##   <chr>  <dbl> <dbl>
## 1 NJ      4.61  5.08
## 2 PA      4.65  4.61

上の表と違って、行に州が、列に時期が入ってしまっているが、計算したいことはできている。

予想通り、ペンシルベニア州(PA)では、政策変更の前後ともにあまり平均賃金は変わっていないが、ニュージャージ州(NJ)では変更後に平均賃金が上がっている。 当然ながら、平均賃金は新しい最低賃金($5.05)よりも高い。

15.2.6.1 演習問題:処置に反応しているかの確認

本当にペンシルベニア州のファーストフード店が最低賃金の引き上げに従っているのだろうか? 政策施行の前後と州ごとに、賃金が5.05 (PAの政策施行後の最低賃金)より低い店の割合を計算し、njmin_table2というオブジェクトに格納してから表示しなさい。

ヒント:もしwage_before < 5.05という命題が論理値によって評価される場合はTRUEかFALSEが返されるが、TRUEは1, FALSEは0として評価される。 そのためmean(wage_before < 5.05)という計算によって、1と0の平均が計算されるということは、wage_before < 5.05である割合を計算することと同義である。

## # A tibble: 2 × 3
##   state before   after
##   <chr>  <dbl>   <dbl>
## 1 NJ     0.911 0.00344
## 2 PA     0.940 0.955

結果は上のような表が出力される。 ニュージャージ州の政策施行後だけ、著しく低い割合になっている。すべての店ではないが、多くの店は最低賃金のルールに従って賃金を設定しているようだ。

15.2.6.2 演習問題:前後での比較

よく行われる比較は、実際に政策を実行してみた後とその前を比較して「上がったか?下がったか?」を議論するやり方だ。 ニュースなどでも「〇〇を実施した後、△△がX%上昇」などという文言を聞くことがあるだろう。 ここでは、前後比較を計算してみよう。

  • ニュージャージー州のみのデータを抽出しなさい
  • フルタイム労働者の割合(full_prop_**)の平均をそれぞれ政策施行前、後でとりなさい。
  • full_prop_diffという列を作成し、計算した前後の平均の差を計算しなさい。
  • njmin_table3というオブジェクトに格納して、コンソールに表示しなさい
## # A tibble: 1 × 3
##   full_prop_before full_prop_after full_prop_diff
##              <dbl>           <dbl>          <dbl>
## 1            0.297           0.320         0.0239

上のような表が表示される。 前後の差は0.0238…なので2.4%ポイント上昇したことになる。これは正しい推定だろうか?

当然前後の比較なので、この比較は「もし政策が変わらなければ、NJ州のフルタイム雇用率は政策実施前から変わらない」ことが仮定として暗に置かれている。 しかし、経済状況など様々な他の要因は変わりうる。この前後比較というのは、実はかなり強い仮定(=あまり現実的ではない仮定)のもとで推定されているに過ぎないのである。

15.2.7 差の差法

各グループと前後の平均だけで推定する方法で差の差法を推定してみよう。

まず2x2の目的変数の2x2の表を作成する。

対照群 (PA) 処置群 (NJ)
時点 t=1 \(Y_{1,PA}\) \(Y_{1,NJ}\)
時点 t=2 \(Y_{2,PA}\) \(Y_{2,NJ}\)
njmin_table_did <- data_njmin |>
  group_by(state) |>
  summarize(full_prop_before = mean(full_prop_before),
            full_prop_after = mean(full_prop_after),
            .groups = "drop") 

njmin_table_did
## # A tibble: 2 × 3
##   state full_prop_before full_prop_after
##   <chr>            <dbl>           <dbl>
## 1 NJ               0.297           0.320
## 2 PA               0.310           0.272

さらに上のコードに付け足して、差の差を計算する。

njmin_table_did2 <- data_njmin |>
  group_by(state) |>
  summarize(full_prop_before = mean(full_prop_before),
            full_prop_after = mean(full_prop_after),
            .groups = "drop") |>
  mutate(full_prop_diff = full_prop_after - full_prop_before) |>
  with(full_prop_diff[state=="NJ"] - full_prop_diff[state=="PA"])

njmin_table_did2
## [1] 0.06155831

つまり、6.15%ポイント雇用が増加したという推定になる。

15.2.8 回帰分析による差の差法

上で解説した回帰分析による差の差法の推定を行ってみよう。

まず、データをlong形式に変換し、同じ変数であるフルタイム雇用の割合を前後で一つの列にする。

data_njmin_long <- data_njmin %>% 
  # 州とfull_で始まる列のみ抽出
  dplyr::select(state, starts_with("full_")) %>%
  # fullで始まる列を選択して、カテゴリ名をtiming, 数値の列名をpropとしてlong形式にする
  pivot_longer(cols = starts_with("full"),
               names_to = "timing",
               names_prefix = "full_prop_",
               values_to = "prop") %>% 
  # 新たなダミー変数を作成する。上と同様に、policyは政策を受ける(最低賃金が上げられる)州なら1とするダミー変数
  # timeは政策の前後で後ならば1とするダミー変数
  mutate(policy = ifelse(state == "NJ", 1, 0),
         time = ifelse(timing == "after", 1, 0))

このlong形式のデータセットを使って回帰分析を行う。

Rの回帰式では、policyとtimeの交差項はpolicy:timeとコロン(:)でつなぐことで表現する。 掛け算なのでpolicy*timeと思うかもしれないが、アスタリスク*でつなぐと、policyとtimeそれぞれも再度含めてしまう。 (すなわちpolicy*timepolicy + time + policy:timeと同義である。ここではあえて省略せずに後者で書くことで、回帰式と一致していることを確認している。)

reg_did1 <- feols(prop ~ policy + time + policy:time, data=data_njmin_long)

etable(reg_did1)
##                           reg_did1
## Dependent Var.:               prop
##                                   
## Constant        0.3100*** (0.0295)
## policy            -0.0134 (0.0327)
## time              -0.0377 (0.0417)
## policy x time      0.0616 (0.0463)
## _______________ __________________
## S.E. type                      IID
## Observations                   716
## R2                         0.00392
## Adj. R2                   -0.00028
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

回帰分析表のpolicy x timeの推定値は0.0616となっているが、これは上で計算した差の差法の推定値と同じである。

ただし、統計的には母集団ではゼロである仮説を棄却できないため、増加するとは言い難いが、 少なくとも減少はしていない。

この研究は、差の差法を使って最低賃金の雇用に対する影響という経済学において重要な問いに答えた有名な研究であり、それまでは最低賃金を上げると雇用に対して悪影響が起こるとされていた理論に対して新たな貢献をした重要な研究である。