Causal Inference VII — Synthetic Control (R companion)¶

tidysynth — Abadie's predictor-based synthetic control for Proposition 99¶

R is where synthetic control was born (Abadie's own Synth package) and where the modern tidy interface lives. This companion uses tidysynth, whose pipeline mirrors Abadie, Diamond & Hainmueller (2010) exactly: match California to a donor pool on economic predictors (price, income, youth share, beer) and pre-treatment cigarette-sales lags, solve for the optimally-weighted donor mix, and run the full placebo distribution for inference.

The key contrast with the from-scratch Python notebook is deliberate: Python matched on the pre-treatment outcome path only and California ranked 3rd of 39 in the placebo test ($p\approx0.08$); here, Abadie's predictor-based matching sharpens the placebo distribution and pushes California to rank 1 of 39 with Fisher's exact $p\approx0.026$ — while the effect size (~26 fewer packs by 2000) is the same. Specification matters for inference, not for the headline effect.

1. Building the synthetic control — the tidysynth pipeline¶

The pipeline reads like the method itself: declare the treated unit and time, add predictors (averaged over chosen pre-treatment windows), generate the optimal weights over the donor pool, and generate the synthetic control. The resulting donor weights reproduce Abadie's published result almost exactly — a sparse blend of Utah, Nevada, Montana, Colorado, and Connecticut.

In [1]:
suppressMessages({library(tidysynth); library(dplyr)})
sm<-read.csv("smoking.csv")
out<-sm %>%
  synthetic_control(outcome=cigsale, unit=state, time=year, i_unit="California", i_time=1988, generate_placebos=TRUE) %>%
  generate_predictor(time_window=1980:1988, ln_income=mean(lnincome,na.rm=TRUE), ret_price=mean(retprice,na.rm=TRUE), youth=mean(age15to24,na.rm=TRUE)) %>%
  generate_predictor(time_window=1984:1988, beer_sales=mean(beer,na.rm=TRUE)) %>%
  generate_predictor(time_window=1975, cigsale_1975=cigsale) %>%
  generate_predictor(time_window=1980, cigsale_1980=cigsale) %>%
  generate_predictor(time_window=1988, cigsale_1988=cigsale) %>%
  generate_weights(optimization_window=1970:1988) %>%
  generate_control()
w<-out %>% grab_unit_weights() %>% arrange(desc(weight)) %>% filter(weight>0.01)
cat("Donor weights forming synthetic California (predictor-based):\n"); print(as.data.frame(w), row.names=FALSE)
Warning message:
"package 'tidysynth' was built under R version 4.6.1"
Donor weights forming synthetic California (predictor-based):
        unit     weight
        Utah 0.34322360
      Nevada 0.23575162
     Montana 0.18197056
    Colorado 0.17468464
 Connecticut 0.06241991

2. Real vs synthetic California — plot_trends and plot_weights¶

plot_trends overlays real and synthetic California: a tight match before 1988, then a widening gap as Proposition 99 takes effect. plot_weights shows the sparse donor composition. The post-1988 divergence — real California falling below its synthetic counterpart — is the estimated effect, reaching roughly 26 fewer packs per capita by 2000.

In [2]:
options(repr.plot.width=9, repr.plot.height=5)
print(out %>% plot_trends() + ggplot2::ggtitle("tidysynth: California vs synthetic California — Proposition 99"))
options(repr.plot.width=8, repr.plot.height=4)
print(out %>% plot_weights() + ggplot2::ggtitle("Donor and predictor weights"))
Warning message:
"Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
ℹ Please use `linewidth` instead.
ℹ The deprecated feature was likely used in the tidysynth package.
  Please report the issue to the authors."
No description has been provided for this image
No description has been provided for this image

3. Placebo inference — plot_placebos, plot_mspe_ratio, grab_significance¶

Synthetic control's inference is the placebo permutation test: re-run the analysis treating each donor as if it had adopted the policy, and compare California's gap to the placebo distribution. plot_placebos overlays all the gaps; plot_mspe_ratio ranks states by the post/pre MSPE ratio; and grab_significance returns California's rank and Fisher's exact $p$-value. With predictor-based matching, California ranks 1st of 39 ($p\approx0.026$) — the sharp inference Abadie reports, and a visible improvement over the pure-outcome Python specification.

In [3]:
options(repr.plot.width=9, repr.plot.height=4.6)
print(out %>% plot_placebos() + ggplot2::ggtitle("Placebo gaps: California (black) vs donor placebos"))
sig<-out %>% grab_significance() %>% filter(unit_name=="California") %>% select(unit_name, mspe_ratio, rank, fishers_exact_pvalue)
cat("California placebo inference:\n"); print(as.data.frame(sig), row.names=FALSE)
options(repr.plot.width=8, repr.plot.height=4.6)
print(out %>% plot_mspe_ratio() + ggplot2::ggtitle("Post/pre MSPE ratio — California tops the ranking"))
California placebo inference:
  unit_name mspe_ratio rank fishers_exact_pvalue
 California   120.4927    1           0.02564103
Ignoring unknown labels:
• colour : ""
No description has been provided for this image
No description has been provided for this image

4. Summary¶

tidysynth reproduced Abadie's canonical Proposition 99 analysis: a synthetic California built from Utah, Nevada, Montana, Colorado, and Connecticut tracked the real state through 1988 and diverged after, estimating that the program cut per-capita cigarette sales by a growing margin (~26 packs by 2000), with a placebo test placing California 1st of 39 ($p\approx0.026$). Compared to the from-scratch Python notebook, the predictor-based matching (price, income, youth, beer, plus outcome lags) sharpened the placebo distribution while leaving the effect size unchanged — the value R's mature Synth/tidysynth implementation adds.

The workflow to remember: declare treated unit and time → add predictors → optimize weights → generate control → validate with placebos and the MSPE-ratio permutation test. Synthetic control is difference-in-differences with data-driven weights (the previous notebook) and its placebo inference is Fisher's randomization test (the first notebook), applied to a single-unit comparative case study. Next: DAGs, Mediation & the Structural Causal Model — the graphical language of identification that unifies the whole arc.