Difference-in-Differences (DiD) — ocena efektów polityk

Streszczenie

Metoda różnicy w różnicach: identyfikacja przyczynowa, założenie równoległych trendów, estymacja, event study i pułapki — z przykładami w R i Python

Problem: pomiar efektu polityki

Wyobraź sobie, że stan wprowadza nową politykę (podwyżka płacy minimalnej, program szkoleniowy, podatek). Pytanie: jaki był jej efekt przyczynowy?

Naiwne podejścia zawodzą:

  • Porównanie przed/po (tylko grupa objęta): myli efekt polityki z ogólnym trendem czasowym
  • Porównanie objęci/nieobjęci (tylko po): myli efekt polityki z wcześniejszymi różnicami między grupami

Difference-in-Differences łączy oba porównania i eliminuje oba obciążenia.

Idea: podwójna różnica

Mamy dwie grupy i dwa okresy:

PrzedPoRóżnica (po − przed)
Grupa objęta (treatment)$Y_{T,0}$$Y_{T,1}$$\Delta_T = Y_{T,1} - Y_{T,0}$
Grupa kontrolna (control)$Y_{C,0}$$Y_{C,1}$$\Delta_C = Y_{C,1} - Y_{C,0}$

Estymator DiD to różnica różnic:

$$ \hat{\delta}_{DiD} = \Delta_T - \Delta_C = (Y_{T,1} - Y_{T,0}) - (Y_{C,1} - Y_{C,0}) $$

Grupa kontrolna pokazuje, co stałoby się z grupą objętą gdyby polityki nie było (kontrafaktyczny trend). Odejmując go, izolujemy czysty efekt.

Ilustracja graficzna

Y
│                              ● objęta (rzeczywista)
│                         ╱
│                    ╱  ← efekt DiD (δ)
│               ╱ ┄┄┄┄○ kontrafaktyczny (gdyby nie polityka)
│          ╱      ╱
│     ●┄┄┄┄┄┄┄╱  kontrola
│    ╱     ╱
│   ╱  ╱
│  ●╱
└─────────┬──────────┬──────► czas
        przed    polityka/po

Estymacja regresyjna

DiD zapisujemy jako regresję z interakcją:

$$ Y_{it} = \beta_0 + \beta_1\,\text{Objęty}_i + \beta_2\,\text{Po}_t + \delta\,(\text{Objęty}_i \times \text{Po}_t) + \varepsilon_{it} $$
  • $\beta_1$ — stała różnica między grupami (efekt przynależności do grupy objętej)
  • $\beta_2$ — wspólny trend czasowy (efekt upływu czasu)
  • $\boldsymbol{\delta}$ — estymator DiD: efekt przyczynowy polityki (interakcja!)
library(fixest)
# Objety = 1 dla grupy treatment, Po = 1 dla okresu po polityce
did <- feols(y ~ objety * po, data = dane)
summary(did)
# Współczynnik 'objety:po' to delta — efekt polityki
import statsmodels.formula.api as smf
did = smf.ols('y ~ objety * po', data=df).fit(cov_type='cluster',
              cov_kwds={'groups': df['jednostka']})
print(did.summary())
# Współczynnik objety:po = efekt DiD

Kluczowe założenie: równoległe trendy

DiD jest wiarygodne tylko jeśli zachodzi parallel trends assumption: w braku polityki grupa objęta i kontrolna podążałyby równoległymi trendami.

$$ \mathbb{E}[Y^0_{T,1} - Y^0_{T,0}] = \mathbb{E}[Y^0_{C,1} - Y^0_{C,0}] $$

To założenie jest niesprawdzalne bezpośrednio (dotyczy kontrafaktu), ale można je uwiarygodnić:

  1. Trendy przed-polityką — sprawdź, czy grupy poruszały się równolegle PRZED interwencją
  2. Event study — estymuj efekty dla wielu okresów przed i po
  3. Testy placebo — udawana polityka w okresie, gdy jej nie było

