Difference-in-Differences (DiD) — ocena efektów polityk
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:
| Przed | Po | Róż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ć:
- Trendy przed-polityką — sprawdź, czy grupy poruszały się równolegle PRZED interwencją
- Event study — estymuj efekty dla wielu okresów przed i po
- 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
- ✅ Czy masz wyraźną grupę objętą i kontrolną?
- ✅ Czy masz dane przed i po interwencji?
- ✅ Czy trendy przed-polityką były równoległe? (event study)
- ✅ Testy placebo przechodzą?
- ✅ Błędy standardowe klastrowane na poziomie jednostki?
- ✅ 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
- 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
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