pymc-labs / pymc-labs/CausalPy

Add Staggered Difference-in-Differences-in-Differences (DDD) via subgroup contrasts

Open
#705 0 comments 0 reactions 1 assignee View on GitHub

@drbenvincent is already working on this.

Since Feb 10, 2026.

enhancement
Dominant language
Python
Stars
1.2k
Forks
115
Avg merge
6d 1h
Merged PRs (30d)
11

Description

Summary

This issue proposes adding support for staggered Difference-in-Differences-in-Differences (DDD; triple differences), where treatment adoption occurs at different times across units (multiple cohorts). The request targets an imputation-based staggered DiD workflow (consistent with the current CausalPy staggered implementation) and extends it to DDD by:

  • estimating subgroup-specific staggered effects, then
  • computing contrasts of those effects (difference-of-DiDs) across levels of a third dimension.

DDD differs across three dimensions:

  1. Time (relative to treatment / event time)
  2. Treatment assignment (treated vs not-yet-treated / never-treated)
  3. Third dimension (pre-defined subgroup / eligibility / exposure tier)
DDD is about dimensions, not levels

The third dimension can be binary or multi-level (K>2). With K>2, the output is a set of contrasts among subgroup-specific staggered effects, not a single scalar.


Motivation

Many policy and business interventions are adopted at different times across geographies or units:

  • taxes or regulations adopted by different jurisdictions in different years
  • platform policy rollouts by region
  • retail refurbishments rolled out by wave

In these settings, standard (non-staggered) DDD is not appropriate because a single “pre/post” split does not exist across all treated units. Users need:

  • cohort-robust effect estimation aligned by event time, and
  • the ability to estimate DDD contrasts across a third dimension.

This feature would allow users to answer questions like:

“How much larger was the treatment effect for subgroup A than subgroup B, accounting for staggered adoption?”


Proposed feature

Provide a new experiment-level interface that computes staggered DDD using the existing staggered DiD estimator as the base.

Name suggestion:

  • StaggeredDifferenceInDifferencesInDifferences

Recommended implementation shape:

  • A thin wrapper around StaggeredDifferenceInDifferences that runs it by subgroup level and then computes contrasts.

Why a direct “triple-interaction TWFE” approach is not suitable

A common temptation is to fit a single TWFE regression with a triple interaction (treated × post × subgroup). In staggered settings, that approach can produce non-intuitive estimands and is sensitive to heterogeneous effects and cohort composition.

CausalPy’s staggered implementation is imputation-based; a staggered DDD feature should follow the same approach rather than introducing a separate TWFE-based path.


Motivating example: staggered sugar taxes, geography, and drink brands (by sugar profile)

Policy question

Different jurisdictions (countries/states/cities) introduce a sugar tax at different times. We want to estimate how the tax affects sales.

Third dimension (multi-level brands)

The third dimension is brand (or brand family). Brands can be grouped (pre-defined, time-invariant for the analysis window) by typical sugar profile, for example:

  • Low-sugar brands (e.g., zero/low-sugar formulations)
  • Medium-sugar brands
  • High-sugar brands

This is intentionally a brand/category dimension rather than a “treatment intensity” dimension.

What users want to estimate
  1. Staggered effects by brand group (event-time ATT curves or aggregated ATT):
    • ATT_low(event_time)
    • ATT_medium(event_time)
    • ATT_high(event_time)
  2. DDD contrasts between brand groups (reference-based or pairwise), e.g.:
    • ATT_high − ATT_low (incremental impact on high-sugar vs low-sugar brands)
    • ATT_high − ATT_medium

Interpretation:

  • Shared jurisdiction-level post-adoption shocks (e.g., unrelated economic shifts) affect overall beverage demand and can move all brands.
  • Contrasting brand-group-specific staggered effects differences out those shared shocks and isolates the incremental impact on high-sugar brands relative to lower-sugar brands.

Design notes (Implementation approach)

Proposed implementation: wrapper around StaggeredDifferenceInDifferences

The core estimator should remain StaggeredDifferenceInDifferences (imputation-based). The DDD capability is an orchestration + contrast layer:

  1. Split/stratify the dataset by third_dimension_name (categorical: K levels).
  2. Fit StaggeredDifferenceInDifferences for each level using the same:
    • unit definition
    • time index
    • treatment timing field
    • model/covariates configuration
  3. Extract level-specific effects (overall ATT and/or event-time ATTs).
  4. Compute contrasts across levels (difference of effects) using a user-specified scheme:
    • reference-based: DiD(level=k) − DiD(level=ref) (recommended default)
    • explicit pairwise contrasts: [("tier2","tier0"), ("tier2","tier1"), ...]
Uncertainty propagation

Contrasts must carry valid uncertainty (avoid subtracting point estimates with naïve SE algebra).

  • Bayesian: compute contrasts at the draw level if posterior draws are available.
  • Frequentist: provide bootstrap resampling aligned to the clustering level (typically the unit of treatment assignment).
Output objects

The wrapper should return:

  • per-level staggered effects (overall and event-time)
  • contrast effects (overall and event-time)
  • a tidy results table suitable for downstream analysis
Plotting strategy

Staggered DDD plots should emphasize contrasts and scalability with K levels:

  1. Event-time curves faceted by level: ATT_level(event_time)
  2. Contrast event-time curves: ATT_levelA − ATT_levelB vs event time
  3. Summary dot-and-interval plot for aggregated effects (per level + contrasts)

For K>2 levels, contrast plots (reference-based) should be the default visualization.


Acceptance criteria

  • Provide a staggered DDD interface that:

    • follows the existing imputation-based staggered DiD approach
    • estimates subgroup-specific staggered effects for a categorical third dimension
    • computes user-specified contrasts among subgroup-specific effects
    • propagates uncertainty correctly for contrasts
    • includes DDD-appropriate plotting (event-time by level + event-time contrasts)
  • Documentation:

    • conceptual guide explaining staggered DDD as “subgroup-specific staggered DiD + contrasts”
    • a worked example using staggered sugar taxes and product intensity tiers

Out of scope

  • A single-regression TWFE-based staggered DDD implementation
  • General N-way differencing beyond DDD
  • Continuous-dose DDD without discretization / tiering
  • Automatic cohort selection rules beyond what staggered DiD already supports

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.