In 1988 California passed Proposition 99, a large tobacco tax and control program. The classic synthetic-control analysis (Abadie, Diamond & Hainmueller 2010) builds a “synthetic California” from other states and reads the program’s effect off the gap. But cigarette taxes leak: Californians can buy cigarettes in Nevada. If the donors absorb part of the treatment, the synthetic control is contaminated and the classical estimate is biased.
scspill models that leakage explicitly. This page walks the full workflow on the bundled panel: load the data, fit the two-step Bayesian model, read the treatment effect, and — the part classical SCM cannot do — read the spillover received by each donor state.
NoteMCMC budget in this tutorial
The pages in this documentation run a tutorial-scale chain (m_iter=4000, burn=2000, seconds on a laptop) so they render quickly. The paper’s production run for California used 100,000 iterations; for serious work use at least tens of thousands and check diagnostics().
Load the data
load_california() returns the panel plus the spatial weights, ready to splat into the estimator’s configuration:
from scspill.data import load_californiapanel = load_california()panel.df.head()
state
state_id
year
cigsale
retprice
treated
0
Alabama
1
1970
89.800003
39.599998
0
1
Alabama
1
1971
95.400002
42.700001
0
2
Alabama
1
1972
101.099998
42.299999
0
3
Alabama
1
1973
102.900002
42.099998
0
4
Alabama
1
1974
108.199997
43.099998
0
Three ingredients matter:
# 1) A 0/1 treatment column: California from 1988 onward.panel.df.query("treated == 1").head(3)
state
state_id
year
cigsale
retprice
treated
80
California
3
1988
90.099998
117.400002
1
81
California
3
1989
82.400002
126.400002
1
82
California
3
1990
77.800003
163.800003
1
# 2) spatial_w: how exposed is each donor to the *treated* unit?# Rook contiguity: Nevada is the only state in the ADH donor pool that# borders California (Oregon and Arizona are excluded from the pool).panel.spatial_w[panel.spatial_w >0]
state
Nevada 1.0
Name: adj, dtype: float64
# 3) spatial_W: donor-to-donor contiguity (row-normalized inside the estimator).panel.spatial_W.iloc[:5, :5]
Under the hood this ran the paper’s two-step sampler: a horseshoe Gibbs sampler for the synthetic weights \(\alpha\) on the 1970–1987 fit, then a spatial-autoregressive block that estimates the spillover intensity \(\rho\) (with the retail cigarette price as a covariate and one latent factor), and finally the identification formulas that turn the posterior draws into effects.
findfont: Failed to find font weight medium, now using 400.
findfont: Failed to find font weight medium, now using 400.
findfont: Failed to find font weight medium, now using 400.
Observed California vs. its no-treatment counterfactual, with the 95% pointwise credible band.
result.plot(kind="effect", display=False);
The per-year treatment effect with its credible band.
The spillovers
The identification result recovers the effect received by every donor. The spillover panel is a time-by-donor DataFrame; the plot ranks donors by their post-treatment spillover magnitude:
scspill’s weights are unconstrained and horseshoe-shrunk; the classical simplex weights are computed alongside for comparison:
result.plot(kind="weights", display=False);
findfont: Failed to find font weight medium, now using 400.
findfont: Failed to find font weight medium, now using 400.
Posterior-mean synthetic weights (bars) against classical simplex-SCM weights (dots).
Convergence diagnostics
result.diagnostics(top_n_alpha=4).round(3)
mean
sd
q025
q50
q975
ess
rhat_split
mcse
geweke_z
parameter
rho
0.300
0.057
0.221
0.299
0.385
2.774
3.049
0.034
-9.861
sigma2
61.423
3.420
54.984
61.330
68.544
1685.262
1.000
0.083
0.268
alpha[Connecticut]
0.274
0.158
-0.016
0.295
0.556
102.944
1.017
0.016
1.504
alpha[Tennessee]
-0.208
0.156
-0.514
-0.215
0.015
71.322
1.000
0.018
1.117
alpha[Nevada]
0.202
0.038
0.122
0.202
0.280
238.631
1.014
0.002
-2.217
alpha[Montana]
0.167
0.137
-0.027
0.164
0.431
88.815
1.001
0.015
-0.702
beta[retprice]
0.351
0.028
0.302
0.350
0.405
5.966
1.399
0.011
3.933
\(\rho\) is the weakly identified parameter of this model — watch its ESS and run long chains for publication numbers. The validation article shows the full toolkit (Geweke test, prior sensitivity, prior predictive checks).
Where to go next
The Sudan case study: trade-network weights instead of contiguity, and spillovers measured in percent of GDP.
The sar model page: the identification result, the samplers, and exactly where this implementation deliberately departs from the R replication package.