Skip to content

Commit 993e43a

Browse files
authored
Merge pull request #301 from igerber/release/v3.0.2
Release v3.0.2
2 parents 8ca80e0 + a6b1a83 commit 993e43a

6 files changed

Lines changed: 33 additions & 29 deletions

File tree

CHANGELOG.md

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,30 +7,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [3.0.2] - 2026-04-12
11+
1012
### Added
11-
- **`ChaisemartinDHaultfoeuille`** (alias `DCDH`) — Phase 1 of the de Chaisemartin-D'Haultfœuille estimator family, the only modern staggered DiD estimator in the library that handles **non-absorbing (reversible) treatments**. Treatment can switch on AND off over time (marketing campaigns, seasonal promotions, on/off policy cycles). Implements `DID_M` from de Chaisemartin & D'Haultfœuille (2020) AER, equivalently `DID_1` (horizon `l = 1`) of the dynamic companion paper (NBER WP 29873). Ships:
12-
- Headline `DID_M` point estimate with cohort-recentered analytical SE from Web Appendix Section 3.7.3 of the dynamic companion paper
13-
- Joiners-only (`DID_+`) and leavers-only (`DID_-`) decompositions with their own inference
14-
- Single-lag placebo `DID_M^pl` point estimate (AER 2020 placebo specification). Placebo SE / inference fields are intentionally `NaN` in Phase 1: the dynamic companion paper Section 3.7.3 derives the cohort-recentered analytical variance for `DID_l` only, not for the placebo. Phase 2 will add multiplier-bootstrap support for the placebo. The bootstrap path in Phase 1 covers `DID_M`, `DID_+`, and `DID_-` only.
15-
- Optional multiplier bootstrap clustered at group level with Rademacher / Mammen / Webb weights for `DID_M`, `DID_+`, and `DID_-` (placebo bootstrap deferred to Phase 2)
16-
- TWFE decomposition diagnostic from Theorem 1 of AER 2020 (per-cell weights, fraction negative, `sigma_fe`, `beta_fe`)
17-
- Multi-switch group filtering (`drop_larger_lower=True` default, matches R `DIDmultiplegtDYN`); singleton-baseline filter (footnote 15 of dynamic paper, variance computation only); consolidated A11 zero-retention warnings — all with explicit warnings (no silent failures). Never-switching groups participate in the variance via stable-control roles after the Round 2 full-IF fix; the `n_groups_dropped_never_switching` field is retained as backwards-compatibility metadata only.
18-
- Phase 1 requires balanced-baseline panels with no interior period gaps. Late-entry groups (missing the first global period) raise `ValueError`; interior-gap groups are dropped with a `UserWarning`; terminally-missing groups (early exit / right-censoring) are retained and contribute from their observed periods only. This is a documented deviation from R `DIDmultiplegtDYN`'s unbalanced-panel support — see `docs/methodology/REGISTRY.md` for rationale and workarounds.
19-
- Forward-compatible `fit()` signature: Phase 2 (multi-horizon event study, `aggregate`, `L_max`) and Phase 3 (covariate adjustment via `controls`, group-specific linear trends, HonestDiD) parameters present from day one, raising `NotImplementedError` with phase pointers
20-
- Validated against R `DIDmultiplegtDYN` v2.3.3 at horizon `l = 1` via `tests/test_chaisemartin_dhaultfoeuille_parity.py`
21-
- **`twowayfeweights()`** — standalone helper function for the TWFE decomposition diagnostic (Theorem 1 of de Chaisemartin & D'Haultfœuille 2020), available without instantiating the full estimator. Returns a `TWFEWeightsResult` with per-cell weights, fraction negative, `sigma_fe`, and `beta_fe`.
22-
- **`generate_reversible_did_data()`** — new generator in `diff_diff.prep` producing reversible-treatment panel data for testing and tutorials. Patterns: `single_switch` (default, A5-safe), `joiners_only`, `leavers_only`, `mixed_single_switch`, `random`, `cycles`, `marketing`. Returns columns `group`, `period`, `treatment`, `outcome`, `true_effect`, `d_lag`, `switcher_type`.
23-
- **REGISTRY.md `## ChaisemartinDHaultfoeuille` section** — single canonical source for dCDH methodology, equations, edge cases, and all documented deviations from the R `DIDmultiplegtDYN` reference implementation. Cites the AER 2020 paper and the dynamic companion paper (NBER WP 29873) by reference; primary papers are upstream sources, not in-repo files.
24-
- **Phase 2: Multi-horizon event study for `ChaisemartinDHaultfoeuille`** — adds `L_max` parameter to `fit()` for computing `DID_l` at horizons `l = 1, ..., L_max` using the per-group building block from Equation 3 of the dynamic companion paper. Ships:
25-
- Per-horizon point estimates and cohort-recentered analytical SE
26-
- Dynamic placebos `DID^{pl}_l` with dual eligibility condition (Web Appendix Section 1.1)
27-
- Normalized estimator `DID^n_l = DID_l / delta^D_l` (Section 3.2)
28-
- Cost-benefit aggregate `delta` (Section 3.3, Lemma 4) — becomes `overall_att` when `L_max > 1`
29-
- Sup-t simultaneous confidence bands via multiplier bootstrap
30-
- `plot_event_study()` integration with `<50%` switcher warning for far horizons
31-
- `to_dataframe(level="event_study")` and `to_dataframe(level="normalized")` output
32-
- Per-horizon bootstrap with bootstrap SE/CI/p-value propagation to event_study_effects
33-
- `L_max=None` (default) preserves exact Phase 1 behavior
13+
- **`ChaisemartinDHaultfoeuille`** (alias `DCDH`) - de Chaisemartin & D'Haultfœuille estimator for **non-absorbing (reversible) treatments**. The only modern staggered DiD estimator that handles treatment switching on AND off. Implements `DID_M` from AER 2020, validated against R `DIDmultiplegtDYN` v2.3.3. Ships Phases 1 and 2:
14+
- Phase 1: headline `DID_M` with analytical SE, joiners/leavers decompositions, single-lag placebo, multiplier bootstrap, TWFE decomposition diagnostic
15+
- Phase 2: multi-horizon event study (`L_max`), dynamic placebos, normalized estimator, cost-benefit aggregate (Lemma 4), sup-t simultaneous confidence bands, `plot_event_study()` integration
16+
- **`twowayfeweights()`** - standalone TWFE decomposition diagnostic (Theorem 1, AER 2020)
17+
- **`generate_reversible_did_data()`** - reversible-treatment panel data generator with 7 switch patterns
18+
- **Survey-aware power analysis** - analytical helpers (`compute_power()`, `compute_mde()`, `compute_sample_size()`) accept a `deff` parameter for design-effect adjustment. Simulation helpers (`simulate_power`, `simulate_mde`, `simulate_sample_size`) accept a `survey_config` (`SurveyPowerConfig`) that generates data with complex survey structure and injects a `SurveyDesign` into each simulated fit.
19+
- **`aggregate_survey()` `second_stage_weights` parameter** - choose `"pweight"` (default, population weights) or `"aweight"` (precision weights). pweight output is compatible with all survey-capable estimators; aweight is opt-in for GLS efficiency with estimators marked Full in the survey support matrix.
20+
- **`conditional_pt` parameter** on `generate_survey_did_data()` - simulates scenarios where unconditional parallel trends fail but conditional PT holds after covariate adjustment
21+
- **Tutorial 18: Geo-Experiment Analysis** (`18_geo_experiments.ipynb`) - SyntheticDiD walkthrough for marketing analytics: simulated DMA panel, 5 treated markets, fit + diagnostics + stakeholder summary
22+
- **Practitioner decision tree** (`docs/practitioner_decision_tree.rst`) - "which method fits my business problem?" guide
23+
- **Practitioner getting started guide** (`docs/practitioner_getting_started.rst`) - end-to-end walkthrough with terminology bridge
24+
- **JOSS paper** (`paper.md`, `paper.bib`) - software paper for Journal of Open Source Software submission
25+
- **CONTRIBUTORS.md** - author and contributor credit
26+
- **Standalone CI Gate workflow** (`.github/workflows/ci-gate.yml`) - doc-only PRs no longer block on path-filtered test workflows
27+
28+
### Changed
29+
- `aggregate_survey()` default second-stage weights changed from `aweight` (precision) to `pweight` (population). Users who need the old precision-weighting behavior can pass `second_stage_weights="aweight"`.
30+
- README "For Data Scientists" section with practitioner-facing links and `aggregate_survey()` documentation
31+
- CITATION.cff updated with version and release date
32+
- ROADMAP.md updated: B1a-d marked done, B2b marked done, B3d marked shipped, dCDH entry updated with correct citations
33+
34+
### Fixed
35+
- Doc-only PRs no longer block indefinitely on CI Gate (standalone gate workflow runs on all PRs regardless of path filters)
36+
- `aggregate_survey()` docs no longer overclaim universal estimator compatibility - explicitly document aweight/pweight restrictions per the survey support matrix
3437

3538
## [3.0.1] - 2026-04-07
3639

@@ -1275,6 +1278,7 @@ for the full feature history leading to this release.
12751278
[2.1.2]: https://github.com/igerber/diff-diff/compare/v2.1.1...v2.1.2
12761279
[2.1.1]: https://github.com/igerber/diff-diff/compare/v2.1.0...v2.1.1
12771280
[2.1.0]: https://github.com/igerber/diff-diff/compare/v2.0.3...v2.1.0
1281+
[3.0.2]: https://github.com/igerber/diff-diff/compare/v3.0.1...v3.0.2
12781282
[2.0.3]: https://github.com/igerber/diff-diff/compare/v2.0.2...v2.0.3
12791283
[2.0.2]: https://github.com/igerber/diff-diff/compare/v2.0.1...v2.0.2
12801284
[2.0.1]: https://github.com/igerber/diff-diff/compare/v2.0.0...v2.0.1

CITATION.cff

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ authors:
77
family-names: Gerber
88
orcid: "https://orcid.org/0009-0009-3275-5591"
99
license: MIT
10-
version: "3.0.1"
11-
date-released: "2026-04-07"
10+
version: "3.0.2"
11+
date-released: "2026-04-12"
1212
url: "https://github.com/igerber/diff-diff"
1313
repository-code: "https://github.com/igerber/diff-diff"
1414
keywords:

diff_diff/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@
228228
ETWFE = WooldridgeDiD
229229
DCDH = ChaisemartinDHaultfoeuille
230230

231-
__version__ = "3.0.1"
231+
__version__ = "3.0.2"
232232
__all__ = [
233233
# Estimators
234234
"DifferenceInDifferences",

docs/llms-full.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
> A Python library for Difference-in-Differences causal inference analysis. Provides sklearn-like estimators with statsmodels-style output for econometric analysis.
44

5-
- Version: 3.0.1
5+
- Version: 3.0.2
66
- Repository: https://github.com/igerber/diff-diff
77
- License: MIT
88
- Dependencies: numpy, pandas, scipy (no statsmodels dependency)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "maturin"
44

55
[project]
66
name = "diff-diff"
7-
version = "3.0.1"
7+
version = "3.0.2"
88
description = "Difference-in-Differences causal inference with sklearn-like API. Callaway-Sant'Anna, Synthetic DiD, Honest DiD, event studies, parallel trends."
99
readme = "README.md"
1010
license = "MIT"

rust/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "diff_diff_rust"
3-
version = "3.0.1"
3+
version = "3.0.2"
44
edition = "2021"
55
rust-version = "1.84"
66
description = "Rust backend for diff-diff DiD library"

0 commit comments

Comments
 (0)