Type at least 2 letters. Enter opens the first result, Esc closes.

    Causal Inference · Lesson 7 · Estimating causal effects

    Regression discontinuity, difference-in-differences and synthetic control

    Intermediate 85 min 4 interactive figures 7 videos Colab notebook

    Before this lesson: lesson 02 (potential outcomes, ATT) and lesson 06 (the Wald ratio, needed for fuzzy RDD). Helpful: regression and its standard errors (probability 13) and linear regression in ML. Time: about 85 minutes, plus the notebook.


    Why this matters #

    • Thresholds of ML models. A fraud model blocks transactions with a score of at least 0.8; a credit model approves applicants above a score; a ranking puts items above a cutoff on the first page. Each rule is a regression discontinuity: it lets you estimate the effect of the action (blocking, approving, showing) near the threshold, without an experiment.
    • Launches that cannot be randomized. A feature launched in one country, a price change in one region, a policy in one state. Difference-in-differences and synthetic control are the standard tools; Google's CausalImpact (Brodersen et al., 2015) is a Bayesian relative of synthetic control used for marketing.
    • Economics and public health. Minimum wages (Card and Krueger, 1994), tobacco control (Abadie, Diamond and Hainmueller, 2010), class size (Angrist and Lavy, 1999), and thousands of policy evaluations use these designs.

    1. Predict first #

    2. The picture #

    2.1 A jump at a cutoff #

    Let RR be the running variable (the exam score) and cc the cutoff. In a sharp design, T=1[R≥c]T = \mathbb 1[R \ge c]: everyone above the cutoff is treated, nobody below is. Plot the outcome against RR. If the scholarship has no effect, the cloud is smooth at cc. If it has an effect, the cloud jumps at cc. The size of the jump is the effect.

    Why is this a fair comparison? Students with R=69.9R = 69.9 and R=70.1R = 70.1 are almost the same in everything: ability, effort, family. Only the treatment changes at cc. So near cc, the assignment is "as good as random". Far from cc the groups differ a lot, which is why we look only near cc.

    To measure the jump, fit a line on each side using units close to cc, and read both lines at cc. The bandwidth hh says how close is close.

    Interactive figure: loading…

    Move the bandwidth and switch the fit. Dots are units: blue treated (R≥0R \ge 0), orange control. The dashed aqua curves are the true regression functions; the thin dotted ones are the potential outcomes we never see on each side. The yellow window holds the units the local linear fit uses. Make hh large with curvature on: the lines miss the curve and the jump is biased. Make hh small: the jump gets noisy (press New sample). Try global degree 5 and press New sample a few times: the ends of the curves swing. Switch to fuzzy: the share treated jumps by only 0.5, and the effect is the ratio of the two jumps.

    2.2 Two groups, two periods #

    Now the treatment is a policy that starts at a known date in one group (the treated group) and not in another (the control group). The naive comparisons both fail:

    • Before–after in the treated group mixes the effect with everything else that changed over time (the economy, the season).
    • Treated vs control after the policy mixes the effect with the fixed differences between the groups.

    Difference-in-differences (DiD) uses the control group's change over time as the counterfactual change for the treated group. Move the control line up or down until it starts at the treated group's level; where the treated group ends up above that moved line is the effect. This works if, without the policy, the two groups would have moved in parallel: the parallel-trends assumption.

    Interactive figure: loading…

    Bend the trends and switch the view. Blue: treated group, orange: control group, policy from period 5 (shaded). The dashed dark line is the DiD counterfactual: the control line moved to the treated group's level before the policy. The dashed aqua line is the true Y(0)Y(0) of the treated group, which only a simulation can show. With trend violation 0 they agree. Set the violation to 0.2 from the start, then open event study: the coefficients before the policy slope away from 0, a warning you can see. Now choose only after the policy: the pre-period looks perfect, but the estimate is still biased.

    Quick check

    Average sales per store: the treated region goes from 50 to 58 after a launch; the control region goes from 40 to 45 over the same time. What is the DiD estimate of the launch effect?

    3. Definitions and main results #

    3.1 Sharp regression discontinuity #

    Setting. Each unit has a running variable RR, observed before treatment. T=1[R≥c]T = \mathbb 1[R \ge c]. Potential outcomes Y(0),Y(1)Y(0), Y(1) as in lesson 02.

    Note what is unusual. At any value rr, all units are treated (if r≥cr \ge c) or all are controls. So positivity fails everywhere: we never see treated and control units with the same RR (lesson 05 needed both). RDD replaces positivity by a smoothness assumption and a limit.

    Assumption (continuity). E[Y(0)∣R=r]\mathbb{E}[Y(0) \mid R = r] and E[Y(1)∣R=r]\mathbb{E}[Y(1) \mid R = r] are continuous in rr at r=cr = c.

    In words: nothing other than the treatment changes suddenly at the cutoff. Without the scholarship, expected earnings would change smoothly with the exam score.

    Theorem (identification of sharp RDD; Hahn, Todd and van der Klaauw, 2001). Under continuity and consistency,

    τc:=E[Y(1)−Y(0)∣R=c]=lim⁡r↓cE[Y∣R=r]−lim⁡r↑cE[Y∣R=r].\tau_c := \mathbb{E}[Y(1) - Y(0) \mid R = c] = \lim_{r \downarrow c} \mathbb{E}[Y \mid R = r] - \lim_{r \uparrow c} \mathbb{E}[Y \mid R = r] .

    Proof.

    1. For r≥cr \ge c, every unit has T=1T = 1, so by consistency Y=Y(1)Y = Y(1) and E[Y∣R=r]=E[Y(1)∣R=r]\mathbb{E}[Y \mid R = r] = \mathbb{E}[Y(1) \mid R = r].
    2. For r<cr < c, every unit has T=0T = 0, so E[Y∣R=r]=E[Y(0)∣R=r]\mathbb{E}[Y \mid R = r] = \mathbb{E}[Y(0) \mid R = r].
    3. Take the limit from above in step 1. By continuity of E[Y(1)∣R=r]\mathbb{E}[Y(1) \mid R = r] at cc, the limit is E[Y(1)∣R=c]\mathbb{E}[Y(1) \mid R = c].
    4. Take the limit from below in step 2. By continuity of E[Y(0)∣R=r]\mathbb{E}[Y(0) \mid R = r] at cc, the limit is E[Y(0)∣R=c]\mathbb{E}[Y(0) \mid R = c].
    5. Subtract: the jump equals E[Y(1)∣R=c]−E[Y(0)∣R=c]=τc\mathbb{E}[Y(1) \mid R = c] - \mathbb{E}[Y(0) \mid R = c] = \tau_c. ■\blacksquare

    The effect is local. τc\tau_c is the average effect for units at the cutoff. It says nothing direct about students with a score of 90 or 40. Extending it needs extra assumptions (for example, that the effect does not change with RR).

    3.2 Estimation: local linear regression and the bandwidth #

    We need the two limits, which are values of regression functions at the boundary of their domains.

    Local linear regression. On each side of cc, fit a line by weighted least squares, using only units with ∣R−c∣<h|R - c| < h, with weights from a kernel. The common choice is the triangular kernel wi=max⁡(0, 1−∣Ri−c∣/h)w_i = \max(0,\, 1 - |R_i - c|/h), which gives the most weight to units near the cutoff. The estimate is

    τ^c=a^+−a^−,(a^±,b^±)=arg⁡min⁡a,b∑i on side ±wi (Yi−a−b (Ri−c))2.\hat\tau_c = \hat a_+ - \hat a_-, \qquad (\hat a_\pm, \hat b_\pm) = \arg\min_{a, b} \sum_{i \text{ on side } \pm} w_i\,\big(Y_i - a - b\,(R_i - c)\big)^2 .

    Why a line and not just the mean of the nearby YY values? At a boundary, a local mean is biased by the slope of the regression function: the units on one side of cc all lie on one side of it. A local line removes this first-order bias (Fan and Gijbels, 1996).

    Bias and variance in hh. A large hh uses many units (small variance) but includes units where the true function bends away from a line (large bias). A small hh has little bias and much variance. For a smooth function, the bias of local linear regression is of order h2h^2 and the variance of order 1/(nh)1/(nh). Data-driven bandwidths balance the two: Imbens and Kalyanaraman (2012) minimize the mean squared error of τ^c\hat\tau_c.

    Robust bias-corrected inference

    The MSE-optimal bandwidth leaves a bias of the same order as the standard error. So the usual interval τ^c±1.96 SE\hat\tau_c \pm 1.96\,\mathrm{SE} is centered in the wrong place and covers the truth less often than 95%. Calonico, Cattaneo and Titiunik (2014) subtract an estimate of the bias (from a local quadratic fit) and widen the standard error to include the uncertainty of that correction. This "robust bias-corrected" interval is the default of the rdrobust software in R, Stata and Python.

    Do not use high-order global polynomials. An old practice fits a polynomial of degree 3–5 in RR to all the data on each side. Gelman and Imbens (2019) give three reasons against it: the estimate puts large weights on units far from the cutoff, it is sensitive to the degree, and its confidence intervals are too short. Use local linear (or local quadratic) fits.

    Quick check

    You increase the bandwidth from 0.1 to 1.0 in an RDD where the true regression function is curved. What happens to the local linear estimate?

    3.3 Threats, checks and fuzzy RDD #

    Continuity cannot be tested directly: it is a statement about potential outcomes at the cutoff. But it has testable consequences.

    • Manipulation of the running variable. If students who score 69 can ask for a regrade, or retake the exam, the students just above 70 include the most persistent ones. Then the two sides differ by more than the treatment. The sign is bunching: too many units just above the cutoff. McCrary (2008) proposed a formal test of continuity of the density of RR at cc; the notebook builds a simple version by counting units in bins next to the cutoff.
    • Covariate continuity. Pre-treatment covariates (gender, past grades) must not jump at cc. Run the same RDD with each covariate as the outcome; the jumps should be near 0.
    • Placebo cutoffs. Run the RDD at fake cutoffs (for example 60 or 80); there should be no jumps.
    • Other rules at the same cutoff. If the score 70 also decides admission to a special class, the jump measures both treatments together.

    Fuzzy RDD. Often the cutoff changes only the probability of treatment: above 70 you are eligible, but not everyone takes the scholarship, and some students below get it in other ways. Then P(T=1∣R)P(T = 1 \mid R) jumps at cc by less than 1. The cutoff indicator Z=1[R≥c]Z = \mathbb 1[R \ge c] is an instrument (lesson 06), used only near cc:

    τfuzzy=lim⁡r↓cE[Y∣R=r]−lim⁡r↑cE[Y∣R=r]lim⁡r↓cE[T∣R=r]−lim⁡r↑cE[T∣R=r].\tau_{\text{fuzzy}} = \frac{\lim_{r \downarrow c} \mathbb{E}[Y \mid R = r] - \lim_{r \uparrow c} \mathbb{E}[Y \mid R = r]}{\lim_{r \downarrow c} \mathbb{E}[T \mid R = r] - \lim_{r \uparrow c} \mathbb{E}[T \mid R = r]} .

    Under continuity, monotonicity (crossing the cutoff makes no one less likely to take the treatment) and exclusion (crossing the cutoff changes YY only through TT), this is the average effect for compliers at the cutoff (Hahn, Todd and van der Klaauw, 2001): a LATE that is local twice. Estimate each jump with local linear regression, or run 2SLS with ZZ as instrument in the window.

    Quick check

    At a cutoff, the share of treated units jumps from 0.30 to 0.70, and the mean outcome jumps by 2.0. What is the fuzzy RDD estimate?

    3.4 Difference-in-differences #

    Note on notation. In this lesson only, tt is time. We have a treated group (G=1G = 1) and a control group (G=0G = 0), and two periods, "pre" and "post". The treated group is treated in the post period only. Potential outcomes now carry a time index: Ypost(1)Y_{\text{post}}(1), Ypost(0)Y_{\text{post}}(0), and so on.

    The estimand is the effect on the treated in the post period: τATT=E[Ypost(1)−Ypost(0)∣G=1]\tau_{\mathrm{ATT}} = \mathbb{E}[Y_{\text{post}}(1) - Y_{\text{post}}(0) \mid G = 1].

    Assumptions.

    1. Parallel trends. Without treatment, the two groups would have changed by the same amount on average: E[Ypost(0)−Ypre(0)∣G=1]=E[Ypost(0)−Ypre(0)∣G=0]\mathbb{E}[Y_{\text{post}}(0) - Y_{\text{pre}}(0) \mid G = 1] = \mathbb{E}[Y_{\text{post}}(0) - Y_{\text{pre}}(0) \mid G = 0].
    2. No anticipation. The treatment does not change the treated group's outcome before it starts: Ypre=Ypre(0)Y_{\text{pre}} = Y_{\text{pre}}(0) for the treated group.
    3. Consistency and no interference. In particular, the policy does not affect the control group (no spillovers), so the control group shows Y(0)Y(0) in both periods.

    Parallel trends allows the groups to differ in levels: New Jersey stores can be smaller than Pennsylvania stores. It forbids differences in trends. It is an assumption about Ypost(0)Y_{\text{post}}(0) of the treated group, which is never observed.

    Theorem (identification of DiD). Under assumptions 1–3,

    τATT=(E[Ypost∣G=1]−E[Ypre∣G=1])−(E[Ypost∣G=0]−E[Ypre∣G=0]).\tau_{\mathrm{ATT}} = \big(\mathbb{E}[Y_{\text{post}} \mid G{=}1] - \mathbb{E}[Y_{\text{pre}} \mid G{=}1]\big) - \big(\mathbb{E}[Y_{\text{post}} \mid G{=}0] - \mathbb{E}[Y_{\text{pre}} \mid G{=}0]\big) .

    Proof.

    1. The treated group is treated in the post period, so by consistency E[Ypost∣G=1]=E[Ypost(1)∣G=1]\mathbb{E}[Y_{\text{post}} \mid G{=}1] = \mathbb{E}[Y_{\text{post}}(1) \mid G{=}1].
    2. By no anticipation, E[Ypre∣G=1]=E[Ypre(0)∣G=1]\mathbb{E}[Y_{\text{pre}} \mid G{=}1] = \mathbb{E}[Y_{\text{pre}}(0) \mid G{=}1].
    3. The control group is never treated (and has no spillovers), so its observed change is E[Ypost(0)−Ypre(0)∣G=0]\mathbb{E}[Y_{\text{post}}(0) - Y_{\text{pre}}(0) \mid G{=}0].
    4. By parallel trends, replace that by E[Ypost(0)−Ypre(0)∣G=1]\mathbb{E}[Y_{\text{post}}(0) - Y_{\text{pre}}(0) \mid G{=}1].
    5. Put steps 1, 2 and 4 into the right side: E[Ypost(1)∣G=1]−E[Ypre(0)∣G=1]−E[Ypost(0)∣G=1]+E[Ypre(0)∣G=1]\mathbb{E}[Y_{\text{post}}(1) \mid G{=}1] - \mathbb{E}[Y_{\text{pre}}(0) \mid G{=}1] - \mathbb{E}[Y_{\text{post}}(0) \mid G{=}1] + \mathbb{E}[Y_{\text{pre}}(0) \mid G{=}1]. The two Ypre(0)Y_{\text{pre}}(0) terms cancel, which leaves E[Ypost(1)−Ypost(0)∣G=1]=τATT\mathbb{E}[Y_{\text{post}}(1) - Y_{\text{post}}(0) \mid G{=}1] = \tau_{\mathrm{ATT}}. ■\blacksquare

    The regression form. With unit ii in period tt, Treati=1\mathrm{Treat}_i = 1 for the treated group and Postt=1\mathrm{Post}_t = 1 in the post period,

    Yit=α+β Treati+γ Postt+δ Treati Postt+εit.Y_{it} = \alpha + \beta\,\mathrm{Treat}_i + \gamma\,\mathrm{Post}_t + \delta\,\mathrm{Treat}_i\,\mathrm{Post}_t + \varepsilon_{it} .

    The model has four parameters for four cell means, so OLS fits them exactly: α\alpha is the control group's pre mean, β\beta the gap between the groups before, γ\gamma the control group's change, and δ\delta the DiD. The regression form is useful because it extends: add covariates, more periods, unit fixed effects (one αi\alpha_i per unit) and period fixed effects (one λt\lambda_t per period).

    Standard errors. Outcomes of the same unit (or state) are correlated over time. Treating every unit-period as independent makes the standard errors far too small (Bertrand, Duflo and Mullainathan, 2004). Cluster the standard errors at the level of the treatment assignment (unit, state). With very few clusters (for example, two states), even clustered standard errors are not reliable.

    Quick check

    Which of these is the parallel-trends assumption of DiD?

    With several periods before and after the policy (policy at period t∗t^*), estimate one coefficient per period relative to the last pre-period:

    Yit=αi+λt+∑k≠t∗−1βk 1[t=k] Treati+εit.Y_{it} = \alpha_i + \lambda_t + \sum_{k \ne t^* - 1} \beta_k\, \mathbb 1[t = k]\, \mathrm{Treat}_i + \varepsilon_{it} .

    This is an event study. Each βk\beta_k is a small DiD between period kk and the reference period t∗−1t^* - 1. Read the plot in two parts:

    • Before the policy (k<t∗k < t^*): the βk\beta_k test for pre-trends. If the groups already moved apart before the policy, parallel trends after it is hard to believe.
    • After the policy (k≥t∗k \ge t^*): the βk\beta_k show how the effect changes over time (it may grow, or fade).

    Flat pre-trends support parallel trends but do not prove it. The assumption is about the post-period, and a violation can start exactly with the policy (try only after the policy in the widget). Pre-trend tests also have low power: a real trend can hide in wide intervals, and choosing studies that "pass" the test can make the bias worse (Roth, 2022).

    Two more checks. Placebo outcomes: an outcome the policy cannot affect should show no DiD effect. Scale: parallel trends in levels and in logs cannot both hold when the groups start at different levels and change over time. Choose the scale with a reason (for example, percent changes for growth processes) and say so.

    3.6 Staggered adoption and two-way fixed effects #

    Often units adopt the treatment at different times. The common regression is two-way fixed effects (TWFE): Yit=αi+λt+δDit+εitY_{it} = \alpha_i + \lambda_t + \delta D_{it} + \varepsilon_{it}, with Dit=1D_{it} = 1 if unit ii is treated at time tt. When the effect is the same for all units and periods, δ^\hat\delta is fine. When effects change over time, δ^\hat\delta can be badly wrong.

    Why TWFE fails with staggered timing, with a small example

    Goodman-Bacon (2021) showed that the TWFE coefficient is a weighted average of all 2×2 DiD comparisons in the data. Some of them use already-treated units as controls. If the effect in those units is still changing, their change is not the counterfactual change, and the comparison is biased.

    Example (verified in section 5). Two groups, three periods, untreated outcomes all 0 (so trends are exactly parallel). The early group is treated from period 2 with effects a=1a = 1 (period 2) and b=4b = 4 (period 3): the effect grows. The late group is treated from period 3 with effect c=1c = 1. Every effect is positive; their average is (1+4+1)/3=2(1 + 4 + 1)/3 = 2.

    period 1 period 2 period 3
    early group 0 1 4
    late group 0 0 1

    The two 2×2 comparisons inside this table:

    1. Early vs late in periods 1–2 (the late group is not yet treated, a clean control): (1−0)−(0−0)=1=a(1 - 0) - (0 - 0) = 1 = a.
    2. Late vs early in periods 2–3 (the early group is already treated and serves as control): (1−0)−(4−1)=−2(1 - 0) - (4 - 1) = -2. The early group's growing effect (b−a=3b - a = 3) is subtracted as if it were a trend.

    Here TWFE gives each comparison weight 1/21/2: δ^=(1+(−2))/2=−0.5\hat\delta = (1 + (-2))/2 = -0.5. In terms of cell effects, δ^=a−b/2+c/2\hat\delta = a - b/2 + c/2: the cell "early group, period 3" gets weight −1/2-1/2. A negative weight on a positive effect turns the estimate negative.

    What to do instead. Estimate each group-time effect ATT(g,t)\mathrm{ATT}(g, t), the effect in period tt for the group that adopted at time gg, by a clean 2×2 DiD against units that are never treated or not yet treated. Then average them with weights you choose (Callaway and Sant'Anna, 2021). Related estimators: Sun and Abraham (2021) for event studies, de Chaisemartin and D'Haultfœuille (2020).

    Interactive figure: loading…

    Raise the growth of the effect and shrink the never-treated group. Each line is a group; a line is orange while its group is untreated and blue once it is treated. The grid below shows the weight TWFE gives to each treated group-period; magenta cells have negative weights. With growth 0, TWFE (magenta readout) equals the true average (aqua). With growth 2 and a small never-treated group, TWFE falls far below the truth and can turn negative, while the group-time average stays exact.

    3.7 Synthetic control #

    Sometimes only one unit is treated: California's 1988 tobacco-control program (Proposition 99), one country's launch. No single control unit is similar enough, and an average of all controls is not either. Synthetic control (Abadie, Diamond and Hainmueller, 2010) builds the comparison as a weighted average of donor units:

    Y^1t(0)=∑j=2J+1wjYjt,wj≥0,∑jwj=1,\hat Y_{1t}(0) = \sum_{j=2}^{J+1} w_j Y_{jt}, \qquad w_j \ge 0, \quad \sum_j w_j = 1,

    with weights chosen so that Y^1t(0)\hat Y_{1t}(0) follows the treated unit's outcome Y1tY_{1t} closely in the pre-period (and matches pre-treatment predictors). The effect estimate in each post period is the gap Y1t−Y^1t(0)Y_{1t} - \hat Y_{1t}(0).

    • Why w≥0w \ge 0 and ∑w=1\sum w = 1? The synthetic unit is then a convex combination of real units: no extrapolation outside the data, and the weights are easy to read ("40% Utah, 30% Nevada …"). The weights are usually sparse.
    • When does it work? When the pre-period is long and the fit is close: a synthetic control that tracks the treated unit for many periods, through ups and downs, is likely to share its hidden drivers. When the pre-period fit is poor, do not trust the gap.
    • Inference by placebos. There is only one treated unit, so the usual standard errors do not apply. Instead, apply the method to each donor as if it were treated (placebo in space) and compare the treated unit's gap with the placebo gaps, usually by the ratio of post- to pre-period root mean squared prediction error (RMSPE). In the tobacco study, California had the largest ratio of 39 states, a permutation p-value of 1/39≈0.0261/39 \approx 0.026, and the estimated drop in cigarette sales was about 26 packs per person per year by 2000.
    Interactive figure: loading…

    Press Fit to the pre-period, then open placebo test. The blue line is the treated unit; the dark line is the synthetic control; the dashed aqua line is the treated unit's true Y(0)Y(0). Donors with weight are yellow (thicker = more weight). With equal weights the synthetic line misses the pre-period, and the post-period gap is not the effect. After Fit it tracks the treated unit before period 20, and the gap after it is close to the true effect. In the placebo view, the treated unit's gap stands out among the donors' gaps.

    3.8 Which design when? #

    Design You need It estimates Key untestable assumption Main checks
    RDD a rule with a cutoff on a running variable effect at the cutoff (fuzzy: for compliers at the cutoff) continuity of potential outcomes at cc density (no bunching), covariate jumps, placebo cutoffs, bandwidth sensitivity
    DiD a policy change, a comparison group, pre-periods ATT in the post-periods parallel trends after the policy; no anticipation event study (pre-trends), placebo outcomes, scale
    Synthetic control one or few treated units, many donors, a long pre-period effect on the treated unit over time the weighted donors follow the treated unit's Y(0)Y(0) after the policy pre-period fit, placebo in space and in time, leave-one-donor-out
    IV (lesson 06) an as-if-random push on the treatment LATE for compliers exclusion first stage, balance of ZZ

    Answers to section 1. (1) No. It estimates the effect for students near 70. Students with 95 also got the scholarship, but the design says nothing direct about them. (2) (21.03−20.44)−(21.17−23.33)=0.59−(−2.16)=2.75(21.03 - 20.44) - (21.17 - 23.33) = 0.59 - (-2.16) = 2.75 full-time-equivalent workers per store (the paper reports 2.76 from unrounded means). Employment did not fall in New Jersey relative to Pennsylvania. (3) No. Flat pre-trends support the assumption, but parallel trends is about the post-period, where the treated group's Y(0)Y(0) is never observed. A shock that starts with the policy cannot be seen in the pre-period.

    4. Worked example #

    (a) A sharp RDD by hand. The cutoff is c=70c = 70. Near the cutoff the (noise-free) data are:

    RR 66 68 69 70 71 73
    YY 58.0 59.0 59.5 63.0 63.5 64.5

    Left of the cutoff, the points lie on a line with slope (59.5−58.0)/(69−66)=0.5(59.5 - 58.0)/(69 - 66) = 0.5. Extend it to R=70R = 70: 59.5+0.5⋅(70−69)=60.059.5 + 0.5 \cdot (70 - 69) = 60.0. Right of the cutoff, the slope is (64.5−63.0)/(73−70)=0.5(64.5 - 63.0)/(73 - 70) = 0.5, and the value at R=70R = 70 is 63.063.0. The jump is τ^70=63.0−60.0=3.0\hat\tau_{70} = 63.0 - 60.0 = 3.0. Note that the left line must be extrapolated from 69 to 70: there are no untreated units at the cutoff. The naive comparison of the means of the two sides, (63.0+63.5+64.5)/3−(58.0+59.0+59.5)/3=63.67−58.83=4.83(63.0 + 63.5 + 64.5)/3 - (58.0 + 59.0 + 59.5)/3 = 63.67 - 58.83 = 4.83, also counts the slope of YY in RR as an effect.

    (b) Fuzzy RDD. Suppose only 70% of the students above 70 take the scholarship, and 30% of those below get it from other sources. The jump in P(T=1)P(T = 1) at the cutoff is 0.70−0.30=0.400.70 - 0.30 = 0.40. If the jump in YY is 2.02.0, the effect for compliers at the cutoff is 2.0/0.40=5.02.0/0.40 = 5.0.

    (c) DiD with the minimum-wage data. Card and Krueger (1994, Table 3) surveyed fast-food restaurants in New Jersey (treated: the minimum wage rose from $4.25 to $5.05 on 1 April 1992) and eastern Pennsylvania (control) before and after the rise. Mean full-time-equivalent (FTE) employment per store:

    before (Feb–Mar 1992) after (Nov–Dec 1992) change
    New Jersey (treated) 20.44 21.03 +0.59+0.59
    Pennsylvania (control) 23.33 21.17 −2.16-2.16
    difference NJ − PA −2.89-2.89 −0.14-0.14 2.75\mathbf{2.75}
    1. Before–after in New Jersey: +0.59+0.59. This mixes the effect with the regional trend.
    2. The Pennsylvania change, −2.16-2.16, estimates that trend (a weakening economy in 1992).
    3. DiD: 0.59−(−2.16)=2.750.59 - (-2.16) = 2.75. The paper reports 2.762.76 (standard error 1.361.36) from unrounded means.
    4. The same number from the other direction: the gap NJ − PA went from −2.89-2.89 to −0.14-0.14, a change of 2.752.75.
    5. The regression form reproduces the table: α=23.33\alpha = 23.33 (PA before), β=−2.89\beta = -2.89 (NJ − PA before), γ=−2.16\gamma = -2.16 (PA change), δ=2.75\delta = 2.75. Check one cell: NJ after =23.33−2.89−2.16+2.75=21.03= 23.33 - 2.89 - 2.16 + 2.75 = 21.03 ✓.

    The estimate rests on parallel trends: without the law, New Jersey stores would have lost about 2.16 workers like Pennsylvania stores. With only two periods, this cannot be checked with pre-trends; the paper adds a within-New-Jersey comparison of low- and high-wage stores as a second control group.

    (d) The TWFE example. In the table of section 3.6, the TWFE coefficient is a−b/2+c/2=1−2+0.5=−0.5a - b/2 + c/2 = 1 - 2 + 0.5 = -0.5, while every effect is positive and their average is 2. A group-time estimator with a never-treated group would recover each cell effect.

    (e) A tiny synthetic control. Treated unit before the policy: (2,4,6)(2, 4, 6). Donors: A =(1,3,5)= (1, 3, 5) and B =(3,5,7)= (3, 5, 7). With wA=wB=0.5w_A = w_B = 0.5, the synthetic unit is (2,4,6)(2, 4, 6): a perfect pre-period fit. After the policy, the treated unit is 5, A is 6 and B is 8, so the synthetic value is 0.5⋅6+0.5⋅8=70.5 \cdot 6 + 0.5 \cdot 8 = 7, and the effect estimate is 5−7=−25 - 7 = -2.

    5. In code #

    NumPy checks the numbers above. The notebook has these cells and the exercises.

    import numpy as np
    rng = np.random.default_rng(0)
    
    # (a) sharp RDD: local linear regression with a triangular kernel on each side of c = 0
    def rdd_local_linear(r, y, c=0.0, h=0.3):
        ends = []
        for side in (r < c, r >= c):
            x, w = r[side] - c, np.clip(1 - np.abs(r[side] - c) / h, 0, None)
            X = np.column_stack([np.ones_like(x), x]) * np.sqrt(w)[:, None]
            ends.append(np.linalg.lstsq(X, y[side] * np.sqrt(w), rcond=None)[0][0])   # value at the cutoff
        return ends[1] - ends[0]
    
    n = 5_000
    r = rng.uniform(-1, 1, n)
    y = 1 + 0.5 * r + 0.6 * np.sin(3 * r) + 1.0 * (r >= 0) + 0.4 * rng.normal(size=n)   # true jump 1.0
    for h in (0.1, 0.3, 1.0):
        print(f"h = {h}: jump {rdd_local_linear(r, y, h=h):.3f}")
    # h = 0.1: jump 0.980
    # h = 0.3: jump 0.962
    # h = 1.0: jump 1.364
    
    # (b) DiD from four means (Card and Krueger 1994, Table 3, FTE employment per store)
    nj_pre, nj_post, pa_pre, pa_post = 20.44, 21.03, 23.33, 21.17
    print(f"DiD = ({nj_post} - {nj_pre}) - ({pa_post} - {pa_pre}) = {(nj_post - nj_pre) - (pa_post - pa_pre):.2f}")
    # DiD = (21.03 - 20.44) - (21.17 - 23.33) = 2.75
    
    # (c) TWFE with staggered adoption: 2 groups, 3 periods, all effects positive
    #     early group treated from t = 2 (effects 1 at t = 2, 4 at t = 3); late group from t = 3 (effect 1)
    Y = np.array([[0., 1, 4],     # early group: Y(0) = 0, plus the effects
                  [0., 0, 1]])    # late group
    D = np.array([[0, 1, 1], [0, 0, 1]])
    g, t = np.meshgrid(range(2), range(3), indexing="ij")
    X = np.column_stack([D.ravel(), g.ravel() == 1, t.ravel() == 1, t.ravel() == 2, np.ones(6)]).astype(float)
    print(f"TWFE coefficient: {np.linalg.lstsq(X, Y.ravel(), rcond=None)[0][0]:.2f}")
    # TWFE coefficient: -0.50
    

    In (a), the bandwidth h=1h = 1 uses all the data, and the curvature of sin⁡(3r)\sin(3r) biases the jump by about 0.36. In (c), the regression has a dummy for the late group, dummies for periods 2 and 3 and a constant: exactly TWFE.

    6. Where it shows up #

    • Decision thresholds in ML systems. Any action triggered by a model score above a threshold (block, review, approve, show a warning) creates a sharp or fuzzy RDD. It is a cheap way to measure the effect of the action near the threshold, without an A/B test. Watch for manipulation: users and fraudsters learn thresholds.
    • Product launches by region or by date. DiD with an event study is the standard analysis when a feature launches in some markets first. With rollouts in waves, use group-time estimators, not TWFE.
    • Marketing measurement. Geo experiments and synthetic-control-style methods (CausalImpact; augmented and penalized synthetic control) estimate the effect of a campaign in one region.
    • Policy and health. Minimum wages, tobacco taxes, Medicaid expansions (staggered across US states), school-entry age cutoffs, test-score cutoffs for schools, and age-65 eligibility for Medicare are classic natural experiments.
    • ML for science. Climate and environmental policy studies (for example, the effect of a regulation on air quality in some regions) often use DiD and synthetic control with satellite or sensor data.

    7. Common mistakes #

    • Reading an RDD effect as the effect for everyone. It holds at the cutoff (fuzzy: for compliers at the cutoff). Say so.
    • Fitting high-order global polynomials. Use local linear regression with a data-driven bandwidth, and show that the result is stable for nearby bandwidths.
    • Ignoring manipulation. Plot the histogram of the running variable. Bunching just above the cutoff breaks the design.
    • Taking flat pre-trends as proof. They support parallel trends but cannot test the post-period. Argue why nothing else changed at the same time.
    • Using TWFE with staggered adoption and changing effects. Already-treated units become controls; weights can be negative. Use group-time estimators and plot their event study.
    • Unclustered standard errors in panels. Serial correlation makes naive standard errors far too small. Cluster at the level of treatment assignment.
    • Spillovers to the control group. If the policy also affects the control units (workers move across the border), the control change is not Y(0)Y(0), and DiD is biased.
    • Trusting a synthetic control with a poor pre-period fit. If the synthetic line cannot follow the treated unit before the policy, the post-period gap is not an effect estimate.

    8. Check yourself #

    1. State the continuity assumption of a sharp RDD and say why it is needed.
      AnswerE[Y(0)∣R=r]\mathbb{E}[Y(0) \mid R = r] and E[Y(1)∣R=r]\mathbb{E}[Y(1) \mid R = r] are continuous at r=cr = c. It is needed because no untreated units exist at or above cc: we learn E[Y(0)∣R=c]\mathbb{E}[Y(0) \mid R = c] only as the limit from below, which equals the value at cc only if the function has no jump there.
    2. A bank approves loans for credit scores of at least 650. Applicants with 649 can apply again a week later after paying off a small debt. Which RDD check would show this, and what would it show?
      AnswerThe density (McCrary-type) check: a histogram of scores would show too few applicants just below 650 and too many just above it (bunching). The applicants who reapply may differ (more motivated), so continuity is in doubt.
    3. In a fuzzy RDD, the cutoff raises the share treated by 0.25 and the mean outcome by 0.5. What is the estimate, and for whom?
      Answer0.5/0.25=2.00.5/0.25 = 2.0: the average effect for compliers at the cutoff (units who take the treatment because they cross the cutoff), under continuity, monotonicity and exclusion.
    4. Write the DiD regression and say what each coefficient means.
      AnswerYit=α+β Treati+γ Postt+δ TreatiPostt+εitY_{it} = \alpha + \beta\,\mathrm{Treat}_i + \gamma\,\mathrm{Post}_t + \delta\,\mathrm{Treat}_i\mathrm{Post}_t + \varepsilon_{it}. α\alpha: control mean before; β\beta: treated minus control before; γ\gamma: control change; δ\delta: the DiD, the ATT under parallel trends and no anticipation.
    5. An event study shows pre-period coefficients −0.9,−0.6,−0.3-0.9, -0.6, -0.3 for periods −4,−3,−2-4, -3, -2 (reference −1-1). What do you conclude?
      AnswerThe treated group was already rising relative to the control group before the policy, by about 0.3 per period. Parallel trends is not plausible; the DiD estimate would include this trend. Consider other control groups, a design with group-specific trends (with care), or another design.
    6. Why must synthetic-control weights be non-negative and sum to 1?
      AnswerThen the synthetic unit is a convex combination of real donors: it stays within the range of the data (no extrapolation) and is interpretable. With free regression weights, a good pre-period fit can come from over-fitting with large positive and negative weights.
    Recall cardsAnswer in your head first, then reveal. Add the cards to your review deck to see them again just before you would forget.
    1. What is the estimand of a sharp regression discontinuity design?

    2. What is the continuity assumption of RDD?

    3. Why use local linear regression and not high-order global polynomials in RDD?

    4. What does a density (McCrary) test check in an RDD?

    5. Compute a fuzzy RDD estimate when P(T=1)P(T = 1) jumps by 0.4 and the outcome jumps by 2.0.

    6. State the parallel-trends assumption.

    7. Compute DiD for treated 20.44→21.0320.44 \to 21.03 and control 23.33→21.1723.33 \to 21.17.

    8. What is an event study used for in DiD?

    9. Why can TWFE fail with staggered adoption?

    10. What is a synthetic control?

    Open the review →

    9. Interview angle #

    • "We launched a feature in one country only. How do you measure its effect?" — With a few comparable countries and many pre-launch weeks: DiD with an event study (check pre-trends, cluster by country), or synthetic control if no single country is comparable (check the pre-period fit, run placebos). Discuss what else changed at launch time (holidays, marketing) and spillovers (users who travel).
    • "A fraud model blocks transactions with score ≥ 0.8. How do you estimate the effect of blocking on later customer churn?" — RDD at 0.8: compare customers just above and just below with local linear regression. If some blocked transactions are released by manual review, it is a fuzzy RDD: divide by the jump in the blocking rate. Check that the score cannot be manipulated and that nothing else uses the same threshold. The answer holds for scores near 0.8 only.
    • "What is the parallel-trends assumption and how do you check it?" — State it in terms of Y(0)Y(0); check pre-trends with an event study and placebo outcomes; say that the post-period part is untestable.
    • "Why not just run TWFE on the rollout data?" — With staggered rollout and effects that grow, TWFE compares new adopters with earlier adopters and can be biased, even in sign. Use group-time estimators.

    10. Practice #

    The notebook for this lesson has five exercises, each with a check cell and a folded solution:

    1. rdd_local_linear(r, y, c, h) with a triangular kernel; a bandwidth sweep shows bias and variance.
    2. density_check(r, c, width): detect bunching created by manipulation of the running variable.
    3. did_2x2, did_regression and event_study: the same DiD three ways, with visible and invisible trend violations.
    4. twfe vs group_time_att under staggered adoption with growing effects.
    5. synthetic_control by projected gradient descent onto the simplex, then a placebo-in-space test.

    11. Watch and read #

    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    Bandwidths, fuzzy designs and manipulation checks, with pictures.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    Event studies and how to read pre-trends.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    The inventor of the method on weights, pre-period fit and placebo inference.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play