Skip to contents

Group-time average treatment effects for staggered adoption, with no unit used as a control after it is treated (Callaway and Sant'Anna 2021), or the extended two-way fixed-effects regression with a coefficient for every cohort and period (Wooldridge 2021); aggregated to one effect and to effects by time since adoption.

Usage

stgatt(
  data,
  unit,
  time,
  y,
  ftreat,
  ctrl = c("notyet", "never"),
  meth = c("csa", "wooldridge"),
  cl = NULL,
  reps = 499L,
  seed = NULL,
  conflvl = 0.95,
  covs = NULL,
  estmeth = c("dr", "ipw", "reg")
)

Arguments

data

A data frame, one row per unit and period.

unit, time, y

Column names: the unit, the period (numeric, for example a year or a month index) and the outcome.

ftreat

Column name of the period a unit is first treated (NA, 0 or Inf for a unit never treated). Treatment is taken as staying on.

ctrl

"notyet" (default) or "never".

meth

"csa" (default) or "wooldridge".

cl

Column name of the clusters (default the unit), or two column names for two-way clustering (Cameron, Gelbach and Miller 2011): \(V_a + V_b - V_{ab}\), each term with the small-sample factor of the dimension with fewer clusters, and t on its \(G - 1\) degrees of freedom. A composite cluster (district by month, say) is one column made with interaction(). Fewer than ten clusters give a warning (CR1 errors are unreliable with so few; wcb is made for that case), and so does a cluster that changes within a unit.

reps

Bootstrap replications (default 499; 0 for none).

seed

Optional seed for the bootstrap.

conflvl

Confidence level of the intervals reported (default 0.95).

covs

Optional column names of covariates for conditional parallel trends (meth = "csa" only).

estmeth

With covariates: "dr" (doubly robust, default), "ipw" or "reg".

Value

A list of class rmbl_staggered_att: attgt (a data frame: cohort, period, evtm, estimate, se, lower, upper, ncoh), overall (one row: estimate, se, lower, upper), dynamic (by evtm), method, control, conflvl. plot() draws the dynamic effects.

Details

Let the cohort \(g\) be the units first treated in period \(g\), and \(C\) the comparison units: never treated (control = "never"), or not yet treated by period \(\max(t, g - 1)\) ("notyet", the default). For meth = "csa", $$ATT(g, t) = \big(\bar Y_{g,t} - \bar Y_{g,b}\big) - \big(\bar Y_{C,t} - \bar Y_{C,b}\big),$$ with base period \(b = g - 1\) for \(t \ge g\), and \(b = t - 1\) before (the "varying" base of the did package), so a pre-period ATT is a placebo of one period. It needs a balanced panel.

With covs, parallel trends need only hold conditionally on them, and each \(ATT(g, t)\) is the two-period estimator of Sant'Anna and Zhao (2020) on the cohort and its comparison units, with the covariates taken at the base period \(b\) (use covariates fixed before treatment: one the policy moves is a bad control). With \(\Delta Y = Y_t - Y_b\), \(D\) the cohort indicator, \(\hat m(X)\) the least-squares fit of \(\Delta Y\) on \(X\) among the comparison units and \(\hat p(X)\) the logistic propensity of belonging to the cohort, $$\widehat{ATT}_{dr} = \frac{\sum D (\Delta Y - \hat m)}{\sum D} - \frac{\sum w (\Delta Y - \hat m)}{\sum w}, \quad w = \frac{\hat p (1 - D)}{1 - \hat p};$$ estmeth = "ipw" drops \(\hat m\) (normalised inverse probability weighting) and "reg" drops the weights (outcome regression). The doubly robust form is consistent when either the propensity or the outcome model is right. Comparison units with \(\hat p \ge 0.995\) are trimmed, as the DRDID package does. These agree with did::attgt() to rounding error.

For meth = "wooldridge", the regression of the outcome on unit and period effects and an indicator for every treated cohort-period cell, with never-treated (or last-treated) units as the comparison, gives each \(ATT(g, t)\) as a coefficient; without covariates it equals the imputation estimator.

The overall effect averages the post-adoption \(ATT(g, t)\) weighted by cohort size; the event-time effect \(\theta(e)\) averages \(ATT(g, g + e)\) over the cohorts observed at \(e\), weighted by cohort size. Standard errors come from the cluster bootstrap: units (or clusters) are resampled with replacement reps times and everything recomputed; intervals are normal with the bootstrap standard error.

References

Callaway B, Sant'Anna PHC (2021). Difference-in-differences with multiple time periods. Journal of Econometrics 225(2):200-230. doi:10.1016/j.jeconom.2020.12.001

Sant'Anna PHC, Zhao J (2020). Doubly robust difference-in-differences estimators. Journal of Econometrics 219(1):101-122. doi:10.1016/j.jeconom.2020.06.003

Wooldridge JM (2021). Two-way fixed effects, the two-way Mundlak regression, and difference-in-differences estimators. doi:10.2139/ssrn.3906345

Applied to a reform rolled out in four stages across judicial districts: Acosta, Mejia and Zorro Medina (2023), see plndid.

See also

Examples

p <- polex()
s <- stgatt(p, "district", "month", "crmrate", "refmonth", reps = 99,
                   seed = 1)
s$overall
#>    estimate        se     lower     upper
#> 1 -2.825844 0.3400471 -3.492324 -2.159364
head(s$dynamic)
#>   evtm   estimate        se     lower     upper
#> 1  -23 -0.3805333 0.7617010 -1.873440 1.1123731
#> 2  -22 -0.6088000 0.9469157 -2.464721 1.2471207
#> 3  -21 -0.4860667 0.7276161 -1.912168 0.9400347
#> 4  -20  1.3736000 0.6214022  0.155674 2.5915260
#> 5  -19 -0.4742667 0.3297593 -1.120583 0.1720498
#> 6  -18  0.1654667 0.6897981 -1.186513 1.5174460
plot(s)


# your own panel: eight towns over ten years, four adopting a by-law (two
# in 2019, two in 2021), with each town's population, county and income
own <- data.frame(
  town = rep(c("A", "B", "C", "D", "E", "F", "G", "H"), each = 10),
  year = rep(2015:2024, times = 8)
)
i <- match(own$town, unique(own$town))
own$first <- c(2019, 2019, 2021, 2021, NA, NA, NA, NA)[i]
own$bylaw <- as.integer(!is.na(own$first) & own$year >= own$first)
own$pop <- c(12, 30, 8, 22, 15, 40, 9, 18)[i] * 1000
own$county <- rep(c("North", "South"), 4)[i]
own$income <- 50 + own$year - 2015 + cos(seq_len(80))
own$rate <- 30 + i + 0.5 * (own$year - 2015) - 4 * own$bylaw +
  0.2 * own$income + sin(seq_len(80))
# each adoption cohort against the towns not yet treated
stgatt(own, "town", "year", "rate", "first", reps = 49, seed = 1)$overall
#>   estimate        se     lower     upper
#> 1 -3.89384 0.8365213 -5.533391 -2.254288