SyntheticControl#

class causalpy.experiments.synthetic_control.SyntheticControl[source]#

The class for the synthetic control experiment.

Parameters:
  • data (NativeDataFrame) – Any eager dataframe Narwhals supports. For a pandas dataframe the index carries the time axis. Dataframes from other libraries have no index, so those callers must pass time_column.

  • treatment_time (int | float | Timestamp) – The time when treatment occurred, in reference to the data index.

  • control_units (list[str]) – A list of control units to be used in the experiment.

  • treated_units (list[str]) – A list of treated units to be used in the experiment.

  • model (PyMCModel | RegressorMixin | None) – A PyMC or sklearn model. Defaults to WeightedSumFitter.

  • min_donor_correlation (float) – Minimum acceptable Pearson correlation between each control unit and treated unit in the pre-treatment period. Control units below this threshold trigger a UserWarning. Defaults to 0.0 (warn on negatively correlated donors).

  • auto_scale_sigma (bool) – If True (default) and the model still carries the weighted-sum fitters’ stock y_hat prior, that sigma ~ HalfNormal(1) default is replaced by sigma ~ Exponential(2/s). The scale is computed per treated unit, with s the standard deviation of that unit’s pre-treatment data, so units on different scales are each calibrated separately. Set to False to keep the original HalfNormal(1) default; the experiment then fits a copy of the model with that prior pinned explicitly, leaving the instance you passed in untouched. A model constructed with an explicit y_hat prior is never rescaled either way.

  • time_column (str | None) – Column holding the time axis. It becomes the index of the data. Required for non-pandas inputs, which carry no index. If None (default), the pandas index of data is used. Passing it for data that already has a meaningful index raises, since only one of the two can be the time axis.

Notes

Lazy lifecycle

Construction only validates input and builds the control/treated design matrices — nothing is sampled. Call fit() to run posterior inference (it returns self, so construction and fitting chain in one expression), and optionally sample_prior_predictive() first for prior predictive checks (plot(group="prior"), effect_summary(group="prior")). Results live on exp.result / exp.prior_result.

Estimate extraction

The model learns control-unit weights from pre-intervention outcomes and applies them to post-intervention controls to construct a synthetic untreated trajectory. Pointwise impact is the observed treated outcome minus this synthetic counterfactual, and cumulative impact is its running sum. Bayesian backends subtract the posterior conditional expectation mu rather than noisy posterior-predictive draws y_hat; OLS subtracts its weighted point prediction.

Examples

>>> import causalpy as cp
>>> df = cp.load_data("sc")
>>> treatment_time = 70
>>> seed = 42
>>> result = cp.SyntheticControl(
...     df,
...     treatment_time,
...     control_units=["a", "b", "c", "d", "e", "f", "g"],
...     treated_units=["actual"],
...     model=cp.pymc_models.WeightedSumFitter(
...         sample_kwargs={
...             "target_accept": 0.95,
...             "random_seed": seed,
...             "progressbar": False,
...         }
...     ),
... ).fit()

Methods

SyntheticControl.build()

Construct the model graph without sampling anything.

SyntheticControl.effect_summary(*[, group, ...])

Generate a decision-ready summary of causal effects for Synthetic Control.

SyntheticControl.fit(**kwargs)

Run the posterior phase and populate result.

SyntheticControl.generate_report(*[, ...])

Generate a self-contained HTML report for this experiment.

SyntheticControl.get_plot_data(*[, group, ...])

Recover the data of the experiment along with the prediction and causal impact information.

SyntheticControl.input_validation(data, ...)

Validate the input data and model formula for correctness.

SyntheticControl.plot(*[, group, round_to, ...])

Plot the synthetic control results for a specific treated unit.

SyntheticControl.print_coefficients([round_to])

Ask the model to print its posterior coefficients.

SyntheticControl.sample_prior_predictive(...)

Run the optional prior phase and populate prior_result.

SyntheticControl.set_maketables_options(*[, ...])

Set optional maketables rendering options for this experiment.

SyntheticControl.summary([round_to])

Print summary of main results and model coefficients.

Attributes

datapost

Data from on or after the treatment time (inclusive).

datapre

Data from before the treatment time (exclusive).

has_prior_predictive

Whether the prior phase has run (draws, and bundle where kept).

idata

Return fitted DataTree when the model backend supports it.

is_built

Whether the model graph / fit design exists (no draws implied).

is_configured

design matrices are ready.

is_fitted

Whether posterior draws and the posterior result bundle exist.

model

The underlying model instance.

prior_result

Prior-group result bundle; raises before prior sampling.

result

Posterior-group result bundle; raises before fit().

supports_bayes

supports_ols

supports_pymc_forecast

labels

data

__init__(data, treatment_time, control_units, treated_units, model=None, min_donor_correlation=0.0, auto_scale_sigma=True, time_column=None)[source]#
Parameters:
Return type:

None

classmethod __new__(*args, **kwargs)#