diff_diff.StackedDiD

class diff_diff.StackedDiD[source]

Bases: object

Stacked Difference-in-Differences estimator.

Implements Wing, Freedman & Hollingsworth (2024). Builds a stacked dataset of sub-experiments (one per adoption cohort), applies corrective Q-weights to address implicit weighting bias in naive stacked regressions, and runs a weighted event-study regression.

Parameters:
  • kappa_pre (int, default=1) – Number of pre-treatment event-time periods in the event window. The event window spans [-kappa_pre, …, kappa_post].

  • kappa_post (int, default=1) – Number of post-treatment event-time periods.

  • weighting (str, default="aggregate") – Target estimand weighting scheme per Table 1 of the paper: - “aggregate”: Equal weight per adoption event (trimmed aggregate ATT) - “population”: Weight by population size of treated cohort - “sample_share”: Weight by sample share of each sub-experiment

  • clean_control (str, default="not_yet_treated") – How to define clean controls per Appendix A of the paper: - “not_yet_treated”: Units with A_s > a + kappa_post - “strict”: Units with A_s > a + kappa_post + kappa_pre - “never_treated”: Only units with A_s = infinity

  • cluster (str, default="unit") – Clustering level for standard errors: - “unit”: Cluster on original unit identifier - “unit_subexp”: Cluster on (unit, sub_experiment) pairs

  • alpha (float, default=0.05) – Significance level for confidence intervals.

  • anticipation (int, default=0) – Number of anticipation periods. When anticipation > 0: - Reference period shifts from e=-1 to e=-1-anticipation - Post-treatment includes anticipation periods (e >= -anticipation) - Event window expands by anticipation pre-periods Consistent with ImputationDiD, TwoStageDiD, SunAbraham.

  • rank_deficient_action (str, default="warn") – Action when design matrix is rank-deficient: - “warn”: Issue warning and drop linearly dependent columns - “error”: Raise ValueError - “silent”: Drop columns silently

results_

Estimation results after calling fit().

Type:

StackedDiDResults

is_fitted_

Whether the model has been fitted.

Type:

bool

Examples

Basic usage:

>>> from diff_diff import StackedDiD, generate_staggered_data
>>> data = generate_staggered_data(n_units=200, seed=42)
>>> est = StackedDiD(kappa_pre=2, kappa_post=2)
>>> results = est.fit(data, outcome='outcome', unit='unit',
...                   time='period', first_treat='first_treat')
>>> results.print_summary()

With event study:

>>> results = est.fit(data, outcome='outcome', unit='unit',
...                   time='period', first_treat='first_treat',
...                   aggregate='event_study')
>>> from diff_diff import plot_event_study
>>> plot_event_study(results)

Notes

The stacked estimator addresses TWFE bias by: 1. Creating one sub-experiment per adoption cohort with clean controls 2. Applying Q-weights to reweight the stacked regression 3. Running a single event-study WLS regression on the weighted stack

References

Wing, C., Freedman, S. M., & Hollingsworth, A. (2024). Stacked

Difference-in-Differences. NBER Working Paper 32054.

__init__(kappa_pre=1, kappa_post=1, weighting='aggregate', clean_control='not_yet_treated', cluster='unit', alpha=0.05, anticipation=0, rank_deficient_action='warn')[source]
Parameters:
  • kappa_pre (int)

  • kappa_post (int)

  • weighting (str)

  • clean_control (str)

  • cluster (str)

  • alpha (float)

  • anticipation (int)

  • rank_deficient_action (str)

Methods

__init__([kappa_pre, kappa_post, weighting, ...])

fit(data, outcome, unit, time, first_treat)

Fit the stacked DiD estimator.

get_params()

Get estimator parameters (sklearn-compatible).

print_summary()

Print summary to stdout.

set_params(**params)

Set estimator parameters (sklearn-compatible).

summary()

Get summary of estimation results.

__init__(kappa_pre=1, kappa_post=1, weighting='aggregate', clean_control='not_yet_treated', cluster='unit', alpha=0.05, anticipation=0, rank_deficient_action='warn')[source]
Parameters:
  • kappa_pre (int)

  • kappa_post (int)

  • weighting (str)

  • clean_control (str)

  • cluster (str)

  • alpha (float)

  • anticipation (int)

  • rank_deficient_action (str)

fit(data, outcome, unit, time, first_treat, aggregate=None, population=None)[source]

Fit the stacked DiD estimator.

Parameters:
  • data (pd.DataFrame) – Panel data with unit and time identifiers.

  • outcome (str) – Name of outcome variable column.

  • unit (str) – Name of unit identifier column.

  • time (str) – Name of time period column.

  • first_treat (str) – Name of column indicating when unit was first treated. Use 0 or np.inf for never-treated units.

  • aggregate (str, optional) – Aggregation mode: None/”simple” (overall ATT only) or “event_study”. Group aggregation is not supported because the pooled stacked regression cannot produce cohort-specific effects. Use CallawaySantAnna or ImputationDiD for cohort-level estimates.

  • population (str, optional) – Column name for population weights. Required only when weighting=”population”.

Returns:

Object containing all estimation results.

Return type:

StackedDiDResults

Raises:

ValueError – If required columns are missing or data validation fails.

get_params()[source]

Get estimator parameters (sklearn-compatible).

Return type:

Dict[str, Any]

set_params(**params)[source]

Set estimator parameters (sklearn-compatible).

Parameters:

params (Any)

Return type:

StackedDiD

summary()[source]

Get summary of estimation results.

Return type:

str

print_summary()[source]

Print summary to stdout.

Return type:

None