scm_inference: Inference for Staggered SCM

View source: R/scm.R

scm_inferenceR Documentation

Inference for Staggered SCM

Description

Standard errors, confidence intervals and p-values for the aggregate ATT of a staggered SCM fit, by the two procedures of Ben-Michael, Feller & Rothstein (2022).

Usage

scm_inference(
  fit,
  method = c("jackknife", "wild_bootstrap"),
  n_boot = 1000L,
  level = 0.95,
  alternative = c("two.sided", "greater", "less"),
  seed = NULL
)

Arguments

fit

A staggered SCM fit from scm_fit() (method = "scm" on a panel with multiple adoption times). Sharp fits are rejected: use mspe_ratio_pval() or conformal_inference() instead.

method

"jackknife" (default) or "wild_bootstrap".

n_boot

Number of bootstrap draws ("wild_bootstrap" only). Default 1000.

level

Confidence level. Default 0.95.

alternative

Direction of the alternative hypothesis for the p-value: "two.sided" (default), "greater", or "less".

seed

Optional RNG seed ("wild_bootstrap" only).

Details

method = "jackknife" (default) is the leave-one-unit-out jackknife of their Appendix A.1: every unit in the panel – treated units and donors alike – is dropped in turn, the weights (and intercepts, for fixedeff fits) are refitted with the pooling parameter nu held at the fit's value, and the aggregate ATT is recomputed. The variance is (n-1)/n \sum_i (\widehat{ATT}^{(-i)} - \overline{ATT})^2, and the interval and p-value use the normal approximation. Because the donors are resampled too, the interval reflects the noise in the synthetic controls, which every treated unit of a cohort shares.

method = "wild_bootstrap" is the multiplier bootstrap of their Section 5.3, which adapts Otsu & Rai (2017). The ATT is written as a sum over every unit of the panel, and each unit's term is perturbed by its own golden-ratio two-point multiplier while the weights stay fixed: a treated unit's term is its effect's deviation from the ATT plus its residual, a donor's is its residual times the weight the synthetic controls put on it. Residuals are against a two-way (unit and period) model of the untreated outcome. Following Otsu & Rai's Equation (9), only the treated units' heterogeneity term is centred at the ATT; centring every unit, as the paper's Equation (13) reads, would make the interval grow with the size of the effect. The draws cost no refitting, so this is the choice for panels too large for the jackknife. It is conservative where the outcome has structure the two-way model does not absorb (unit-specific trends): in simulation a nominal 95% interval covered 96-99.5% across donor pools of 8 to 40 and 3 to 24 treated units, and 93% with 2 treated units.

The jackknife refits the model once per unit, which grows quickly with the panel: about 0.2 s at 50 units, 0.7 s at 100 and 3 s at 200, but over a minute at 400. Donors that carry no weight in any block are not refitted (dropping one leaves the solution unchanged), and each refit starts from the full fit's weights.

Both work with both staggered SCM paths (the partially pooled default and nu = "separate") and honour the aggregation weights ⁠N_treated x T_post⁠, or the weights given to scm_fit().

The jackknife treats the outcome as fixed: a fit with covariates partialled out keeps the partialled-out outcome, and one with cov_method = "balance" keeps its scaled covariate matrix, rather than re-estimating either without the dropped unit. A dropped unit that was the only member of its cohort removes that cohort from the estimate, as in the paper's J^{(-i)}.

Value

A coresynth_inference object with the standard fields (estimate, se, p_value, ci_lower, ci_upper, method, staggered, n_controls, alternative), compatible with tidy.coresynth_inference() and glance.coresynth_inference(). n_treated records the number of treated units. The wild bootstrap adds the draws in boot_ests, centred on the estimate; the jackknife adds the leave-one-out estimates in jack_ests (named by the dropped unit, NA where a refit was not possible).

References

Ben-Michael, E., Feller, A., & Rothstein, J. (2022). Synthetic controls with staggered adoption. JRSS-B, 84(2), 351-381.

Otsu, T., & Rai, Y. (2017). Bootstrap inference of matching estimators for average treatment effects. JASA, 112(520), 1720-1732.

Examples


set.seed(1)
dat <- expand.grid(time = 1:20, id = paste0("u", 1:12))
dat$y <- rnorm(nrow(dat)) + as.numeric(factor(dat$id))
dat$d <- as.integer(
  (dat$id %in% c("u1", "u2") & dat$time > 10) |
  (dat$id %in% c("u3", "u4") & dat$time > 14)
)
fit <- scm_fit(y ~ d | id + time, data = dat, method = "scm")
scm_inference(fit)


coresynth documentation built on Oct. 11, 2026, 5:07 p.m.