You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Demonstrates the now-fully-supported HeterogeneousAdoptionDiD +
did_had_pretest_workflow workflow under SurveyDesign(weights, strata,
psu, fpc) end-to-end on a BRFSS-shape state-rollout panel
(5 strata x 6 PSUs/stratum x 2 states/PSU = 60 states; post-
stratification raking weights with CV ~ 0.30; FPC = 30 PSUs/stratum;
PSU x period interaction shocks injected so cluster correlation
survives DiD first-differencing). Closes the Phase 5 wave 2 second
slice tutorial gap that the survey-strata gate lift unblocked.
Eight sections: motivation; panel + in-notebook helper for attaching
survey columns to a HAD panel; naive vs survey-aware headline fit
with side-by-side ATT/SE/CI table (~10% SE inflation, sign-only
direction asserted); a dedicated discussion of why the SE inflation
is modest for HAD specifically (WAS-d_lower IF concentration at the
boundary vs full-panel regression coefficients); event-study with
sup-t cband under the survey design; pretest workflow on both overall
and event-study paths walking the Phase 4.5 C0 QUG-deferred verdict
suffix and the now-supported stratified-clustered Stute multiplier
bootstrap; communicating-to-leadership two-paragraph template;
Extensions + Summary Checklist surfacing the still-deferred
lonely_psu='adjust' + singleton-strata, replicate-weight designs,
and the permanent QUG-under-survey C0 deferral.
Companion drift-test file (25 tests across 5 groups) locks panel
composition with deterministic exact pins, naive-vs-survey SE
inflation direction (sign-only structural anchor; HAD's IF
concentration caps inflation around 1.10x even at large PSU shock
SD), design auto-detection to continuous_near_d_lower, event-study
cband-vs-pointwise width ordering, _QUG_DEFERRED_SUFFIX substring on
report.verdict for both overall and event-study paths, the distinct
report.summary() QUG-skip note on the event-study path, deterministic
Yatchew sigma2_* pins, and bootstrap p-value tolerance bands at
>= 0.25 abs per feedback_strata_bootstrap_path_divergence.
Cross-surface updates: T20 and T21 Extensions bullets gain
forward-pointers to T22 (T20 also drops the deprecated weights=
kwarg phrasing in favor of survey_design=); practitioner decision
tree HAD universal-rollout and survey sections each gain a tip
cross-link to T22 (adjacent to T20 / T17, not displacing); api/had.rst
gains a Survey-aware fit cross-reference; survey-roadmap.md gains a
Phase 4.5 C HAD Stute Survey Workflow section; bundled llms.txt and
llms-practitioner.txt carry T22 inventory entries; doc-deps.yaml
wires T22 as a dependent of both had.py and had_pretests.py;
REGISTRY closers L2529 + L2577 flipped to shipped; TODO row L115
marked shipped; CHANGELOG carries the new Unreleased Added entry
plus closer flips at the T21 (PR #409) and HAD handlers (PR #402)
queued-tutorial closer lines.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: TODO.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -112,7 +112,7 @@ Deferred items from PR reviews that were not addressed before merge.
112
112
|`HeterogeneousAdoptionDiD` Phase 3 R-parity: Phase 3 ships coverage-rate validation on synthetic DGPs (not tight point parity against `chaisemartin::stute_test` / `yatchew_test`). Tight numerical parity requires aligning bootstrap seed semantics and `B` across numpy/R and is deferred. |`tests/test_had_pretests.py`| Phase 3 | Low |
113
113
|`HeterogeneousAdoptionDiD` Phase 3 nprobust bandwidth for Stute: some Stute variants on continuous regressors use nprobust-style optimal bandwidth selection. Phase 3 uses OLS residuals from a 2-parameter linear fit (no bandwidth selection). nprobust integration is a future enhancement; not in paper scope. |`diff_diff/had_pretests.py::stute_test`| Phase 3 | Low |
|`HeterogeneousAdoptionDiD` Phase 5 follow-up tutorial (T22 weighted/survey HAD tutorial). T21 HAD pretest workflow notebook landed in PR #409; `practitioner_next_steps()` HAD handlers + `llms-full.txt` HeterogeneousAdoptionDiD section + Choosing-an-Estimator row landed in Phase 5 wave 1 (PR #402). |`tutorials/`, `tests/test_t22_*_drift.py`| Phase 2a | Low|
115
+
|`HeterogeneousAdoptionDiD` Phase 5 follow-up tutorial — SHIPPED. T22 (`docs/tutorials/22_had_survey_design.ipynb` + `tests/test_t22_had_survey_design_drift.py`) landed as the follow-up to PR #432; demonstrates the now-supported `SurveyDesign(strata=...)` path through HAD + `did_had_pretest_workflow` end-to-end on a BRFSS-shape household-panel design. T20 HAD brand-campaign (PR #394), T21 HAD pretest workflow (PR #409), and `practitioner_next_steps()` HAD handlers + `llms-full.txt` HeterogeneousAdoptionDiD section + Choosing-an-Estimator row (Phase 5 wave 1, PR #402) landed earlier. |`tutorials/`, `tests/test_t22_*_drift.py`| Phase 2a (shipped) | Done|
116
116
|`HeterogeneousAdoptionDiD` time-varying dose on event study: Phase 2b REJECTS panels where `D_{g,t}` varies within a unit for `t >= F` (the aggregation uses `D_{g, F}` as the single regressor for all horizons, paper Appendix B.2 constant-dose convention). A follow-up PR could add a time-varying-dose estimator for these panels; current behavior is front-door rejection with a redirect to `ChaisemartinDHaultfoeuille`. |`diff_diff/had.py::_validate_had_panel_event_study`| Phase 2b | Low |
117
117
|`HeterogeneousAdoptionDiD` repeated-cross-section support: paper Section 2 defines HAD on panel OR repeated cross-section, but Phase 2a is panel-only. RCS inputs (disjoint unit IDs between periods) are rejected by the balanced-panel validator with the generic "unit(s) do not appear in both periods" error. A follow-up PR will add an RCS identification path based on pre/post cell means (rather than unit-level first differences), with its own validator and a distinct `data_mode` / API surface. |`diff_diff/had.py::_validate_had_panel`, `diff_diff/had.py::_aggregate_first_difference`| Phase 2a | Medium |
118
118
| SyntheticDiD: bootstrap cross-language parity anchor against R's default `synthdid::vcov(method="bootstrap")` (refit; rebinds `opts` per draw) or Julia `Synthdid.jl::src/vcov.jl::bootstrap_se` (refit by construction). Same-library validation (placebo-SE tracking, AER §6.3 MC truth) is in place; a cross-language anchor is desirable to bolster the methodology contract. Julia is the cleanest target — minimal wrapping work and refit-native vcov. Tolerance target: 1e-6 on Monte Carlo samples (different BLAS + RNG paths preclude 1e-10). The R-parity fixture from the previous release was deleted because it pinned the now-removed fixed-weight path. |`benchmarks/R/`, `benchmarks/julia/`, `tests/`| follow-up | Low |
note: "Drift-locks `HAD(design=\"auto\")` resolution to `continuous_at_zero` on T21's panel via `tests/test_t21_had_pretest_workflow_drift.py::test_had_design_auto_lands_on_continuous_at_zero`; changes to `_detect_design()` heuristic should re-validate T21"
394
+
- path: docs/tutorials/22_had_survey_design.ipynb
395
+
type: tutorial
396
+
note: "Survey-aware HAD walkthrough; drift-locked at `tests/test_t22_had_survey_design_drift.py`. Drift-locks `HAD(design=\"auto\")` resolution to `continuous_near_d_lower` on T22's panel and the `survey_design=` path's SE/CI behavior."
Copy file name to clipboardExpand all lines: docs/methodology/REGISTRY.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2526,7 +2526,7 @@ Shipped in `diff_diff/had_pretests.py` as `stute_joint_pretest()` (residuals-in
2526
2526
-**Note:** Horizon labels in `StuteJointResult.horizon_labels` are `str(t)` verbatim and carry STRING IDENTITY ONLY — NOT a chronological ordering key. Callers who need chronological order must preserve the original period values alongside (e.g. from the `pre_periods` / `post_periods` argument).
2527
2527
-**Note:** NaN propagation is explicit: when any horizon has NaN in residuals, `cvm_stat_joint=NaN`, `p_value=NaN`, `reject=False`, AND `per_horizon_stats={label: np.nan for every horizon}` (full dict preserved with NaN values — not empty, not partial).
2528
2528
2529
-
**Phase 3 follow-up delivery:**`stute_joint_pretest()`, `joint_pretrends_test()`, `joint_homogeneity_test()`, `StuteJointResult`, and `did_had_pretest_workflow(aggregate="event_study")` shipped together in PR #353 (2026-04). The `practitioner_next_steps()` HAD handlers landed in Phase 5 wave 1 (PR #402); the T21 HAD pretest workflow tutorial landed in PR #409 (Phase 5 wave 2 first slice). T22 weighted/survey HAD tutorial remains queued.
2529
+
**Phase 3 follow-up delivery:**`stute_joint_pretest()`, `joint_pretrends_test()`, `joint_homogeneity_test()`, `StuteJointResult`, and `did_had_pretest_workflow(aggregate="event_study")` shipped together in PR #353 (2026-04). The `practitioner_next_steps()` HAD handlers landed in Phase 5 wave 1 (PR #402); the T21 HAD pretest workflow tutorial landed in PR #409 (Phase 5 wave 2 first slice). The T22 survey-weighted HAD tutorial (`docs/tutorials/22_had_survey_design.ipynb`) shipped as the follow-up to PR #432 (2026-05).
@@ -2574,7 +2574,7 @@ Shipped in `diff_diff/had_pretests.py` as `stute_joint_pretest()` (residuals-in
2574
2574
-[x] Phase 5 (wave 1, PR #402): `llms-full.txt` HeterogeneousAdoptionDiD section + result-class blocks + `## HAD Pretests` index + Choosing-an-Estimator row landed; constructor / fit() parameter names are regression-locked against `inspect.signature(HeterogeneousAdoptionDiD.__init__)` and `HeterogeneousAdoptionDiD.fit` for parameter-name presence (parameter defaults and the non-return parameter type annotations remain unpinned; the `fit()` return-type union is locked BOTH at the source-code level AND at the test level by `TestFitReturnAnnotation`); result-class field tables enumerate every public dataclass field (regression-tested via `dataclasses.fields()`); `llms-practitioner.txt` Step 4 decision tree distinguishes ContinuousDiD (per-dose ATT(d), needs never-treated) from HeterogeneousAdoptionDiD (WAS, universal-rollout-compatible).
2575
2575
-[x] Phase 5 (partial): README catalog one-liner, bundled `llms.txt``## Estimators` entry, `docs/api/had.rst` (autoclass for the three classes), and `docs/references.rst` citation landed in PR #372 docs refresh.
2576
2576
-[x] Phase 5 (wave 2 first slice, PR #409): T21 HAD pretest workflow tutorial (`docs/tutorials/21_had_pretest_workflow.ipynb`) — composite pre-test walkthrough for `did_had_pretest_workflow`. Uses a `Uniform[$0.01K, $50K]` dose-distribution variant of T20's brand-campaign panel (true support strictly positive but near-zero, chosen so QUG fails-to-reject `H0: d_lower = 0` in finite sample). Walks through `aggregate="overall"` (Steps 1 + 3 only, verdict explicitly flags Step 2 deferral) and upgrades to `aggregate="event_study"` (joint pre-trends Stute + joint homogeneity Stute close the gap). Side panel exercises both `yatchew_hr_test` null modes (`linearity` vs `mean_independence`). Companion drift-test file `tests/test_t21_had_pretest_workflow_drift.py` (16 tests pinning panel composition, both verdict pivots, structural anchors, deterministic stats, bootstrap p-value tolerance bands per backend, and `HAD(design="auto")` resolution to `continuous_at_zero` on this panel).
2577
-
-[] Phase 5 (remaining): T22 weighted/survey HAD tutorial - tracked in `TODO.md`.
2577
+
-[x] Phase 5 (wave 2 second slice): T22 weighted/survey HAD tutorial (`docs/tutorials/22_had_survey_design.ipynb`) - shipped as the follow-up to PR #432. End-to-end walkthrough of `HeterogeneousAdoptionDiD` + `did_had_pretest_workflow` under `SurveyDesign(weights, strata, psu, fpc)` on a BRFSS-shape state-rollout panel (5 strata x 6 PSUs/stratum x 2 states/PSU = 60 states; post-stratification raking weights with CV ~ 0.30; FPC = 30 PSUs/stratum). Companion drift-test file `tests/test_t22_had_survey_design_drift.py` (25 tests pinning panel composition, naive-vs-survey SE inflation direction, design auto-detection, event-study cband-vs-pointwise width ordering, `_QUG_DEFERRED_SUFFIX` substring on `report.verdict` for both overall and event-study paths, the distinct `report.summary()` QUG-skip note on the event-study path, deterministic Yatchew sigma2_*, and bootstrap p-value tolerance bands per `feedback_strata_bootstrap_path_divergence` (>= 0.25 abs)).
2578
2578
-[ ] Documentation of non-testability of Assumptions 5 and 6.
2579
2579
-[ ] Warnings for staggered treatment timing (redirect to `ChaisemartinDHaultfoeuille`).
2580
2580
-[ ]`NotImplementedError` phase pointer when `covariates=` is passed (Theorem 6 future work).
Copy file name to clipboardExpand all lines: docs/tutorials/20_had_brand_campaign.ipynb
+1-1Lines changed: 1 addition & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -388,7 +388,7 @@
388
388
"\n",
389
389
"This tutorial covered HAD's headline workflow: the overall WAS_d_lower fit and the multi-week event study. The library also supports several extensions we did not demonstrate here.\n",
390
390
"\n",
391
-
"- **Population-weighted (survey-aware) inference**: when some markets or regions carry more weight than others - e.g., DMAs weighted by population - HAD accepts a `weights=` array or a `SurveyDesign` object on the same `fit()` interface.\n",
391
+
"- **Population-weighted (survey-aware) inference**: when some markets or regions carry more weight than others - e.g., DMAs weighted by population - HAD accepts a `SurveyDesign` object on the same `fit()` interface (the deprecated `weights=` and `survey=` kwarg aliases will be removed in the next minor release; use `survey_design=` going forward). [Tutorial 22](22_had_survey_design.ipynb) walks the BRFSS-shape survey-design path end-to-end including the pretest workflow.\n",
392
392
"- **Composite pretest workflow**: HAD ships a `did_had_pretest_workflow` that combines the QUG support-infimum test (`H0: d_lower = 0`, which adjudicates between the `continuous_at_zero` and `continuous_near_d_lower` design paths) with linearity tests (Stute and Yatchew-HR). On the two-period (`aggregate='overall'`) path this workflow checks QUG and linearity only; the parallel-trends step is closed by the multi-period (`aggregate='event_study'`) joint variants (`stute_joint_pretest`, `joint_pretrends_test`, `joint_homogeneity_test`). The visual placebo check we used in Section 4 is a parallel-trends sanity check, not a substitute for the formal joint pretests; see [Tutorial 21](21_had_pretest_workflow.ipynb) for an end-to-end pretest walkthrough.\n",
393
393
"- **`continuous_at_zero` design path**: if the lightest-touch DMA had no regional add-on (spend exactly $0), HAD switches to the Design 1' identification path with target `WAS` instead of `WAS_d_lower`. The auto-detection picks it up.\n",
394
394
"- **Mass-point design path**: if a meaningful chunk of DMAs sit at exactly the same minimum spend (rather than spread continuously near the boundary), HAD switches to a 2SLS estimator with matching identification logic. Auto-detected as well.\n",
0 commit comments