Event study — weryfikacja trendów

# Efekty dynamiczne względem okresu polityki (rok 0 = referencja)
es <- feols(y ~ i(rok, objety, ref = -1) | jednostka + rok,
            data = dane)
iplot(es)   # wykres współczynników — przed polityką powinny być ~0

Jeśli współczynniki przed polityką są bliskie zera (nieistotne) → trendy były równoległe → DiD wiarygodne. Jeśli rosną przed polityką → naruszenie założenia.

Klasyczny przykład: Card & Krueger (1994)

Podwyżka płacy minimalnej w New Jersey (luty 1992). Grupa kontrolna: sąsiednia Pensylwania (bez zmiany).

  • Objęci: restauracje fast-food w NJ
  • Kontrola: restauracje fast-food w PA
  • Wynik $Y$: zatrudnienie

Wbrew teorii konkurencyjnego rynku pracy, DiD pokazał, że podwyżka nie zmniejszyła zatrudnienia (a nawet lekko zwiększyła). Przełomowe badanie — Card dostał Nobla 2021.

Uogólnienie: panel z efektami stałymi (TWFE)

Z wieloma jednostkami i okresami stosujemy two-way fixed effects:

$$ Y_{it} = \alpha_i + \lambda_t + \delta\,D_{it} + \varepsilon_{it} $$
  • $\alpha_i$ — efekty stałe jednostek (kontroluje stałe różnice między jednostkami)
  • $\lambda_t$ — efekty stałe czasu (kontroluje wspólne szoki czasowe)
  • $D_{it}$ — wskaźnik „objęty polityką w okresie t"
library(fixest)
twfe <- feols(y ~ d | jednostka + rok, data = panel)
summary(twfe, cluster = ~jednostka)   # klastrowane SE!

Zawsze klastruj błędy standardowe na poziomie jednostki — obserwacje tej samej jednostki w czasie są skorelowane (autokorelacja). Bez tego SE są zaniżone.

Pułapki nowoczesnego DiD

Ostrożnie z TWFE gdy polityka wchodzi w różnych momentach dla różnych jednostek (staggered adoption). Badania (Goodman-Bacon 2021, Callaway & Sant’Anna 2021) pokazały, że klasyczny TWFE może dawać obciążone wyniki — używa wcześnie-objętych jako kontroli dla późno-objętych.

Nowoczesne estymatory rozwiązują ten problem:

library(did)
# Estymator Callaway & Sant'Anna — odporny na staggered adoption
att <- att_gt(yname = "y", tname = "rok", idname = "jednostka",
              gname = "pierwszy_rok_polityki", data = panel)
aggte(att, type = "dynamic")   # efekty dynamiczne

Checklist DiD

  1. ✅ Czy masz wyraźną grupę objętą i kontrolną?
  2. ✅ Czy masz dane przed i po interwencji?
  3. ✅ Czy trendy przed-polityką były równoległe? (event study)
  4. ✅ Testy placebo przechodzą?
  5. ✅ Błędy standardowe klastrowane na poziomie jednostki?
  6. ✅ Przy staggered adoption — używasz nowoczesnego estymatora?

Siła DiD: kontroluje wszystkie niezmienne w czasie cechy nieobserwowalne (przez różnicowanie) i wspólne szoki czasowe. To czyni go jednym z najpopularniejszych narzędzi ekonometrii przyczynowej.


Powiązane: Zmienne instrumentalne · Dane panelowe

📚 Zasoby do nauki
  • Angrist & Pischke, Mostly Harmless Econometrics, rozdz. 5
  • Card & Krueger (1994) — klasyczne badanie płacy minimalnej
  • Pakiet R: fixest::feols(), did (Callaway & Sant’Anna)
  • Python: linearmodels, statsmodels
💻 Kod źródłowy

Wymagane pakiety:

install.packages(c("fixest", "did", "ggplot2"))
library(fixest)   # szybkie modele z efektami stałymi
import statsmodels.formula.api as smf
import pandas as pd