diff-diff 3.6.0__tar.gz → 3.6.1__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-3.6.0 → diff_diff-3.6.1}/PKG-INFO +4 -4
- {diff_diff-3.6.0 → diff_diff-3.6.1}/README.md +3 -3
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/__init__.py +1 -1
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/business_report.py +13 -4
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/chaisemartin_dhaultfoeuille.py +13 -6
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/chaisemartin_dhaultfoeuille_results.py +5 -3
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/estimators.py +31 -46
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/guides/llms-autonomous.txt +15 -6
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/guides/llms-full.txt +9 -5
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/guides/llms-practitioner.txt +6 -3
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/guides/llms.txt +3 -3
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/lpdid.py +461 -59
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/lpdid_results.py +43 -2
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/staggered.py +185 -33
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/staggered_aggregation.py +16 -7
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/staggered_results.py +9 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/trop.py +153 -22
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/trop_global.py +17 -1
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/trop_local.py +179 -19
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/trop_results.py +33 -2
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/utils.py +195 -89
- {diff_diff-3.6.0 → diff_diff-3.6.1}/pyproject.toml +1 -1
- {diff_diff-3.6.0 → diff_diff-3.6.1}/rust/Cargo.lock +1 -1
- {diff_diff-3.6.0 → diff_diff-3.6.1}/rust/Cargo.toml +1 -1
- {diff_diff-3.6.0 → diff_diff-3.6.1}/LICENSE +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/_backend.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/_guides_api.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/_nprobust_port.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/_reporting_helpers.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/agent_workflow.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/bacon.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/balancing.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/bootstrap_chunking.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/bootstrap_utils.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/chaisemartin_dhaultfoeuille_bootstrap.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/conformal.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/conley.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/continuous_did.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/continuous_did_bspline.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/continuous_did_results.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/datasets.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/diagnostic_report.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/diagnostics.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/efficient_did.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/efficient_did_bootstrap.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/efficient_did_covariates.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/efficient_did_results.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/efficient_did_weights.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/guides/__init__.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/had.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/had_pretests.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/honest_did.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/imputation.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/imputation_bootstrap.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/imputation_results.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/linalg.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/local_linear.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/power.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/practitioner.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/prep.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/prep_dgp.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/pretrends.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/profile.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/results.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/spillover.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/stacked_did.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/stacked_did_results.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/staggered_bootstrap.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/staggered_triple_diff.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/staggered_triple_diff_results.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/sun_abraham.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/survey.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/synthetic_control.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/synthetic_control_results.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/synthetic_did.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/triple_diff.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/twfe.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/two_stage.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/two_stage_bootstrap.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/two_stage_results.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/visualization/__init__.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/visualization/_common.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/visualization/_continuous.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/visualization/_diagnostic.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/visualization/_event_study.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/visualization/_power.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/visualization/_staggered.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/visualization/_synthetic.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/wooldridge.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/diff_diff/wooldridge_results.py +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/rust/build.rs +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/rust/src/bootstrap.rs +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/rust/src/lib.rs +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/rust/src/linalg.rs +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/rust/src/trop.rs +0 -0
- {diff_diff-3.6.0 → diff_diff-3.6.1}/rust/src/weights.rs +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: diff-diff
|
|
3
|
-
Version: 3.6.
|
|
3
|
+
Version: 3.6.1
|
|
4
4
|
Classifier: Development Status :: 5 - Production/Stable
|
|
5
5
|
Classifier: Intended Audience :: Science/Research
|
|
6
6
|
Classifier: Operating System :: OS Independent
|
|
@@ -155,7 +155,7 @@ Full guide: `diff_diff.get_llm_guide("practitioner")`.
|
|
|
155
155
|
- [TwoWayFixedEffects](https://diff-diff.readthedocs.io/en/stable/api/estimators.html) - panel data DiD with unit and time fixed effects via within-transformation or dummies
|
|
156
156
|
- [MultiPeriodDiD](https://diff-diff.readthedocs.io/en/stable/api/estimators.html) - event study design with period-specific treatment effects for dynamic analysis
|
|
157
157
|
- [CallawaySantAnna](https://diff-diff.readthedocs.io/en/stable/api/staggered.html) - Callaway & Sant'Anna (2021) group-time ATT estimator for staggered adoption
|
|
158
|
-
- [ChaisemartinDHaultfoeuille](https://diff-diff.readthedocs.io/en/stable/api/chaisemartin_dhaultfoeuille.html) - de Chaisemartin & D'Haultfœuille (2020/2022) for **reversible (non-absorbing) treatments** with multi-horizon event study, normalized effects, cost-benefit delta, sup-t bands, and dynamic placebos. The
|
|
158
|
+
- [ChaisemartinDHaultfoeuille](https://diff-diff.readthedocs.io/en/stable/api/chaisemartin_dhaultfoeuille.html) - de Chaisemartin & D'Haultfœuille (2020/2022) for **reversible (non-absorbing) treatments** with multi-horizon event study, normalized effects, cost-benefit delta, sup-t bands, and dynamic placebos. The most general option for treatments that switch on AND off (see also `LPDiD`/`TROP` `non_absorbing`). Alias `DCDH`.
|
|
159
159
|
- [SunAbraham](https://diff-diff.readthedocs.io/en/stable/api/staggered.html) - Sun & Abraham (2021) interaction-weighted estimator for heterogeneity-robust event studies
|
|
160
160
|
- [ImputationDiD](https://diff-diff.readthedocs.io/en/stable/api/imputation.html) - Borusyak, Jaravel & Spiess (2024) imputation estimator, most efficient under homogeneous effects
|
|
161
161
|
- [TwoStageDiD](https://diff-diff.readthedocs.io/en/stable/api/two_stage.html) - Gardner (2022) two-stage estimator with GMM sandwich variance
|
|
@@ -170,7 +170,7 @@ Full guide: `diff_diff.get_llm_guide("practitioner")`.
|
|
|
170
170
|
- [TROP](https://diff-diff.readthedocs.io/en/stable/api/trop.html) - Triply Robust Panel estimator (Athey et al. 2025) with nuclear norm factor adjustment
|
|
171
171
|
- [StaggeredTripleDifference](https://diff-diff.readthedocs.io/en/stable/api/staggered.html#staggeredtripledifference) - Ortiz-Villavicencio & Sant'Anna (2025) staggered DDD with group-time ATT
|
|
172
172
|
- [WooldridgeDiD](https://diff-diff.readthedocs.io/en/stable/api/wooldridge_etwfe.html) - Wooldridge (2023, 2025) ETWFE: saturated OLS, logit/Poisson QMLE (ASF-based ATT). Alias `ETWFE`.
|
|
173
|
-
- [LPDiD](https://diff-diff.readthedocs.io/en/stable/api/lpdid.html) - Dube, Girardi, Jorda & Taylor (2025) Local Projections DiD: per-horizon long-difference event study on clean controls (no negative weighting), variance- or equally-weighted ATT, for absorbing treatment
|
|
173
|
+
- [LPDiD](https://diff-diff.readthedocs.io/en/stable/api/lpdid.html) - Dube, Girardi, Jorda & Taylor (2025) Local Projections DiD: per-horizon long-difference event study on clean controls (no negative weighting), variance- or equally-weighted ATT, for absorbing or non-absorbing (reversible) treatment
|
|
174
174
|
- [BaconDecomposition](https://diff-diff.readthedocs.io/en/stable/api/bacon.html) - Goodman-Bacon (2021) decomposition for diagnosing TWFE bias in staggered settings
|
|
175
175
|
|
|
176
176
|
## Diagnostics & Sensitivity
|
|
@@ -198,7 +198,7 @@ No other Python or R DiD package offers design-based variance estimation for mod
|
|
|
198
198
|
- Python 3.9 - 3.14
|
|
199
199
|
- numpy >= 1.20
|
|
200
200
|
- pandas >= 1.3
|
|
201
|
-
- scipy >= 1.
|
|
201
|
+
- scipy >= 1.10
|
|
202
202
|
|
|
203
203
|
## Development
|
|
204
204
|
|
|
@@ -102,7 +102,7 @@ Full guide: `diff_diff.get_llm_guide("practitioner")`.
|
|
|
102
102
|
- [TwoWayFixedEffects](https://diff-diff.readthedocs.io/en/stable/api/estimators.html) - panel data DiD with unit and time fixed effects via within-transformation or dummies
|
|
103
103
|
- [MultiPeriodDiD](https://diff-diff.readthedocs.io/en/stable/api/estimators.html) - event study design with period-specific treatment effects for dynamic analysis
|
|
104
104
|
- [CallawaySantAnna](https://diff-diff.readthedocs.io/en/stable/api/staggered.html) - Callaway & Sant'Anna (2021) group-time ATT estimator for staggered adoption
|
|
105
|
-
- [ChaisemartinDHaultfoeuille](https://diff-diff.readthedocs.io/en/stable/api/chaisemartin_dhaultfoeuille.html) - de Chaisemartin & D'Haultfœuille (2020/2022) for **reversible (non-absorbing) treatments** with multi-horizon event study, normalized effects, cost-benefit delta, sup-t bands, and dynamic placebos. The
|
|
105
|
+
- [ChaisemartinDHaultfoeuille](https://diff-diff.readthedocs.io/en/stable/api/chaisemartin_dhaultfoeuille.html) - de Chaisemartin & D'Haultfœuille (2020/2022) for **reversible (non-absorbing) treatments** with multi-horizon event study, normalized effects, cost-benefit delta, sup-t bands, and dynamic placebos. The most general option for treatments that switch on AND off (see also `LPDiD`/`TROP` `non_absorbing`). Alias `DCDH`.
|
|
106
106
|
- [SunAbraham](https://diff-diff.readthedocs.io/en/stable/api/staggered.html) - Sun & Abraham (2021) interaction-weighted estimator for heterogeneity-robust event studies
|
|
107
107
|
- [ImputationDiD](https://diff-diff.readthedocs.io/en/stable/api/imputation.html) - Borusyak, Jaravel & Spiess (2024) imputation estimator, most efficient under homogeneous effects
|
|
108
108
|
- [TwoStageDiD](https://diff-diff.readthedocs.io/en/stable/api/two_stage.html) - Gardner (2022) two-stage estimator with GMM sandwich variance
|
|
@@ -117,7 +117,7 @@ Full guide: `diff_diff.get_llm_guide("practitioner")`.
|
|
|
117
117
|
- [TROP](https://diff-diff.readthedocs.io/en/stable/api/trop.html) - Triply Robust Panel estimator (Athey et al. 2025) with nuclear norm factor adjustment
|
|
118
118
|
- [StaggeredTripleDifference](https://diff-diff.readthedocs.io/en/stable/api/staggered.html#staggeredtripledifference) - Ortiz-Villavicencio & Sant'Anna (2025) staggered DDD with group-time ATT
|
|
119
119
|
- [WooldridgeDiD](https://diff-diff.readthedocs.io/en/stable/api/wooldridge_etwfe.html) - Wooldridge (2023, 2025) ETWFE: saturated OLS, logit/Poisson QMLE (ASF-based ATT). Alias `ETWFE`.
|
|
120
|
-
- [LPDiD](https://diff-diff.readthedocs.io/en/stable/api/lpdid.html) - Dube, Girardi, Jorda & Taylor (2025) Local Projections DiD: per-horizon long-difference event study on clean controls (no negative weighting), variance- or equally-weighted ATT, for absorbing treatment
|
|
120
|
+
- [LPDiD](https://diff-diff.readthedocs.io/en/stable/api/lpdid.html) - Dube, Girardi, Jorda & Taylor (2025) Local Projections DiD: per-horizon long-difference event study on clean controls (no negative weighting), variance- or equally-weighted ATT, for absorbing or non-absorbing (reversible) treatment
|
|
121
121
|
- [BaconDecomposition](https://diff-diff.readthedocs.io/en/stable/api/bacon.html) - Goodman-Bacon (2021) decomposition for diagnosing TWFE bias in staggered settings
|
|
122
122
|
|
|
123
123
|
## Diagnostics & Sensitivity
|
|
@@ -145,7 +145,7 @@ No other Python or R DiD package offers design-based variance estimation for mod
|
|
|
145
145
|
- Python 3.9 - 3.14
|
|
146
146
|
- numpy >= 1.20
|
|
147
147
|
- pandas >= 1.3
|
|
148
|
-
- scipy >= 1.
|
|
148
|
+
- scipy >= 1.10
|
|
149
149
|
|
|
150
150
|
## Development
|
|
151
151
|
|
|
@@ -353,12 +353,21 @@ class BusinessReport:
|
|
|
353
353
|
"""Return a structured multi-section markdown report."""
|
|
354
354
|
base = _render_full_report(self.to_dict())
|
|
355
355
|
if self._include_appendix:
|
|
356
|
+
appendix_text = None
|
|
356
357
|
try:
|
|
357
358
|
appendix = self._results.summary()
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
359
|
+
if appendix:
|
|
360
|
+
appendix_text = str(appendix)
|
|
361
|
+
except Exception as exc: # noqa: BLE001
|
|
362
|
+
appendix_error = type(exc).__name__ or "Exception"
|
|
363
|
+
base = (
|
|
364
|
+
base
|
|
365
|
+
+ "\n\n## Technical Appendix\n\n"
|
|
366
|
+
+ "Technical appendix unavailable: estimator summary rendering failed "
|
|
367
|
+
+ f"({appendix_error}).\n"
|
|
368
|
+
)
|
|
369
|
+
if appendix_text:
|
|
370
|
+
base = base + "\n\n## Technical Appendix\n\n```\n" + appendix_text + "\n```\n"
|
|
362
371
|
return base
|
|
363
372
|
|
|
364
373
|
def export_markdown(self) -> str:
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
"""
|
|
2
2
|
de Chaisemartin-D'Haultfoeuille (dCDH) estimator for reversible-treatment DiD.
|
|
3
3
|
|
|
4
|
-
The dCDH estimator is the
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
The dCDH estimator is the most general DiD estimator in the diff-diff library
|
|
5
|
+
for **non-absorbing (reversible) treatments** — treatment can switch on AND off
|
|
6
|
+
over time, switcher vs non-switcher comparisons are its primitive object, and it
|
|
7
|
+
allows dynamic (carryover) effects with explicit joiner/leaver (``DID_+`` /
|
|
8
|
+
``DID_-``) decomposition. ``LPDiD`` (``non_absorbing="first_entry"`` /
|
|
9
|
+
``"effect_stabilization"``) and ``TROP`` (``non_absorbing=True``, under a
|
|
10
|
+
no-dynamic-effects assumption) also accept non-absorbing treatment under stronger
|
|
11
|
+
assumptions. The remaining staggered estimators in the library
|
|
7
12
|
(``CallawaySantAnna``, ``SunAbraham``, ``ImputationDiD``, ``TwoStageDiD``,
|
|
8
13
|
``EfficientDiD``, ``WooldridgeDiD``) assume treatment is absorbing.
|
|
9
14
|
|
|
@@ -354,9 +359,11 @@ class ChaisemartinDHaultfoeuille(ChaisemartinDHaultfoeuilleBootstrapMixin):
|
|
|
354
359
|
"""
|
|
355
360
|
de Chaisemartin-D'Haultfoeuille (dCDH) estimator.
|
|
356
361
|
|
|
357
|
-
The
|
|
358
|
-
|
|
359
|
-
|
|
362
|
+
The most general library estimator for **reversible (non-absorbing)
|
|
363
|
+
treatments** - treatment may switch on AND off over time, with explicit
|
|
364
|
+
joiner/leaver (``DID_+`` / ``DID_-``) decomposition (``LPDiD`` and ``TROP``
|
|
365
|
+
also support non-absorbing treatment under stronger assumptions; see their
|
|
366
|
+
``non_absorbing`` parameters). Computes the contemporaneous-switch DiD ``DID_M`` from the
|
|
360
367
|
AER 2020 paper (equivalently ``DID_1`` at horizon ``l = 1`` of the
|
|
361
368
|
dynamic companion paper, NBER WP 29873) plus the full multi-horizon
|
|
362
369
|
event study ``DID_l`` for ``l = 1..L_max`` via the ``L_max`` parameter
|
|
@@ -4,9 +4,11 @@ Result containers for the de Chaisemartin-D'Haultfoeuille (dCDH) estimator.
|
|
|
4
4
|
This module contains ``ChaisemartinDHaultfoeuilleResults`` and
|
|
5
5
|
``DCDHBootstrapResults`` dataclasses produced by the
|
|
6
6
|
``ChaisemartinDHaultfoeuille`` (alias ``DCDH``) estimator. The dCDH
|
|
7
|
-
estimator is the
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
estimator is the most general library estimator for non-absorbing
|
|
8
|
+
(reversible) treatments (``LPDiD`` and ``TROP`` also support non-absorbing
|
|
9
|
+
treatment under stronger assumptions; see their ``non_absorbing`` parameters).
|
|
10
|
+
Phase 1 ships the contemporaneous-switch case ``DID_M`` (= ``DID_1`` of the
|
|
11
|
+
dynamic companion paper).
|
|
10
12
|
|
|
11
13
|
References
|
|
12
14
|
----------
|
|
@@ -28,7 +28,7 @@ from diff_diff.linalg import (
|
|
|
28
28
|
from diff_diff.results import DiDResults, MultiPeriodDiDResults, PeriodEffect
|
|
29
29
|
from diff_diff.utils import (
|
|
30
30
|
WildBootstrapResults,
|
|
31
|
-
|
|
31
|
+
demean_by_groups,
|
|
32
32
|
fe_dummy_names,
|
|
33
33
|
safe_inference,
|
|
34
34
|
validate_binary,
|
|
@@ -414,17 +414,9 @@ class DifferenceInDifferences:
|
|
|
414
414
|
absorbed_vars = []
|
|
415
415
|
n_absorbed_effects = 0
|
|
416
416
|
|
|
417
|
-
#
|
|
418
|
-
#
|
|
419
|
-
#
|
|
420
|
-
# consume it.
|
|
421
|
-
if absorb and len(absorb) > 1 and survey_weights is not None:
|
|
422
|
-
raise ValueError(
|
|
423
|
-
f"Multiple absorbed fixed effects (absorb={absorb}) with survey "
|
|
424
|
-
"weights is not supported. Single-pass sequential demeaning is not "
|
|
425
|
-
"the correct weighted FWL projection for multiple absorbed dimensions. "
|
|
426
|
-
"Use absorb with a single variable, or use fixed_effects= instead."
|
|
427
|
-
)
|
|
417
|
+
# Weighted multiple absorbed FE is supported: the absorb path below uses
|
|
418
|
+
# iterative alternating projections (demean_by_groups), the exact weighted
|
|
419
|
+
# FWL projection for N > 1 dimensions on both balanced and unbalanced panels.
|
|
428
420
|
|
|
429
421
|
# Validate vcov_type="conley" wire-up. DiD.fit() accepts `unit`
|
|
430
422
|
# as a fit-time arg (NOT on __init__) because cluster/unit
|
|
@@ -462,16 +454,18 @@ class DifferenceInDifferences:
|
|
|
462
454
|
float
|
|
463
455
|
) * working_data[time].values.astype(float)
|
|
464
456
|
vars_to_demean = [outcome, treatment, time, "_treat_time"] + (covariates or [])
|
|
465
|
-
for
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
457
|
+
# Method of alternating projections: for N > 1 absorbed dimensions a
|
|
458
|
+
# single sequential sweep is only exact on balanced (orthogonal-FE)
|
|
459
|
+
# panels; demean_by_groups iterates to the exact (W)LS-FWL residual.
|
|
460
|
+
working_data, n_fe = demean_by_groups(
|
|
461
|
+
working_data,
|
|
462
|
+
vars_to_demean,
|
|
463
|
+
list(absorb),
|
|
464
|
+
inplace=True,
|
|
465
|
+
weights=survey_weights,
|
|
466
|
+
)
|
|
467
|
+
n_absorbed_effects += n_fe
|
|
468
|
+
absorbed_vars = list(absorb)
|
|
475
469
|
|
|
476
470
|
# Extract variables (may be demeaned if absorb was used)
|
|
477
471
|
y = working_data[outcome].values.astype(float)
|
|
@@ -644,8 +638,7 @@ class DifferenceInDifferences:
|
|
|
644
638
|
float
|
|
645
639
|
)
|
|
646
640
|
vars_dm = [outcome, treatment, time, "_treat_time"] + (covariates or [])
|
|
647
|
-
|
|
648
|
-
wd, _ = demean_by_group(wd, vars_dm, ab_var, inplace=True, weights=w_nz)
|
|
641
|
+
wd, _ = demean_by_groups(wd, vars_dm, _absorb_list, inplace=True, weights=w_nz)
|
|
649
642
|
y_r = wd[outcome].values.astype(float)
|
|
650
643
|
d_r = wd[treatment].values.astype(float)
|
|
651
644
|
t_r = wd[time].values.astype(float)
|
|
@@ -1572,17 +1565,9 @@ class MultiPeriodDiD(DifferenceInDifferences):
|
|
|
1572
1565
|
absorb = None
|
|
1573
1566
|
n_absorbed_effects = 0
|
|
1574
1567
|
|
|
1575
|
-
#
|
|
1576
|
-
#
|
|
1577
|
-
#
|
|
1578
|
-
# didn't consume it.
|
|
1579
|
-
if absorb and len(absorb) > 1 and survey_weights is not None:
|
|
1580
|
-
raise ValueError(
|
|
1581
|
-
f"Multiple absorbed fixed effects (absorb={absorb}) with survey "
|
|
1582
|
-
"weights is not supported. Single-pass sequential demeaning is not "
|
|
1583
|
-
"the correct weighted FWL projection for multiple absorbed dimensions. "
|
|
1584
|
-
"Use absorb with a single variable, or use fixed_effects= instead."
|
|
1585
|
-
)
|
|
1568
|
+
# Weighted multiple absorbed FE is supported: the absorb path below uses
|
|
1569
|
+
# iterative alternating projections (demean_by_groups), the exact weighted
|
|
1570
|
+
# FWL projection for N > 1 dimensions on both balanced and unbalanced panels.
|
|
1586
1571
|
|
|
1587
1572
|
# MultiPeriodDiD is intrinsically a multi-period panel estimator;
|
|
1588
1573
|
# Phase 2 panel block-decomposed Conley (matches R conleyreg) needs
|
|
@@ -1622,15 +1607,16 @@ class MultiPeriodDiD(DifferenceInDifferences):
|
|
|
1622
1607
|
+ [f"_did_interact_{p}" for p in non_ref_periods]
|
|
1623
1608
|
+ (covariates or [])
|
|
1624
1609
|
)
|
|
1625
|
-
for
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1610
|
+
# Method of alternating projections (exact for unbalanced panels; a
|
|
1611
|
+
# single sequential sweep is exact only on balanced orthogonal-FE panels).
|
|
1612
|
+
working_data, n_fe = demean_by_groups(
|
|
1613
|
+
working_data,
|
|
1614
|
+
vars_to_demean,
|
|
1615
|
+
list(absorb),
|
|
1616
|
+
inplace=True,
|
|
1617
|
+
weights=survey_weights,
|
|
1618
|
+
)
|
|
1619
|
+
n_absorbed_effects += n_fe
|
|
1634
1620
|
|
|
1635
1621
|
# Extract outcome and treatment (may be demeaned if absorb was used)
|
|
1636
1622
|
y = working_data[outcome].values.astype(float)
|
|
@@ -1854,8 +1840,7 @@ class MultiPeriodDiD(DifferenceInDifferences):
|
|
|
1854
1840
|
+ [f"_did_interact_{p}" for p in non_ref_periods]
|
|
1855
1841
|
+ (covariates or [])
|
|
1856
1842
|
)
|
|
1857
|
-
|
|
1858
|
-
wd, _ = demean_by_group(wd, vars_dm_, ab_var_, inplace=True, weights=w_nz)
|
|
1843
|
+
wd, _ = demean_by_groups(wd, vars_dm_, _absorb_list_mp, inplace=True, weights=w_nz)
|
|
1859
1844
|
y_r = wd[outcome].values.astype(float)
|
|
1860
1845
|
d_r = wd["_did_treatment"].values.astype(float)
|
|
1861
1846
|
X_r = np.column_stack([np.ones(len(y_r)), d_r])
|
|
@@ -531,12 +531,21 @@ When `has_never_treated == False`:
|
|
|
531
531
|
|
|
532
532
|
When `treatment_type == "binary_non_absorbing"`:
|
|
533
533
|
|
|
534
|
-
- `ChaisemartinDHaultfoeuille` is the
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
534
|
+
- `ChaisemartinDHaultfoeuille` is the most general / default choice and
|
|
535
|
+
treats this natively. Switcher / non-switcher comparisons are its
|
|
536
|
+
primitive object; it allows dynamic (carryover) effects and reports
|
|
537
|
+
joiner/leaver (`DID_+` / `DID_-`) views. Prefer it when effects may
|
|
538
|
+
persist after treatment turns off.
|
|
539
|
+
- `LPDiD(non_absorbing="first_entry")` or `"effect_stabilization"`
|
|
540
|
+
(entry-effect estimands) and `TROP(non_absorbing=True, method="local")`
|
|
541
|
+
(valid under a no-dynamic-effects / no-carryover assumption) also handle
|
|
542
|
+
non-absorbing treatment, under stronger assumptions. Use TROP's option
|
|
543
|
+
only when effects are contemporaneous (no carryover).
|
|
544
|
+
- The remaining estimators (`CallawaySantAnna`, `SunAbraham`,
|
|
545
|
+
`ImputationDiD`, `TwoStageDiD`, `EfficientDiD`, `WooldridgeDiD`) assume
|
|
546
|
+
absorbing treatment and will produce estimates whose interpretation is
|
|
547
|
+
unclear on non-absorbing data. Do not use them without a well-argued
|
|
548
|
+
reason.
|
|
540
549
|
|
|
541
550
|
### §4.6 Triple-difference design (DDD)
|
|
542
551
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> A Python library for Difference-in-Differences causal inference analysis. Provides sklearn-like estimators with statsmodels-style output for econometric analysis.
|
|
4
4
|
|
|
5
|
-
- Version: 3.6.
|
|
5
|
+
- Version: 3.6.1
|
|
6
6
|
- Repository: https://github.com/igerber/diff-diff
|
|
7
7
|
- License: MIT
|
|
8
8
|
- Dependencies: numpy, pandas, scipy (no statsmodels dependency)
|
|
@@ -231,7 +231,7 @@ plot_event_study(results)
|
|
|
231
231
|
|
|
232
232
|
### ChaisemartinDHaultfoeuille
|
|
233
233
|
|
|
234
|
-
de Chaisemartin & D'Haultfœuille (2020/2022) estimator for **non-absorbing (reversible) treatments**. The
|
|
234
|
+
de Chaisemartin & D'Haultfœuille (2020/2022) estimator for **non-absorbing (reversible) treatments**. The most general library estimator for treatments that switch on AND off over time (allows dynamic/carryover effects + joiner/leaver decomposition); `LPDiD` (`non_absorbing="first_entry"`/`"effect_stabilization"`) and `TROP` (`non_absorbing=True`, no-dynamic-effects) also handle non-absorbing treatment under stronger assumptions. Ships `DID_M` (= `DID_1` at horizon `l = 1`) plus the full multi-horizon event study `DID_l` for `l = 1..L_max` from the dynamic companion paper (NBER WP 29873). Includes normalized estimator `DID^n_l`, cost-benefit aggregate `delta`, dynamic placebos `DID^{pl}_l`, and sup-t simultaneous confidence bands.
|
|
235
235
|
|
|
236
236
|
```python
|
|
237
237
|
ChaisemartinDHaultfoeuille(
|
|
@@ -897,7 +897,7 @@ results.print_summary()
|
|
|
897
897
|
|
|
898
898
|
### LPDiD
|
|
899
899
|
|
|
900
|
-
Local Projections DiD (Dube, Girardi, Jorda & Taylor 2025). Estimates a separate OLS at each event-time horizon of a long difference (`y_{i,t+h} - y_{i,t-1}`) on the treatment-switch indicator plus calendar-time fixed effects (no unit FE), restricted to a flexible "clean control" sample of newly-treated and not-yet-treated units. Excluding already-treated units from the control group removes the negative-weighting bias of naive TWFE, so the default (variance-weighted) estimand has strictly non-negative weights. `reweight=True` yields the equally-weighted ATT (numerically equivalent to Callaway-Sant'Anna); covariates then enter via regression adjustment. Standard errors on the default/weighted path are cluster-robust at the unit level (the paper specifies no SE; matches Stata `lpdid` `vce(cluster unit)`); the regression-adjustment covariate path (`reweight=True`) instead reports an influence-function cluster variance (ImputationDiD/BJS family). Scope: binary
|
|
900
|
+
Local Projections DiD (Dube, Girardi, Jorda & Taylor 2025). Estimates a separate OLS at each event-time horizon of a long difference (`y_{i,t+h} - y_{i,t-1}`) on the treatment-switch indicator plus calendar-time fixed effects (no unit FE), restricted to a flexible "clean control" sample of newly-treated and not-yet-treated units. Excluding already-treated units from the control group removes the negative-weighting bias of naive TWFE, so the default (variance-weighted) estimand has strictly non-negative weights. `reweight=True` yields the equally-weighted ATT (numerically equivalent to Callaway-Sant'Anna); covariates then enter via regression adjustment. Standard errors on the default/weighted path are cluster-robust at the unit level (the paper specifies no SE; matches Stata `lpdid` `vce(cluster unit)`); the regression-adjustment covariate path (`reweight=True`) instead reports an influence-function cluster variance (ImputationDiD/BJS family). Scope: binary treatment; absorbing by default (rejects panels where treatment turns off), with non-absorbing (reversible) treatment available via `non_absorbing` - `"first_entry"` (Dube et al. Eq. 12, the effect of entering for the first time and staying treated) or `"effect_stabilization"` (Eq. 13, requires `stabilization_window=L`; lets units whose treatment has been stable for at least `L` periods act as clean controls, so estimation is feasible with few/no never-treated units). Non-absorbing modes require a gap-free panel within each unit's observed span. Complex-survey designs are supported on the variance-weighted default path via the `survey_design=` argument to `fit()` (probability weights enter the WLS point estimate; the SE is the stratified-PSU Taylor-linearization sandwich with `df = n_PSU - n_strata`, with optional FPC and lonely-PSU handling) — rejected with `reweight=True`, replicate weights, or non-pweight types.
|
|
901
901
|
|
|
902
902
|
```python
|
|
903
903
|
LPDiD(
|
|
@@ -910,6 +910,8 @@ LPDiD(
|
|
|
910
910
|
alpha: float = 0.05,
|
|
911
911
|
cluster: str | None = None, # Cluster column for cluster-robust SEs; defaults to the unit identifier
|
|
912
912
|
rank_deficient_action: str = "warn", # "warn", "error", or "silent"
|
|
913
|
+
non_absorbing: str | None = None, # None=absorbing; "first_entry" (Eq. 12); "effect_stabilization" (Eq. 13)
|
|
914
|
+
stabilization_window: int | None = None, # The paper's L; required when non_absorbing="effect_stabilization"
|
|
913
915
|
)
|
|
914
916
|
```
|
|
915
917
|
|
|
@@ -921,7 +923,7 @@ lpdid.fit(
|
|
|
921
923
|
outcome: str,
|
|
922
924
|
unit: str,
|
|
923
925
|
time: str,
|
|
924
|
-
treatment: str, # Binary
|
|
926
|
+
treatment: str, # Binary treatment indicator (0/1); absorbing unless non_absorbing is set
|
|
925
927
|
covariates: list[str] = None, # Direct inclusion (reweight=False) or regression adjustment (reweight=True)
|
|
926
928
|
ylags: int = 0, # Lagged-outcome controls
|
|
927
929
|
dylags: int = 0, # Lagged first-difference controls
|
|
@@ -930,6 +932,7 @@ lpdid.fit(
|
|
|
930
932
|
pre_pooled: int | tuple = None, # Pooled pre-window horizons (int or (start, end))
|
|
931
933
|
only_event: bool = False, # Compute only the event-study table
|
|
932
934
|
only_pooled: bool = False, # Compute only the pooled pre/post table
|
|
935
|
+
survey_design: SurveyDesign = None, # Complex-survey design (pweight + optional strata/PSU/FPC); variance-weighted default path only (rejected with reweight=True)
|
|
933
936
|
) -> LPDiDResults
|
|
934
937
|
```
|
|
935
938
|
|
|
@@ -960,6 +963,7 @@ TROP(
|
|
|
960
963
|
alpha: float = 0.05,
|
|
961
964
|
n_bootstrap: int = 200,
|
|
962
965
|
seed: int | None = None,
|
|
966
|
+
non_absorbing: bool = False, # False: require absorbing D (reject non-monotonic). True: allow on/off treatment (Eq. 12/Alg. 2), method='local' only; emits a caveat warning (Thm 5.1 is block-only).
|
|
963
967
|
)
|
|
964
968
|
```
|
|
965
969
|
|
|
@@ -969,7 +973,7 @@ TROP(
|
|
|
969
973
|
trop.fit(
|
|
970
974
|
data: pd.DataFrame,
|
|
971
975
|
outcome: str,
|
|
972
|
-
treatment: str, #
|
|
976
|
+
treatment: str, # Treatment indicator (0/1). Default (non_absorbing=False): absorbing state -- 0 for all pre-treatment periods, 1 for treatment and post-treatment; non-monotonic D raises ValueError. With non_absorbing=True: any on/off pattern (general assignment).
|
|
973
977
|
unit: str,
|
|
974
978
|
time: str,
|
|
975
979
|
) -> TROPResults
|
|
@@ -213,9 +213,12 @@ Is treatment adoption staggered (multiple cohorts, different timing)?
|
|
|
213
213
|
|
|
|
214
214
|
|-- Treatment switches ON and OFF (reversible / non-absorbing)?
|
|
215
215
|
| \-- ChaisemartinDHaultfoeuille (dCDH / alias `DCDH`)
|
|
216
|
-
| --
|
|
217
|
-
| L_max multi-horizon,
|
|
218
|
-
|
|
|
216
|
+
| -- Most general option for non-absorbing treatments (allows dynamic
|
|
217
|
+
| effects + joiner/leaver views); supports L_max multi-horizon,
|
|
218
|
+
| dynamic placebos, cost-benefit delta, HonestDiD, and
|
|
219
|
+
| `survey_design=` (pweight + strata/PSU/FPC via TSL)
|
|
220
|
+
| -- Also: LPDiD(non_absorbing="first_entry"/"effect_stabilization")
|
|
221
|
+
| and TROP(non_absorbing=True, no-dynamic-effects) under stronger assumptions
|
|
219
222
|
|
|
|
220
223
|
|-- Few treated units (< 20)?
|
|
221
224
|
| \-- SyntheticDiD (SDiD) -- synthetic control + DiD hybrid
|
|
@@ -54,7 +54,7 @@ Full practitioner guide: call `diff_diff.get_llm_guide("practitioner")`
|
|
|
54
54
|
- [TwoWayFixedEffects](https://diff-diff.readthedocs.io/en/stable/api/estimators.html): Panel data DiD with unit and time fixed effects via within-transformation or dummies
|
|
55
55
|
- [MultiPeriodDiD](https://diff-diff.readthedocs.io/en/stable/api/estimators.html): Event study design with period-specific treatment effects for dynamic analysis
|
|
56
56
|
- [CallawaySantAnna](https://diff-diff.readthedocs.io/en/stable/api/staggered.html): Callaway & Sant'Anna (2021) group-time ATT estimator for staggered adoption with aggregation
|
|
57
|
-
- [ChaisemartinDHaultfoeuille](https://diff-diff.readthedocs.io/en/stable/api/chaisemartin_dhaultfoeuille.html): de Chaisemartin & D'Haultfœuille (2020/2022) estimator for **reversible (non-absorbing) treatments** with multi-horizon event study (`L_max`), normalized effects, cost-benefit delta, sup-t bands, and dynamic placebos. The
|
|
57
|
+
- [ChaisemartinDHaultfoeuille](https://diff-diff.readthedocs.io/en/stable/api/chaisemartin_dhaultfoeuille.html): de Chaisemartin & D'Haultfœuille (2020/2022) estimator for **reversible (non-absorbing) treatments** with multi-horizon event study (`L_max`), normalized effects, cost-benefit delta, sup-t bands, and dynamic placebos. The most general option for treatments that switch on AND off (LPDiD/TROP `non_absorbing` also handle non-absorbing treatment under stronger assumptions). Alias `DCDH`.
|
|
58
58
|
- [SunAbraham](https://diff-diff.readthedocs.io/en/stable/api/staggered.html): Sun & Abraham (2021) interaction-weighted estimator for heterogeneity-robust event studies
|
|
59
59
|
- [ImputationDiD](https://diff-diff.readthedocs.io/en/stable/api/imputation.html): Borusyak, Jaravel & Spiess (2024) imputation estimator — most efficient under homogeneous effects
|
|
60
60
|
- [TwoStageDiD](https://diff-diff.readthedocs.io/en/stable/api/two_stage.html): Gardner (2022) two-stage estimator with GMM sandwich variance
|
|
@@ -66,10 +66,10 @@ Full practitioner guide: call `diff_diff.get_llm_guide("practitioner")`
|
|
|
66
66
|
- [HeterogeneousAdoptionDiD](https://diff-diff.readthedocs.io/en/stable/api/had.html): de Chaisemartin, Ciccia, D'Haultfœuille & Knau (2026) for designs where **no unit remains untreated**; local-linear estimator at the dose support boundary returning Weighted Average Slope (WAS) on Design 1' (`d̲=0` / QUG) or `WAS_{d̲}` on Design 1 (`d̲>0`, continuous-near-d̲ or mass-point), with multi-period event-study extension (last-treatment cohort, pointwise CIs). **Panel-only** in this release (repeated cross-sections rejected by the validator). Alias `HAD`.
|
|
67
67
|
- [StackedDiD](https://diff-diff.readthedocs.io/en/stable/api/stacked_did.html): Wing, Freedman & Hollingsworth (2024) stacked DiD with Q-weights and sub-experiments; optional covariate balancing (`balance="entropy"`, Ustyuzhanin 2026)
|
|
68
68
|
- [EfficientDiD](https://diff-diff.readthedocs.io/en/stable/api/efficient_did.html): Chen, Sant'Anna & Xie (2025) efficient DiD with optimal weighting for tighter SEs
|
|
69
|
-
- [TROP](https://diff-diff.readthedocs.io/en/stable/api/trop.html): Triply Robust Panel estimator (Athey et al. 2025) with nuclear norm factor adjustment
|
|
69
|
+
- [TROP](https://diff-diff.readthedocs.io/en/stable/api/trop.html): Triply Robust Panel estimator (Athey et al. 2025) with nuclear norm factor adjustment (absorbing by default; `non_absorbing=True` for on/off treatment, method='local')
|
|
70
70
|
- [StaggeredTripleDifference](https://diff-diff.readthedocs.io/en/stable/api/staggered.html#staggeredtripledifference): Ortiz-Villavicencio & Sant'Anna (2025) staggered DDD with group-time ATT
|
|
71
71
|
- [WooldridgeDiD](https://diff-diff.readthedocs.io/en/stable/api/wooldridge_etwfe.html): Wooldridge (2023, 2025) ETWFE — saturated OLS, logit/Poisson QMLE (ASF-based ATT). Alias: ETWFE
|
|
72
|
-
- [LPDiD](https://diff-diff.readthedocs.io/en/stable/api/lpdid.html): Dube, Girardi, Jorda & Taylor (2025) Local Projections DiD: per-horizon long-difference event study on clean controls (no negative weighting); variance- or equally-weighted ATT, premean differencing, pooled pre/post, fast. Absorbing treatment.
|
|
72
|
+
- [LPDiD](https://diff-diff.readthedocs.io/en/stable/api/lpdid.html): Dube, Girardi, Jorda & Taylor (2025) Local Projections DiD: per-horizon long-difference event study on clean controls (no negative weighting); variance- or equally-weighted ATT, premean differencing, pooled pre/post, fast. Absorbing by default; non-absorbing (reversible) treatment via `non_absorbing="first_entry"` (Eq. 12) or `"effect_stabilization"` (Eq. 13, window `L`). Complex-survey designs (pweight + stratified-PSU TSL SEs) on the default path via `fit(survey_design=...)`.
|
|
73
73
|
- [BaconDecomposition](https://diff-diff.readthedocs.io/en/stable/api/bacon.html): Goodman-Bacon (2021) decomposition for diagnosing TWFE bias in staggered settings
|
|
74
74
|
|
|
75
75
|
## Diagnostics and Sensitivity Analysis
|