Causal Identification / DID

DID and Event Studies

DID is not just two subtractions; it uses the untreated group trend to construct the treated group counterfactual path.

Mechanism Lab

Animation: how DID constructs the treated counterfactual

The animation shows treated observed outcomes, control trends, and a dashed treated counterfactual. The treatment effect is the vertical gap after policy.

Step 1 / 5

Pre trends

Inspect whether treated and control groups move with similar pre-policy slopes.

pre-trend check

Animation Control

Reduced-motion users receive the same step states without continuous motion.

01 / Intuition

Core Intuition

DID estimates the post-policy counterfactual gap for treated units: observed outcome minus what would have happened without treatment.

The control group is not mainly a level comparison; it supplies the time trend for untreated potential outcomes.

Parallel trends says the treated and control groups would have had the same average change without treatment. Event studies inspect that logic over relative time.

02 / Math

From potential outcomes to the DID estimator

01 / Target

Let G=1 denote treated units and Post=1 post-policy periods. The usual target is the ATT for treated units after treatment.

ATT = E[Y(1) - Y(0) | G=1, Post=1]
    = E[Y | G=1, Post=1] - E[Y(0) | G=1, Post=1]

02 / Missing counterfactual

E[Y(0)|G=1,Post=1] is unobserved. DID builds it by adding the control-group untreated trend to treated pre-policy levels.

E[Y(0)|G=1,Post=1]
  = E[Y|G=1,Post=0]
    + {E[Y|G=0,Post=1] - E[Y|G=0,Post=0]}

03 / Parallel trends

The replacement depends on equal average changes in untreated potential outcomes across groups.

E[Y(0)_post - Y(0)_pre | G=1]
  = E[Y(0)_post - Y(0)_pre | G=0]

04 / Estimator

Substitute the counterfactual into the ATT and get the difference in treated changes minus control changes.

tau_DID = {E[Y|G=1,Post=1] - E[Y|G=1,Post=0]}
        - {E[Y|G=0,Post=1] - E[Y|G=0,Post=0]}

05 / Regression form

In a two-group, two-period design, the interaction coefficient equals the DID estimate.

Y_it = alpha + gamma G_i + lambda Post_t + tau(G_i * Post_t) + epsilon_it

03 / Code

Python code: DID, clustered standard errors, and event study

This demonstrates two-by-two DID and a panel event-study skeleton. Applied papers should add fixed effects, clustering, sample restrictions, and robustness checks.

import pandas as pd
import statsmodels.formula.api as smf

# df columns:
# unit_id, year, outcome, treated, post, first_treat_year
df = pd.read_csv("policy_panel.csv")
df["did"] = df["treated"] * df["post"]

did_model = smf.ols(
    "outcome ~ treated + post + did",
    data=df,
).fit(
    cov_type="cluster",
    cov_kwds={"groups": df["unit_id"]},
)
print(did_model.params["did"])
print(did_model.conf_int().loc["did"])

# Event-study skeleton with unit and year fixed effects.
df["event_time"] = df["year"] - df["first_treat_year"]
event_terms = []
for k in range(-4, 5):
    if k == -1:
        continue  # omitted baseline period
    col = f"event_{k:+d}".replace("+", "p").replace("-", "m")
    df[col] = ((df["event_time"] == k) & (df["treated"] == 1)).astype(int)
    event_terms.append(col)

formula = "outcome ~ " + " + ".join(event_terms) + " + C(unit_id) + C(year)"
event_model = smf.ols(formula, data=df).fit(
    cov_type="cluster",
    cov_kwds={"groups": df["unit_id"]},
)
print(event_model.params[event_terms])

04 / Case

Case: evaluating an education subsidy

  • Question: did a subsidy introduced in 2020 improve student test scores?
  • Treated schools received coverage; control schools remained uncovered in the same institutional environment.
  • The key check is not whether treated schools are higher after policy, but whether pre-policy trends are close and whether the post-policy gap exceeds the control trend.
  • Outputs should include the DID coefficient, event-study plot, sample flow, clustering level, alternative windows, and placebo policy timing.

05 / Risks

Common Pitfalls

Showing only post-policy differences without pre-trend diagnostics.
Interpreting TWFE mechanically under staggered adoption and heterogeneous effects.
Failing to justify the clustering level in school, region, or panel data.

References