knitr::opts_chunk$set( collapse = TRUE, comment = "#>", message = FALSE, fig.width = 7, fig.height = 4, dpi = 96 )
coresynth provides six causal-inference estimators for panel data behind a
single formula interface, with the computational core (QP solving, SVD, Kalman
filtering) written in C++ via RcppArmadillo. This vignette walks through the
basics: fitting a model, comparing methods, and pulling results out with
broom and plot().
library(coresynth)
Every estimator is reached through scm_fit() with the same formula syntax:
outcome ~ treatment | unit_id + time_id
The data must be a long-format balanced panel (one row per unit–time), and
treatment is a 0/1 indicator that switches on for treated units in
post-treatment periods. method = selects the estimator.
We simulate a balanced panel of 10 units over 20 periods. Unit u1 is treated
from period 11 onward with a true ATT of 2.0.
set.seed(42) N <- 10; TT <- 20; T_pre <- 10 f <- cumsum(rnorm(TT, 0, 0.5)) # common factor lam <- rnorm(N, 1, 0.3) # unit loadings dat <- expand.grid(time = seq_len(TT), id = paste0("u", seq_len(N))) dat$y <- as.vector(outer(f, lam)) + rnorm(nrow(dat), 0, 0.3) dat$d <- as.integer(dat$id == "u1" & dat$time > T_pre) dat$y[dat$d == 1] <- dat$y[dat$d == 1] + 2.0 # inject the treatment effect head(dat)
fit <- scm_fit(y ~ d | id + time, data = dat, method = "scm") fit
The estimated ATT lives in fit$estimate:
fit$estimate
Because the interface is shared, swapping estimators is a one-word change. Here we run all six on the same data (true ATT = 2.0).
methods <- c("scm", "sdid", "gsc", "mc", "tasc", "si") fits <- lapply(methods, function(m) scm_fit(y ~ d | id + time, data = dat, method = m)) names(fits) <- methods data.frame( method = methods, estimate = round(sapply(fits, `[[`, "estimate"), 3) )
| Method | Full name | Reference |
|----|----|----|
| scm | Synthetic Control Method | Abadie, Diamond & Hainmueller (2010) |
| sdid | Synthetic Difference-in-Differences | Arkhangelsky et al. (2021) |
| gsc | Generalized Synthetic Control | Xu (2017) |
| mc | Matrix Completion | Athey et al. (2021) |
| tasc | Time-Aware Synthetic Control | Rho et al. (2026) |
| si | Synthetic Interventions | Agarwal et al. (2025) |
plot.coresynth() offers three views via type =.
plot(fits$sdid, type = "trend") # observed vs. synthetic
plot(fits$scm, type = "gap") # treatment effect over time
plot(fits$scm, type = "weights") # donor weights
coresynth integrates with broom, so results drop straight into tidy
workflows and paper tables.
library(broom) tidy(fits$scm) # donor weights as a data frame glance(fits$scm) # one-row model summary
For custom plots or diagnostics, the accessor generics return the underlying series from any fit under a uniform interface, whatever the estimation method:
head(treated_outcomes(fits$scm)) # observed treated series head(synthetic_outcomes(fits$scm)) # estimated counterfactual dim(donor_outcomes(fits$scm)) # T x N_co donor outcome matrix
export_json() writes a fit to disk as JSON for reproducibility or downstream
(e.g. AI) workflows:
export_json(fits$scm, file = "scm_result.json")
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.