SyntheticDifferenceInDifferences#

class causalpy.experiments.synthetic_difference_in_differences.SyntheticDifferenceInDifferences[source]#

Bayesian Synthetic Difference-in-Differences experiment.

Combines the synthetic control method’s unit weighting with difference-in-differences time weighting. The treatment effect (tau) is computed analytically from the posterior weight distributions via the double-difference formula, rather than being estimated inside the MCMC model (cut-posterior formulation).

Parameters:
  • data (NativeDataFrame) – Any eager dataframe Narwhals supports, in wide format (columns = units, rows = time periods). 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, should be in reference to the data index.

  • control_units (list[str]) – A list of control unit column names.

  • treated_units (list[str]) – A list of treated unit column names.

  • model (PyMCModel | RegressorMixin | None) – A SyntheticDifferenceInDifferencesWeightFitter instance. Defaults to SyntheticDifferenceInDifferencesWeightFitter.

  • 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 inputs and prepares the design matrices. Call fit() to build the weight-model graph and sample the posterior (populating result), optionally preceded by sample_prior_predictive() for prior predictive checks. Read methods (summary(), plot(), effect_summary()) raise until the matching phase has been sampled.

Estimate extraction

The Bayesian weight model produces posterior draws of synthetic-control unit weights and pre-period time weights. For each draw, the class constructs treated-minus-synthetic gaps and evaluates the weighted double-difference analytically to obtain the scalar tau_posterior ATT; the effect is not read from a regression coefficient or obtained by population-standardized g-computation. The time-indexed post_impact consumed by effect_summary() is the post-period treated-minus-synthetic trajectory rather than this time-weighted scalar.

This implements Bayesian SDiD method. The model fits two weight modules via MCMC:

  • Unit weights (omega): balance control units against treated units in the pre-treatment period, similar to synthetic control.

  • Time weights (lambda): balance pre-treatment periods against post-treatment periods for control units.

The treatment effect is then computed analytically via the double-difference:

\[\tau = \bar{\Delta}_{\text{post}} - \boldsymbol{\lambda}^\top \boldsymbol{\Delta}_{\text{pre}}\]

where \(\Delta_t = y_{\text{tr},t} - (\omega_0 + \boldsymbol{\omega}^\top \mathbf{Y}_{\text{co},t})\) is the gap between the observed treated outcome and the synthetic control at time t.

References

Examples

>>> import causalpy as cp
>>> df = cp.load_data("sc")
>>> treatment_time = 70
>>> result = cp.SyntheticDifferenceInDifferences(
...     df,
...     treatment_time,
...     control_units=["a", "b", "c", "d", "e", "f", "g"],
...     treated_units=["actual"],
...     model=cp.pymc_models.SyntheticDifferenceInDifferencesWeightFitter(
...         sample_kwargs={
...             "tune": 20,
...             "draws": 20,
...             "chains": 2,
...             "cores": 2,
...             "progressbar": False,
...         }
...     ),
... ).fit()

Methods

SyntheticDifferenceInDifferences.build()

Construct the model graph without sampling anything.

SyntheticDifferenceInDifferences.effect_summary(*)

Generate a decision-ready summary of causal effects for SDiD.

SyntheticDifferenceInDifferences.fit(**kwargs)

Run the posterior phase and populate result.

SyntheticDifferenceInDifferences.generate_report(*)

Generate a self-contained HTML report for this experiment.

SyntheticDifferenceInDifferences.input_validation(...)

Validate the input data for correctness.

SyntheticDifferenceInDifferences.plot(*[, ...])

Plot SDiD results: counterfactual, period impact, and cumulative impact.

SyntheticDifferenceInDifferences.print_coefficients([...])

Ask the model to print its posterior coefficients.

SyntheticDifferenceInDifferences.sample_prior_predictive(...)

Run the optional prior phase and populate prior_result.

SyntheticDifferenceInDifferences.set_maketables_options(*)

Set optional maketables rendering options for this experiment.

SyntheticDifferenceInDifferences.summary([...])

Print summary of main results.

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, time_column=None)[source]#
Parameters:
Return type:

None

classmethod __new__(*args, **kwargs)#