MSC.jl
MSC.jl implements multivariate synthetic control for high-dimensional disaggregated panels. It is designed for settings with many treated units and many controls where fitting one synthetic-control model per treated unit is expensive.
The package includes:
fit_mscandpredict_counterfactualfor matrix-level MSC estimation.msc_estimatefor panel-matrix and long-table ATT estimation.panel_matricesfor balanced common-adoption panel conversion.- Cross-validated regularization over the MSC penalty path.
- Treated-unit counterfactuals, effect curves, unit effects, and weight tables.
- Time-placebo and control-unit placebo routines.
- A lightweight
RecipesBaseplotting recipe. - Synthetic data generators and a paper-application replication scaffold.
Where To Start
- Use Getting Started for the matrix API.
- Use DataFrame Panels for long panel data.
- Use Paper Application for the county-unemployment application from Shen, Song, and Abadie.
- Use Examples for runnable scripts.
The paper application script uses the local BLS data file and prints the replication comparison directly:
control counties MSC.jl 438 paper 438
treated counties MSC.jl 2674 paper 2,674
pre-treatment months MSC.jl 147 paper 147
April 2020 ATT, pp MSC.jl 4.9552 paper 5.06The estimator fits
Y_pre = X_pre * Theta + Ewith the sparse multivariate square-root Lasso objective
min_Theta (1 / sqrt(T0)) * ||Y_pre - X_pre * Theta||_* + lambda * ||Theta||_1where X_pre is T0 x n_controls, Y_pre is T0 x n_treated, and Theta maps control outcomes to treated-unit counterfactuals.
Installation
using Pkg
Pkg.add(url = "https://github.com/xiangao/MSC.jl")For local development:
using Pkg
Pkg.develop(path = "/home/xao/projects/software/MSC.jl")Quick Start
using MSC
using Statistics
sim = simulate_msc(ncontrols = 20, ntreated = 5, T0 = 50, T1 = 6, tau = 1.2, seed = 1)
est = msc_estimate(
sim.Y,
sim.N0,
sim.T0;
nlambda = 8,
nfolds = 4,
max_iter = 800,
tol = 1e-6,
)
round(est.estimate, digits = 3)1.179The fitted object stores treated-unit counterfactuals and diagnostics:
(
effects = round.(unit_effects(est), digits = 3),
prefit = round(prefit_rmse(est.fit, sim.Y[1:sim.N0, 1:sim.T0]', sim.Y[(sim.N0 + 1):end, 1:sim.T0]'), digits = 3),
nonzero_weights = size(weights_table(est; atol = 1e-8), 1),
)(effects = [1.184, 1.183, 1.165, 1.195, 1.167], prefit = 0.047, nonzero_weights = 22)See the vignettes and examples pages for complete workflows.