diff-diff 2.4.3__tar.gz → 2.6.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {diff_diff-2.4.3 → diff_diff-2.6.0}/PKG-INFO +133 -2
- {diff_diff-2.4.3 → diff_diff-2.6.0}/README.md +132 -1
- {diff_diff-2.4.3 → diff_diff-2.6.0}/diff_diff/__init__.py +19 -1
- diff_diff-2.6.0/diff_diff/bootstrap_utils.py +279 -0
- diff_diff-2.6.0/diff_diff/continuous_did.py +1155 -0
- diff_diff-2.6.0/diff_diff/continuous_did_bspline.py +188 -0
- diff_diff-2.6.0/diff_diff/continuous_did_results.py +353 -0
- {diff_diff-2.4.3 → diff_diff-2.6.0}/diff_diff/prep.py +1 -0
- {diff_diff-2.4.3 → diff_diff-2.6.0}/diff_diff/prep_dgp.py +156 -1
- diff_diff-2.6.0/diff_diff/stacked_did.py +871 -0
- diff_diff-2.6.0/diff_diff/stacked_did_results.py +318 -0
- {diff_diff-2.4.3 → diff_diff-2.6.0}/diff_diff/staggered.py +3 -1
- {diff_diff-2.4.3 → diff_diff-2.6.0}/diff_diff/staggered_bootstrap.py +23 -251
- {diff_diff-2.4.3 → diff_diff-2.6.0}/diff_diff/sun_abraham.py +9 -41
- {diff_diff-2.4.3 → diff_diff-2.6.0}/diff_diff/visualization.py +2 -0
- {diff_diff-2.4.3 → diff_diff-2.6.0}/pyproject.toml +1 -1
- {diff_diff-2.4.3 → diff_diff-2.6.0}/rust/Cargo.lock +12 -12
- {diff_diff-2.4.3 → diff_diff-2.6.0}/rust/Cargo.toml +1 -1
- {diff_diff-2.4.3 → diff_diff-2.6.0}/diff_diff/_backend.py +0 -0
- {diff_diff-2.4.3 → diff_diff-2.6.0}/diff_diff/bacon.py +0 -0
- {diff_diff-2.4.3 → diff_diff-2.6.0}/diff_diff/datasets.py +0 -0
- {diff_diff-2.4.3 → diff_diff-2.6.0}/diff_diff/diagnostics.py +0 -0
- {diff_diff-2.4.3 → diff_diff-2.6.0}/diff_diff/estimators.py +0 -0
- {diff_diff-2.4.3 → diff_diff-2.6.0}/diff_diff/honest_did.py +0 -0
- {diff_diff-2.4.3 → diff_diff-2.6.0}/diff_diff/imputation.py +0 -0
- {diff_diff-2.4.3 → diff_diff-2.6.0}/diff_diff/imputation_bootstrap.py +0 -0
- {diff_diff-2.4.3 → diff_diff-2.6.0}/diff_diff/imputation_results.py +0 -0
- {diff_diff-2.4.3 → diff_diff-2.6.0}/diff_diff/linalg.py +0 -0
- {diff_diff-2.4.3 → diff_diff-2.6.0}/diff_diff/power.py +0 -0
- {diff_diff-2.4.3 → diff_diff-2.6.0}/diff_diff/pretrends.py +0 -0
- {diff_diff-2.4.3 → diff_diff-2.6.0}/diff_diff/results.py +0 -0
- {diff_diff-2.4.3 → diff_diff-2.6.0}/diff_diff/staggered_aggregation.py +0 -0
- {diff_diff-2.4.3 → diff_diff-2.6.0}/diff_diff/staggered_results.py +0 -0
- {diff_diff-2.4.3 → diff_diff-2.6.0}/diff_diff/synthetic_did.py +0 -0
- {diff_diff-2.4.3 → diff_diff-2.6.0}/diff_diff/triple_diff.py +0 -0
- {diff_diff-2.4.3 → diff_diff-2.6.0}/diff_diff/trop.py +0 -0
- {diff_diff-2.4.3 → diff_diff-2.6.0}/diff_diff/trop_results.py +0 -0
- {diff_diff-2.4.3 → diff_diff-2.6.0}/diff_diff/twfe.py +0 -0
- {diff_diff-2.4.3 → diff_diff-2.6.0}/diff_diff/two_stage.py +0 -0
- {diff_diff-2.4.3 → diff_diff-2.6.0}/diff_diff/two_stage_bootstrap.py +0 -0
- {diff_diff-2.4.3 → diff_diff-2.6.0}/diff_diff/two_stage_results.py +0 -0
- {diff_diff-2.4.3 → diff_diff-2.6.0}/diff_diff/utils.py +0 -0
- {diff_diff-2.4.3 → diff_diff-2.6.0}/rust/build.rs +0 -0
- {diff_diff-2.4.3 → diff_diff-2.6.0}/rust/src/bootstrap.rs +0 -0
- {diff_diff-2.4.3 → diff_diff-2.6.0}/rust/src/lib.rs +0 -0
- {diff_diff-2.4.3 → diff_diff-2.6.0}/rust/src/linalg.rs +0 -0
- {diff_diff-2.4.3 → diff_diff-2.6.0}/rust/src/trop.rs +0 -0
- {diff_diff-2.4.3 → diff_diff-2.6.0}/rust/src/weights.rs +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: diff-diff
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.6.0
|
|
4
4
|
Classifier: Development Status :: 5 - Production/Stable
|
|
5
5
|
Classifier: Intended Audience :: Science/Research
|
|
6
6
|
Classifier: Operating System :: OS Independent
|
|
@@ -108,7 +108,7 @@ Signif. codes: '***' 0.001, '**' 0.01, '*' 0.05, '.' 0.1
|
|
|
108
108
|
- **Wild cluster bootstrap**: Valid inference with few clusters (<50) using Rademacher, Webb, or Mammen weights
|
|
109
109
|
- **Panel data support**: Two-way fixed effects estimator for panel designs
|
|
110
110
|
- **Multi-period analysis**: Event-study style DiD with period-specific treatment effects
|
|
111
|
-
- **Staggered adoption**: Callaway-Sant'Anna (2021), Sun-Abraham (2021), Borusyak-Jaravel-Spiess (2024) imputation,
|
|
111
|
+
- **Staggered adoption**: Callaway-Sant'Anna (2021), Sun-Abraham (2021), Borusyak-Jaravel-Spiess (2024) imputation, Two-Stage DiD (Gardner 2022), and Stacked DiD (Wing, Freedman & Hollingsworth 2024) estimators for heterogeneous treatment timing
|
|
112
112
|
- **Triple Difference (DDD)**: Ortiz-Villavicencio & Sant'Anna (2025) estimators with proper covariate handling
|
|
113
113
|
- **Synthetic DiD**: Combined DiD with synthetic control for improved robustness
|
|
114
114
|
- **Triply Robust Panel (TROP)**: Factor-adjusted DiD with synthetic weights (Athey et al. 2025)
|
|
@@ -138,6 +138,9 @@ We provide Jupyter notebook tutorials in `docs/tutorials/`:
|
|
|
138
138
|
| `08_triple_diff.ipynb` | Triple Difference (DDD) estimation with proper covariate handling |
|
|
139
139
|
| `09_real_world_examples.ipynb` | Real-world data examples (Card-Krueger, Castle Doctrine, Divorce Laws) |
|
|
140
140
|
| `10_trop.ipynb` | Triply Robust Panel (TROP) estimation with factor model adjustment |
|
|
141
|
+
| `11_imputation_did.ipynb` | Imputation DiD (Borusyak et al. 2024), pre-trend test, efficiency comparison |
|
|
142
|
+
| `12_two_stage_did.ipynb` | Two-Stage DiD (Gardner 2022), GMM sandwich variance, per-observation effects |
|
|
143
|
+
| `13_stacked_did.ipynb` | Stacked DiD (Wing et al. 2024), Q-weights, sub-experiment inspection, trimming, clean control definitions |
|
|
141
144
|
|
|
142
145
|
## Data Preparation
|
|
143
146
|
|
|
@@ -1012,6 +1015,78 @@ TwoStageDiD(
|
|
|
1012
1015
|
|
|
1013
1016
|
Both estimators are the efficient estimator under homogeneous treatment effects, producing shorter confidence intervals than Callaway-Sant'Anna or Sun-Abraham.
|
|
1014
1017
|
|
|
1018
|
+
### Stacked DiD (Wing, Freedman & Hollingsworth 2024)
|
|
1019
|
+
|
|
1020
|
+
Stacked DiD addresses TWFE bias in staggered adoption settings by constructing a "clean" comparison dataset for each treatment cohort and stacking them together. Each cohort's sub-experiment compares units treated at that cohort's timing against units that are not yet treated (or never treated) within a symmetric event-study window. This avoids the "bad comparisons" problem in TWFE while retaining a regression-based framework that practitioners familiar with event studies will find intuitive.
|
|
1021
|
+
|
|
1022
|
+
```python
|
|
1023
|
+
from diff_diff import StackedDiD, generate_staggered_data
|
|
1024
|
+
|
|
1025
|
+
# Generate sample data
|
|
1026
|
+
data = generate_staggered_data(n_units=200, n_periods=12,
|
|
1027
|
+
cohort_periods=[4, 6, 8], seed=42)
|
|
1028
|
+
|
|
1029
|
+
# Fit stacked DiD with event study
|
|
1030
|
+
est = StackedDiD(kappa_pre=2, kappa_post=2)
|
|
1031
|
+
results = est.fit(data, outcome='outcome', unit='unit',
|
|
1032
|
+
time='period', first_treat='first_treat',
|
|
1033
|
+
aggregate='event_study')
|
|
1034
|
+
results.print_summary()
|
|
1035
|
+
|
|
1036
|
+
# Access stacked data for custom analysis
|
|
1037
|
+
stacked = results.stacked_data
|
|
1038
|
+
|
|
1039
|
+
# Convenience function
|
|
1040
|
+
from diff_diff import stacked_did
|
|
1041
|
+
results = stacked_did(data, 'outcome', 'unit', 'period', 'first_treat',
|
|
1042
|
+
kappa_pre=2, kappa_post=2, aggregate='event_study')
|
|
1043
|
+
```
|
|
1044
|
+
|
|
1045
|
+
**Parameters:**
|
|
1046
|
+
|
|
1047
|
+
```python
|
|
1048
|
+
StackedDiD(
|
|
1049
|
+
kappa_pre=1, # Pre-treatment event-study periods
|
|
1050
|
+
kappa_post=1, # Post-treatment event-study periods
|
|
1051
|
+
weighting='aggregate', # 'aggregate', 'population', or 'sample_share'
|
|
1052
|
+
clean_control='not_yet_treated', # 'not_yet_treated', 'strict', or 'never_treated'
|
|
1053
|
+
cluster='unit', # 'unit' or 'unit_subexp'
|
|
1054
|
+
alpha=0.05, # Significance level
|
|
1055
|
+
anticipation=0, # Anticipation periods
|
|
1056
|
+
rank_deficient_action='warn', # 'warn', 'error', or 'silent'
|
|
1057
|
+
)
|
|
1058
|
+
```
|
|
1059
|
+
|
|
1060
|
+
> **Note:** Group aggregation (`aggregate='group'`) is not supported because the pooled
|
|
1061
|
+
> stacked regression cannot produce cohort-specific effects. Use `CallawaySantAnna` or
|
|
1062
|
+
> `ImputationDiD` for cohort-level estimates.
|
|
1063
|
+
|
|
1064
|
+
**When to use Stacked DiD vs Callaway-Sant'Anna:**
|
|
1065
|
+
|
|
1066
|
+
| Aspect | Stacked DiD | Callaway-Sant'Anna |
|
|
1067
|
+
|--------|-------------|-------------------|
|
|
1068
|
+
| Approach | Stack cohort sub-experiments, run pooled TWFE | 2x2 DiD aggregation |
|
|
1069
|
+
| Symmetric windows | Enforced via kappa_pre / kappa_post | Not required |
|
|
1070
|
+
| Control group | Not-yet-treated (default) or never-treated | Never-treated or not-yet-treated |
|
|
1071
|
+
| Covariates | Passed to pooled regression | Doubly robust / IPW |
|
|
1072
|
+
| Intuition | Familiar event-study regression | Nonparametric aggregation |
|
|
1073
|
+
|
|
1074
|
+
**Convenience function:**
|
|
1075
|
+
|
|
1076
|
+
```python
|
|
1077
|
+
# One-liner estimation
|
|
1078
|
+
results = stacked_did(
|
|
1079
|
+
data,
|
|
1080
|
+
outcome='outcome',
|
|
1081
|
+
unit='unit',
|
|
1082
|
+
time='period',
|
|
1083
|
+
first_treat='first_treat',
|
|
1084
|
+
kappa_pre=3,
|
|
1085
|
+
kappa_post=3,
|
|
1086
|
+
aggregate='event_study'
|
|
1087
|
+
)
|
|
1088
|
+
```
|
|
1089
|
+
|
|
1015
1090
|
### Triple Difference (DDD)
|
|
1016
1091
|
|
|
1017
1092
|
Triple Difference (DDD) is used when treatment requires satisfying two criteria: belonging to a treated **group** AND being in an eligible **partition**. The `TripleDifference` class implements the methodology from Ortiz-Villavicencio & Sant'Anna (2025), which correctly handles covariate adjustment (unlike naive implementations).
|
|
@@ -2241,6 +2316,60 @@ TwoStageDiD(
|
|
|
2241
2316
|
| `print_summary(alpha)` | Print summary to stdout |
|
|
2242
2317
|
| `to_dataframe(level)` | Convert to DataFrame ('observation', 'event_study', 'group') |
|
|
2243
2318
|
|
|
2319
|
+
### StackedDiD
|
|
2320
|
+
|
|
2321
|
+
```python
|
|
2322
|
+
StackedDiD(
|
|
2323
|
+
kappa_pre=1, # Pre-treatment event-study periods
|
|
2324
|
+
kappa_post=1, # Post-treatment event-study periods
|
|
2325
|
+
weighting='aggregate', # 'aggregate', 'population', or 'sample_share'
|
|
2326
|
+
clean_control='not_yet_treated', # 'not_yet_treated', 'strict', or 'never_treated'
|
|
2327
|
+
cluster='unit', # 'unit' or 'unit_subexp'
|
|
2328
|
+
alpha=0.05, # Significance level
|
|
2329
|
+
anticipation=0, # Anticipation periods
|
|
2330
|
+
rank_deficient_action='warn', # 'warn', 'error', or 'silent'
|
|
2331
|
+
)
|
|
2332
|
+
```
|
|
2333
|
+
|
|
2334
|
+
**fit() Parameters:**
|
|
2335
|
+
|
|
2336
|
+
| Parameter | Type | Description |
|
|
2337
|
+
|-----------|------|-------------|
|
|
2338
|
+
| `data` | DataFrame | Panel data |
|
|
2339
|
+
| `outcome` | str | Outcome variable column name |
|
|
2340
|
+
| `unit` | str | Unit identifier column |
|
|
2341
|
+
| `time` | str | Time period column |
|
|
2342
|
+
| `first_treat` | str | First treatment period column (0 for never-treated) |
|
|
2343
|
+
| `population` | str, optional | Population column (required if weighting='population') |
|
|
2344
|
+
| `aggregate` | str | Aggregation: None, `"simple"`, or `"event_study"` |
|
|
2345
|
+
|
|
2346
|
+
### StackedDiDResults
|
|
2347
|
+
|
|
2348
|
+
**Attributes:**
|
|
2349
|
+
|
|
2350
|
+
| Attribute | Description |
|
|
2351
|
+
|-----------|-------------|
|
|
2352
|
+
| `overall_att` | Overall average treatment effect on the treated |
|
|
2353
|
+
| `overall_se` | Standard error |
|
|
2354
|
+
| `overall_t_stat` | T-statistic |
|
|
2355
|
+
| `overall_p_value` | P-value for H0: ATT = 0 |
|
|
2356
|
+
| `overall_conf_int` | Confidence interval |
|
|
2357
|
+
| `event_study_effects` | Dict of relative time -> effect dict (if `aggregate='event_study'`) |
|
|
2358
|
+
| `stacked_data` | The stacked dataset used for estimation |
|
|
2359
|
+
| `n_treated_obs` | Number of treated observations |
|
|
2360
|
+
| `n_untreated_obs` | Number of untreated (clean control) observations |
|
|
2361
|
+
| `n_cohorts` | Number of treatment cohorts |
|
|
2362
|
+
| `kappa_pre` | Pre-treatment window used |
|
|
2363
|
+
| `kappa_post` | Post-treatment window used |
|
|
2364
|
+
|
|
2365
|
+
**Methods:**
|
|
2366
|
+
|
|
2367
|
+
| Method | Description |
|
|
2368
|
+
|--------|-------------|
|
|
2369
|
+
| `summary(alpha)` | Get formatted summary string |
|
|
2370
|
+
| `print_summary(alpha)` | Print summary to stdout |
|
|
2371
|
+
| `to_dataframe(level)` | Convert to DataFrame ('event_study') |
|
|
2372
|
+
|
|
2244
2373
|
### TripleDifference
|
|
2245
2374
|
|
|
2246
2375
|
```python
|
|
@@ -2727,6 +2856,8 @@ The `HonestDiD` module implements sensitivity analysis methods for relaxing the
|
|
|
2727
2856
|
|
|
2728
2857
|
- **Goodman-Bacon, A. (2021).** "Difference-in-Differences with Variation in Treatment Timing." *Journal of Econometrics*, 225(2), 254-277. [https://doi.org/10.1016/j.jeconom.2021.03.014](https://doi.org/10.1016/j.jeconom.2021.03.014)
|
|
2729
2858
|
|
|
2859
|
+
- **Wing, C., Freedman, S. M., & Hollingsworth, A. (2024).** "Stacked Difference-in-Differences." *NBER Working Paper* 32054. [https://www.nber.org/papers/w32054](https://www.nber.org/papers/w32054)
|
|
2860
|
+
|
|
2730
2861
|
### Power Analysis
|
|
2731
2862
|
|
|
2732
2863
|
- **Bloom, H. S. (1995).** "Minimum Detectable Effects: A Simple Way to Report the Statistical Power of Experimental Designs." *Evaluation Review*, 19(5), 547-556. [https://doi.org/10.1177/0193841X9501900504](https://doi.org/10.1177/0193841X9501900504)
|
|
@@ -70,7 +70,7 @@ Signif. codes: '***' 0.001, '**' 0.01, '*' 0.05, '.' 0.1
|
|
|
70
70
|
- **Wild cluster bootstrap**: Valid inference with few clusters (<50) using Rademacher, Webb, or Mammen weights
|
|
71
71
|
- **Panel data support**: Two-way fixed effects estimator for panel designs
|
|
72
72
|
- **Multi-period analysis**: Event-study style DiD with period-specific treatment effects
|
|
73
|
-
- **Staggered adoption**: Callaway-Sant'Anna (2021), Sun-Abraham (2021), Borusyak-Jaravel-Spiess (2024) imputation,
|
|
73
|
+
- **Staggered adoption**: Callaway-Sant'Anna (2021), Sun-Abraham (2021), Borusyak-Jaravel-Spiess (2024) imputation, Two-Stage DiD (Gardner 2022), and Stacked DiD (Wing, Freedman & Hollingsworth 2024) estimators for heterogeneous treatment timing
|
|
74
74
|
- **Triple Difference (DDD)**: Ortiz-Villavicencio & Sant'Anna (2025) estimators with proper covariate handling
|
|
75
75
|
- **Synthetic DiD**: Combined DiD with synthetic control for improved robustness
|
|
76
76
|
- **Triply Robust Panel (TROP)**: Factor-adjusted DiD with synthetic weights (Athey et al. 2025)
|
|
@@ -100,6 +100,9 @@ We provide Jupyter notebook tutorials in `docs/tutorials/`:
|
|
|
100
100
|
| `08_triple_diff.ipynb` | Triple Difference (DDD) estimation with proper covariate handling |
|
|
101
101
|
| `09_real_world_examples.ipynb` | Real-world data examples (Card-Krueger, Castle Doctrine, Divorce Laws) |
|
|
102
102
|
| `10_trop.ipynb` | Triply Robust Panel (TROP) estimation with factor model adjustment |
|
|
103
|
+
| `11_imputation_did.ipynb` | Imputation DiD (Borusyak et al. 2024), pre-trend test, efficiency comparison |
|
|
104
|
+
| `12_two_stage_did.ipynb` | Two-Stage DiD (Gardner 2022), GMM sandwich variance, per-observation effects |
|
|
105
|
+
| `13_stacked_did.ipynb` | Stacked DiD (Wing et al. 2024), Q-weights, sub-experiment inspection, trimming, clean control definitions |
|
|
103
106
|
|
|
104
107
|
## Data Preparation
|
|
105
108
|
|
|
@@ -974,6 +977,78 @@ TwoStageDiD(
|
|
|
974
977
|
|
|
975
978
|
Both estimators are the efficient estimator under homogeneous treatment effects, producing shorter confidence intervals than Callaway-Sant'Anna or Sun-Abraham.
|
|
976
979
|
|
|
980
|
+
### Stacked DiD (Wing, Freedman & Hollingsworth 2024)
|
|
981
|
+
|
|
982
|
+
Stacked DiD addresses TWFE bias in staggered adoption settings by constructing a "clean" comparison dataset for each treatment cohort and stacking them together. Each cohort's sub-experiment compares units treated at that cohort's timing against units that are not yet treated (or never treated) within a symmetric event-study window. This avoids the "bad comparisons" problem in TWFE while retaining a regression-based framework that practitioners familiar with event studies will find intuitive.
|
|
983
|
+
|
|
984
|
+
```python
|
|
985
|
+
from diff_diff import StackedDiD, generate_staggered_data
|
|
986
|
+
|
|
987
|
+
# Generate sample data
|
|
988
|
+
data = generate_staggered_data(n_units=200, n_periods=12,
|
|
989
|
+
cohort_periods=[4, 6, 8], seed=42)
|
|
990
|
+
|
|
991
|
+
# Fit stacked DiD with event study
|
|
992
|
+
est = StackedDiD(kappa_pre=2, kappa_post=2)
|
|
993
|
+
results = est.fit(data, outcome='outcome', unit='unit',
|
|
994
|
+
time='period', first_treat='first_treat',
|
|
995
|
+
aggregate='event_study')
|
|
996
|
+
results.print_summary()
|
|
997
|
+
|
|
998
|
+
# Access stacked data for custom analysis
|
|
999
|
+
stacked = results.stacked_data
|
|
1000
|
+
|
|
1001
|
+
# Convenience function
|
|
1002
|
+
from diff_diff import stacked_did
|
|
1003
|
+
results = stacked_did(data, 'outcome', 'unit', 'period', 'first_treat',
|
|
1004
|
+
kappa_pre=2, kappa_post=2, aggregate='event_study')
|
|
1005
|
+
```
|
|
1006
|
+
|
|
1007
|
+
**Parameters:**
|
|
1008
|
+
|
|
1009
|
+
```python
|
|
1010
|
+
StackedDiD(
|
|
1011
|
+
kappa_pre=1, # Pre-treatment event-study periods
|
|
1012
|
+
kappa_post=1, # Post-treatment event-study periods
|
|
1013
|
+
weighting='aggregate', # 'aggregate', 'population', or 'sample_share'
|
|
1014
|
+
clean_control='not_yet_treated', # 'not_yet_treated', 'strict', or 'never_treated'
|
|
1015
|
+
cluster='unit', # 'unit' or 'unit_subexp'
|
|
1016
|
+
alpha=0.05, # Significance level
|
|
1017
|
+
anticipation=0, # Anticipation periods
|
|
1018
|
+
rank_deficient_action='warn', # 'warn', 'error', or 'silent'
|
|
1019
|
+
)
|
|
1020
|
+
```
|
|
1021
|
+
|
|
1022
|
+
> **Note:** Group aggregation (`aggregate='group'`) is not supported because the pooled
|
|
1023
|
+
> stacked regression cannot produce cohort-specific effects. Use `CallawaySantAnna` or
|
|
1024
|
+
> `ImputationDiD` for cohort-level estimates.
|
|
1025
|
+
|
|
1026
|
+
**When to use Stacked DiD vs Callaway-Sant'Anna:**
|
|
1027
|
+
|
|
1028
|
+
| Aspect | Stacked DiD | Callaway-Sant'Anna |
|
|
1029
|
+
|--------|-------------|-------------------|
|
|
1030
|
+
| Approach | Stack cohort sub-experiments, run pooled TWFE | 2x2 DiD aggregation |
|
|
1031
|
+
| Symmetric windows | Enforced via kappa_pre / kappa_post | Not required |
|
|
1032
|
+
| Control group | Not-yet-treated (default) or never-treated | Never-treated or not-yet-treated |
|
|
1033
|
+
| Covariates | Passed to pooled regression | Doubly robust / IPW |
|
|
1034
|
+
| Intuition | Familiar event-study regression | Nonparametric aggregation |
|
|
1035
|
+
|
|
1036
|
+
**Convenience function:**
|
|
1037
|
+
|
|
1038
|
+
```python
|
|
1039
|
+
# One-liner estimation
|
|
1040
|
+
results = stacked_did(
|
|
1041
|
+
data,
|
|
1042
|
+
outcome='outcome',
|
|
1043
|
+
unit='unit',
|
|
1044
|
+
time='period',
|
|
1045
|
+
first_treat='first_treat',
|
|
1046
|
+
kappa_pre=3,
|
|
1047
|
+
kappa_post=3,
|
|
1048
|
+
aggregate='event_study'
|
|
1049
|
+
)
|
|
1050
|
+
```
|
|
1051
|
+
|
|
977
1052
|
### Triple Difference (DDD)
|
|
978
1053
|
|
|
979
1054
|
Triple Difference (DDD) is used when treatment requires satisfying two criteria: belonging to a treated **group** AND being in an eligible **partition**. The `TripleDifference` class implements the methodology from Ortiz-Villavicencio & Sant'Anna (2025), which correctly handles covariate adjustment (unlike naive implementations).
|
|
@@ -2203,6 +2278,60 @@ TwoStageDiD(
|
|
|
2203
2278
|
| `print_summary(alpha)` | Print summary to stdout |
|
|
2204
2279
|
| `to_dataframe(level)` | Convert to DataFrame ('observation', 'event_study', 'group') |
|
|
2205
2280
|
|
|
2281
|
+
### StackedDiD
|
|
2282
|
+
|
|
2283
|
+
```python
|
|
2284
|
+
StackedDiD(
|
|
2285
|
+
kappa_pre=1, # Pre-treatment event-study periods
|
|
2286
|
+
kappa_post=1, # Post-treatment event-study periods
|
|
2287
|
+
weighting='aggregate', # 'aggregate', 'population', or 'sample_share'
|
|
2288
|
+
clean_control='not_yet_treated', # 'not_yet_treated', 'strict', or 'never_treated'
|
|
2289
|
+
cluster='unit', # 'unit' or 'unit_subexp'
|
|
2290
|
+
alpha=0.05, # Significance level
|
|
2291
|
+
anticipation=0, # Anticipation periods
|
|
2292
|
+
rank_deficient_action='warn', # 'warn', 'error', or 'silent'
|
|
2293
|
+
)
|
|
2294
|
+
```
|
|
2295
|
+
|
|
2296
|
+
**fit() Parameters:**
|
|
2297
|
+
|
|
2298
|
+
| Parameter | Type | Description |
|
|
2299
|
+
|-----------|------|-------------|
|
|
2300
|
+
| `data` | DataFrame | Panel data |
|
|
2301
|
+
| `outcome` | str | Outcome variable column name |
|
|
2302
|
+
| `unit` | str | Unit identifier column |
|
|
2303
|
+
| `time` | str | Time period column |
|
|
2304
|
+
| `first_treat` | str | First treatment period column (0 for never-treated) |
|
|
2305
|
+
| `population` | str, optional | Population column (required if weighting='population') |
|
|
2306
|
+
| `aggregate` | str | Aggregation: None, `"simple"`, or `"event_study"` |
|
|
2307
|
+
|
|
2308
|
+
### StackedDiDResults
|
|
2309
|
+
|
|
2310
|
+
**Attributes:**
|
|
2311
|
+
|
|
2312
|
+
| Attribute | Description |
|
|
2313
|
+
|-----------|-------------|
|
|
2314
|
+
| `overall_att` | Overall average treatment effect on the treated |
|
|
2315
|
+
| `overall_se` | Standard error |
|
|
2316
|
+
| `overall_t_stat` | T-statistic |
|
|
2317
|
+
| `overall_p_value` | P-value for H0: ATT = 0 |
|
|
2318
|
+
| `overall_conf_int` | Confidence interval |
|
|
2319
|
+
| `event_study_effects` | Dict of relative time -> effect dict (if `aggregate='event_study'`) |
|
|
2320
|
+
| `stacked_data` | The stacked dataset used for estimation |
|
|
2321
|
+
| `n_treated_obs` | Number of treated observations |
|
|
2322
|
+
| `n_untreated_obs` | Number of untreated (clean control) observations |
|
|
2323
|
+
| `n_cohorts` | Number of treatment cohorts |
|
|
2324
|
+
| `kappa_pre` | Pre-treatment window used |
|
|
2325
|
+
| `kappa_post` | Post-treatment window used |
|
|
2326
|
+
|
|
2327
|
+
**Methods:**
|
|
2328
|
+
|
|
2329
|
+
| Method | Description |
|
|
2330
|
+
|--------|-------------|
|
|
2331
|
+
| `summary(alpha)` | Get formatted summary string |
|
|
2332
|
+
| `print_summary(alpha)` | Print summary to stdout |
|
|
2333
|
+
| `to_dataframe(level)` | Convert to DataFrame ('event_study') |
|
|
2334
|
+
|
|
2206
2335
|
### TripleDifference
|
|
2207
2336
|
|
|
2208
2337
|
```python
|
|
@@ -2689,6 +2818,8 @@ The `HonestDiD` module implements sensitivity analysis methods for relaxing the
|
|
|
2689
2818
|
|
|
2690
2819
|
- **Goodman-Bacon, A. (2021).** "Difference-in-Differences with Variation in Treatment Timing." *Journal of Econometrics*, 225(2), 254-277. [https://doi.org/10.1016/j.jeconom.2021.03.014](https://doi.org/10.1016/j.jeconom.2021.03.014)
|
|
2691
2820
|
|
|
2821
|
+
- **Wing, C., Freedman, S. M., & Hollingsworth, A. (2024).** "Stacked Difference-in-Differences." *NBER Working Paper* 32054. [https://www.nber.org/papers/w32054](https://www.nber.org/papers/w32054)
|
|
2822
|
+
|
|
2692
2823
|
### Power Analysis
|
|
2693
2824
|
|
|
2694
2825
|
- **Bloom, H. S. (1995).** "Minimum Detectable Effects: A Simple Way to Report the Statistical Power of Experimental Designs." *Evaluation Review*, 19(5), 547-556. [https://doi.org/10.1177/0193841X9501900504](https://doi.org/10.1177/0193841X9501900504)
|
|
@@ -70,6 +70,7 @@ from diff_diff.prep import (
|
|
|
70
70
|
aggregate_to_cohorts,
|
|
71
71
|
balance_panel,
|
|
72
72
|
create_event_time,
|
|
73
|
+
generate_continuous_did_data,
|
|
73
74
|
generate_did_data,
|
|
74
75
|
generate_ddd_data,
|
|
75
76
|
generate_event_study_data,
|
|
@@ -107,6 +108,11 @@ from diff_diff.two_stage import (
|
|
|
107
108
|
TwoStageDiDResults,
|
|
108
109
|
two_stage_did,
|
|
109
110
|
)
|
|
111
|
+
from diff_diff.stacked_did import (
|
|
112
|
+
StackedDiD,
|
|
113
|
+
StackedDiDResults,
|
|
114
|
+
stacked_did,
|
|
115
|
+
)
|
|
110
116
|
from diff_diff.sun_abraham import (
|
|
111
117
|
SABootstrapResults,
|
|
112
118
|
SunAbraham,
|
|
@@ -117,6 +123,11 @@ from diff_diff.triple_diff import (
|
|
|
117
123
|
TripleDifferenceResults,
|
|
118
124
|
triple_difference,
|
|
119
125
|
)
|
|
126
|
+
from diff_diff.continuous_did import (
|
|
127
|
+
ContinuousDiD,
|
|
128
|
+
ContinuousDiDResults,
|
|
129
|
+
DoseResponseCurve,
|
|
130
|
+
)
|
|
120
131
|
from diff_diff.trop import (
|
|
121
132
|
TROP,
|
|
122
133
|
TROPResults,
|
|
@@ -148,7 +159,7 @@ from diff_diff.datasets import (
|
|
|
148
159
|
load_mpdta,
|
|
149
160
|
)
|
|
150
161
|
|
|
151
|
-
__version__ = "2.
|
|
162
|
+
__version__ = "2.6.0"
|
|
152
163
|
__all__ = [
|
|
153
164
|
# Estimators
|
|
154
165
|
"DifferenceInDifferences",
|
|
@@ -156,11 +167,13 @@ __all__ = [
|
|
|
156
167
|
"MultiPeriodDiD",
|
|
157
168
|
"SyntheticDiD",
|
|
158
169
|
"CallawaySantAnna",
|
|
170
|
+
"ContinuousDiD",
|
|
159
171
|
"SunAbraham",
|
|
160
172
|
"ImputationDiD",
|
|
161
173
|
"TwoStageDiD",
|
|
162
174
|
"TripleDifference",
|
|
163
175
|
"TROP",
|
|
176
|
+
"StackedDiD",
|
|
164
177
|
# Bacon Decomposition
|
|
165
178
|
"BaconDecomposition",
|
|
166
179
|
"BaconDecompositionResults",
|
|
@@ -175,6 +188,8 @@ __all__ = [
|
|
|
175
188
|
"CallawaySantAnnaResults",
|
|
176
189
|
"CSBootstrapResults",
|
|
177
190
|
"GroupTimeEffect",
|
|
191
|
+
"ContinuousDiDResults",
|
|
192
|
+
"DoseResponseCurve",
|
|
178
193
|
"SunAbrahamResults",
|
|
179
194
|
"SABootstrapResults",
|
|
180
195
|
"ImputationDiDResults",
|
|
@@ -187,6 +202,8 @@ __all__ = [
|
|
|
187
202
|
"triple_difference",
|
|
188
203
|
"TROPResults",
|
|
189
204
|
"trop",
|
|
205
|
+
"StackedDiDResults",
|
|
206
|
+
"stacked_did",
|
|
190
207
|
# Visualization
|
|
191
208
|
"plot_event_study",
|
|
192
209
|
"plot_group_effects",
|
|
@@ -220,6 +237,7 @@ __all__ = [
|
|
|
220
237
|
"generate_ddd_data",
|
|
221
238
|
"generate_panel_data",
|
|
222
239
|
"generate_event_study_data",
|
|
240
|
+
"generate_continuous_did_data",
|
|
223
241
|
"create_event_time",
|
|
224
242
|
"aggregate_to_cohorts",
|
|
225
243
|
"rank_control_units",
|