moderndid.did_multiplegt#

moderndid.did_multiplegt(data, yname, tname, idname, dname, cluster=None, weightsname=None, xformla='~1', effects=1, placebo=0, normalized=False, effects_equal=False, predict_het=None, predict_het_hc2bm=False, switchers='', only_never_switchers=False, same_switchers=False, same_switchers_pl=False, trends_lin=False, trends_nonparam=None, continuous=0, ci_level=95.0, less_conservative_se=False, more_granular_demeaning=False, keep_bidirectional_switchers=False, drop_missing_preswitch=False, boot=False, biters=1000, random_state=None, n_partitions=None)[source]#

Estimate intertemporal treatment effects with non-binary, non-absorbing treatments.

Implements difference-in-differences estimation for settings where treatment may be non-binary, non-absorbing (time-varying), and where lagged treatments may affect the outcome, following [3]. Unlike standard DID which assumes binary absorbing treatment, this estimator handles complex treatment patterns where units can experience treatment increases, decreases, or multiple changes over time.

Let \(F_g\) denote the first period when group \(g\)’s treatment changes, and let \(D_{g,1}\) be its baseline (period-1) treatment. The key parameter of interest is the actual-versus-status-quo (AVSQ) effect

\[\delta_{g,\ell} = \mathbb{E}\left[Y_{g,F_g-1+\ell} - Y_{g,F_g-1+\ell}(D_{g,1}, \ldots, D_{g,1}) \mid \boldsymbol{D}\right]\]

which measures the expected difference between group \(g\)’s actual outcome at \(F_g - 1 + \ell\) and the counterfactual “status quo” outcome it would have obtained if its treatment had remained equal to its period-one value.

The estimator computes

\[\text{DID}_{g,\ell} = Y_{g,F_g-1+\ell} - Y_{g,F_g-1} - \frac{1}{N_{F_g-1+\ell}^g} \sum_{g': D_{g',1}=D_{g,1}, F_{g'}>F_g-1+\ell} \left(Y_{g',F_g-1+\ell} - Y_{g',F_g-1}\right)\]

comparing the outcome evolution of switchers to that of groups with the same baseline treatment that have not yet switched. These are aggregated into event-study effects \(\delta_\ell\), the average effect of having been exposed to a weakly higher treatment dose for \(\ell\) periods.

When normalized=True, the estimator computes \(\delta_\ell^n\), which normalizes by the cumulative treatment change and can be interpreted as a weighted average of the effects of the current treatment and its \(\ell - 1\) first lags on the outcome.

Parameters:
dataDataFrame

Panel data in long format. Accepts any object implementing the Arrow PyCapsule Interface (__arrow_c_stream__), including polars, pandas, pyarrow Table, and cudf DataFrames.

ynamestr

Name of the outcome variable.

tnamestr

Name of the time period variable.

idnamestr

Name of the unit identifier variable.

dnamestr

Name of the treatment variable. Can be binary or continuous, and can vary over time for the same unit (non-absorbing). Must be non-negative.

clusterstr, optional

Name of the cluster variable for clustered standard errors. If None, standard errors are computed using the influence function at the unit level.

weightsnamestr, optional

Name of the sampling weights column. If None, all observations have equal weight.

xformlastr, default=”~1”

A formula for the covariates to include in the model. Should be of the form “~ X1 + X2” (intercept is always included). Use “~1” for no covariates.

effectsint, default=1

Number of post-treatment horizons to estimate (1, 2, …, effects). \(\delta_\ell\) estimates the effect of \(\ell\) periods of exposure to changed treatment.

placeboint, default=0

Number of pre-treatment horizons to estimate for placebo tests (-1, -2, …, -placebo). These compare outcome trends of switchers and non-switchers before switching occurs, testing the parallel trends assumption.

normalizedbool, default=False

If True, compute normalized effects \(\delta_\ell^n\) by dividing by the average cumulative treatment change. The normalized effect is a weighted average of the effects of the current treatment and its lags, useful when treatment magnitudes vary across units.

effects_equalbool or str or tuple, default=False

Test whether treatment effects are equal across horizons.

  • True or "all": test all effects

  • "lb, ub" string: test effects in the range [lb, ub]

  • (lb, ub) tuple: test effects in the range [lb, ub]

Returns a chi-squared test statistic and p-value.

predict_hettuple[list[str], list[int]], optional

Analyze heterogeneous effects by covariates. A tuple of (covariates, horizons) where covariates is a list of time-invariant covariate names and horizons is a list of effect horizons to analyze (use [-1] for all horizons). Runs WLS regressions to test whether effects vary by covariates.

predict_het_hc2bmbool, default=False

If True, use HC2 Bell-McCaffrey degrees-of-freedom adjusted standard errors for the predict_het regressions, which are more robust in small samples. Requires predict_het to be specified. Clusters on cluster if set, otherwise idname.

switchers{“”, “in”, “out”}, default=””

Which switchers to include in estimation:

  • "": All switchers (treatment increases and decreases)

  • "in": Only treatment increases (\(D_{g,F_g} > D_{g,1}\))

  • "out": Only treatment decreases (\(D_{g,F_g} < D_{g,1}\))

only_never_switchersbool, default=False

If True, use only never-switchers as controls. If False (default), also use not-yet-switchers as controls.

same_switchersbool, default=False

If True, use the same set of switchers across all effect horizons. This ensures comparability across horizons but may reduce sample size.

same_switchers_plbool, default=False

If True, use the same set of switchers across all placebo horizons.

trends_linbool, default=False

If True, include unit-specific linear time trends in the estimation.

trends_nonparamlist[str], optional

Variables for non-parametric group-specific trends.

continuousint, default=0

Polynomial degree for continuous treatment. If > 0, treatment is modeled as continuous with polynomial terms of the specified degree.

ci_levelfloat, default=95.0

Confidence level for confidence intervals (e.g., 95.0 for 95% CI).

less_conservative_sebool, default=False

If True, use less conservative standard error estimation with degrees-of-freedom adjustment based on the number of clusters or switchers.

more_granular_demeaningbool, default=False

If True, enable path-based variance demeaning. This is a semantic alias that automatically sets less_conservative_se=True.

keep_bidirectional_switchersbool, default=False

If True, keep units that experience both treatment increases AND decreases over time. By default, these units are dropped because their \(\delta_{g,\ell}\) may not satisfy the no-sign-reversal property.

drop_missing_preswitchbool, default=False

If True, drop observations where treatment is missing before the first switch time.

bootbool, default=False

If True, compute standard errors using the multiplier bootstrap instead of asymptotic influence function-based inference. The bootstrap resamples at the cluster level when cluster is specified.

bitersint, default=1000

Number of bootstrap iterations when boot=True.

random_stateint, Generator, optional

Random seed for reproducibility of bootstrap.

n_partitionsint, optional

Number of partitions for distributed computation when data is a Dask or Spark DataFrame. If None, defaults to the framework’s default parallelism.

Returns:
DIDInterResult

Result object containing:

  • effects: EffectsResult with treatment effects at each horizon, including point estimates, standard errors, confidence intervals, and sample sizes

  • placebos: PlacebosResult with placebo effects (if placebo > 0)

  • ate: ATEResult with the average total effect \(\delta\), which can be used for cost-benefit analysis

  • n_units: Total number of units in the sample

  • n_switchers: Number of switching units

  • n_never_switchers: Number of never-switching units

  • ci_level: Confidence level used for intervals

  • effects_equal_test: Chi-squared test for equal effects (if requested)

  • placebo_joint_test: Joint test that all placebo effects are zero

  • influence_effects: Influence functions for effects (for custom inference)

  • influence_placebos: Influence functions for placebos

  • heterogeneity: Heterogeneous effects analysis (if predict_het specified)

  • estimation_params: Dictionary of estimation parameters used

See also

att_gt

Group-time ATT for binary, staggered adoption designs.

cont_did

Continuous treatment DID with dose-response estimation.

Notes

Identification relies on a parallel trends assumption for groups with the same baseline treatment. If two groups have the same period-one treatment, they have the same expected evolution of their status-quo outcome. This is weaker than standard parallel trends across all groups, which would rule out both dynamic treatment effects and time-varying effects.

With binary staggered treatment and uniform baseline, this is equivalent to the att_gt event-study estimator. With varying baseline treatments, the estimators differ because this method compares switchers only to non-switchers with the same baseline, preserving validity under a conditional parallel trends assumption that allows for lagged and time-varying effects.

By default, units that experience both treatment increases and decreases are dropped (keep_bidirectional_switchers=False) because their \(\delta_{g,\ell}\) can be written as a linear combination with negative weights of effects of different treatment lags, potentially violating the no-sign-reversal property.

The ATE parameter \(\delta\) measures the average total effect per unit of treatment, where total effect includes both contemporaneous and lagged effects. It can be compared to the average treatment cost to assess whether treatment changes were beneficial.

References

[1]

Bell, R., & McCaffrey, D. (2002). Bias Reduction in Standard Errors for Linear Regression with Multi-Stage Samples. Survey Methodology, 28(2), 169-181.

[2]

Callaway, B., & Sant’Anna, P. H. (2021). Difference-in-Differences with Multiple Time Periods. Journal of Econometrics, 225(2), 200-230. https://doi.org/10.1016/j.jeconom.2020.12.001

[3]

de Chaisemartin, C., & D’Haultfoeuille, X. (2024). Difference-in- Differences Estimators of Intertemporal Treatment Effects. Review of Economics and Statistics, 106(6), 1723-1736. https://doi.org/10.1162/rest_a_01414

Examples

Estimate intertemporal treatment effects using the Favara and Imbs (2015) banking deregulation data, where treatment (interstate branching) is non-binary and potentially non-absorbing.

In [1]: import moderndid as md
   ...: df = md.load_favara_imbs()
   ...: df.head()
   ...: 
Out[1]: 
shape: (5, 7)
┌──────┬────────┬─────────┬─────────────┬───────────┬──────────┬──────────┐
│ year ┆ county ┆ state_n ┆ Dl_vloans_b ┆ inter_bra ┆ w1       ┆ Dl_hpi   │
│ ---  ┆ ---    ┆ ---     ┆ ---         ┆ ---       ┆ ---      ┆ ---      │
│ i64  ┆ i64    ┆ i64     ┆ f64         ┆ i64       ┆ f64      ┆ f64      │
╞══════╪════════╪═════════╪═════════════╪═══════════╪══════════╪══════════╡
│ 1994 ┆ 1001   ┆ 1       ┆ 0.270248    ┆ 0         ┆ 0.975312 ┆ 0.003176 │
│ 1995 ┆ 1001   ┆ 1       ┆ -0.038427   ┆ 0         ┆ 0.975312 ┆ 0.048912 │
│ 1996 ┆ 1001   ┆ 1       ┆ 0.161633    ┆ 0         ┆ 0.975312 ┆ 0.058203 │
│ 1997 ┆ 1001   ┆ 1       ┆ 0.056523    ┆ 0         ┆ 0.975312 ┆ 0.044366 │
│ 1998 ┆ 1001   ┆ 1       ┆ 0.034236    ┆ 1         ┆ 0.975312 ┆ 0.047092 │
└──────┴────────┴─────────┴─────────────┴───────────┴──────────┴──────────┘

Estimate effects at multiple horizons with placebo tests.

In [2]: result = md.did_multiplegt(
   ...:     data=df,
   ...:     yname="Dl_vloans_b",
   ...:     idname="county",
   ...:     tname="year",
   ...:     dname="inter_bra",
   ...:     effects=8,
   ...:     placebo=3,
   ...:     cluster="state_n",
   ...:     normalized=True,
   ...:     same_switchers=True,
   ...:     effects_equal=True,
   ...: )
   ...: result
   ...: 
Out[2]: 
===============================================================================
 Intertemporal Treatment Effects
===============================================================================

 Average Total Effect:

┌────────┬────────────┬────────────────────────┬───────┬───────────┐
│    ATE │ Std. Error │ [95% Conf. Interval]   │     N │ Switchers │
├────────┼────────────┼────────────────────────┼───────┼───────────┤
│ 0.0551 │     0.0165 │ [  0.0226,   0.0875] * │ 14745 │      6184 │
└────────┴────────────┴────────────────────────┴───────┴───────────┘


 Treatment Effects by Horizon:

┌─────────┬──────────┬────────────┬────────────────────────┬──────┬───────────┐
│ Horizon │ Estimate │ Std. Error │ [95% Conf. Interval]   │    N │ Switchers │
├─────────┼──────────┼────────────┼────────────────────────┼──────┼───────────┤
│       1 │   0.0240 │     0.0183 │ [ -0.0119,   0.0600]   │ 3368 │       773 │
│       2 │   0.0117 │     0.0106 │ [ -0.0092,   0.0325]   │ 2604 │       773 │
│       3 │   0.0143 │     0.0083 │ [ -0.0019,   0.0306]   │ 2031 │       773 │
│       4 │   0.0115 │     0.0082 │ [ -0.0045,   0.0275]   │ 1541 │       773 │
│       5 │   0.0162 │     0.0068 │ [  0.0028,   0.0295] * │ 1412 │       773 │
│       6 │   0.0134 │     0.0062 │ [  0.0012,   0.0256] * │ 1293 │       773 │
│       7 │   0.0098 │     0.0049 │ [  0.0002,   0.0195] * │ 1248 │       773 │
│       8 │   0.0101 │     0.0043 │ [  0.0017,   0.0185] * │ 1248 │       773 │
└─────────┴──────────┴────────────┴────────────────────────┴──────┴───────────┘


 Placebo Effects (Pre-treatment):

┌─────────┬──────────┬────────────┬────────────────────────┬──────┬───────────┐
│ Horizon │ Estimate │ Std. Error │ [95% Conf. Interval]   │    N │ Switchers │
├─────────┼──────────┼────────────┼────────────────────────┼──────┼───────────┤
│      -1 │   0.0304 │     0.0251 │ [ -0.0188,   0.0796]   │ 2335 │       764 │
│      -2 │  -0.0218 │     0.0230 │ [ -0.0669,   0.0234]   │  957 │       497 │
│      -3 │  -0.0137 │     0.0255 │ [ -0.0636,   0.0362]   │  511 │       358 │
└─────────┴──────────┴────────────┴────────────────────────┴──────┴───────────┘

 Joint test (placebos = 0): p-value = 0.2676

 Test of equal effects: p-value = 0.0760

-------------------------------------------------------------------------------
 Signif. codes: '*' confidence interval does not cover 0

-------------------------------------------------------------------------------
 Data Info
-------------------------------------------------------------------------------
 Number of units: 1045
 Switchers: 916
 Never-switchers: 129

-------------------------------------------------------------------------------
 Estimation Details
-------------------------------------------------------------------------------
 Effects estimated: 8
 Placebos estimated: 3
 Normalized: Yes
 Same switchers across horizons: Yes

-------------------------------------------------------------------------------
 Inference
-------------------------------------------------------------------------------
 Confidence level: 95%
 Clustered standard errors: state_n
===============================================================================
 See de Chaisemartin and D'Haultfoeuille (2024) for details.