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
feat(had): cluster-robust SE on the continuous paths (Phase 2a)
Thread cluster= into bias_corrected_local_linear on the continuous designs
(continuous_at_zero / continuous_near_d_lower). Previously cluster= was ignored
on the continuous path with a UserWarning; the Phase-1c wrapper already supports
cluster, so the estimator just needed to pass it through. The CCT-2014 robust
variance becomes cluster-robust and the beta-scale SE is se_robust / |den|.
- Resolve the per-unit cluster array for the continuous designs (was mass-point
only) and thread it into _fit_continuous -> bias_corrected_local_linear.
- Composes with the weights= shortcut (weighted cluster-robust).
- cluster= + survey_design= raises NotImplementedError (rejected BEFORE cluster
extraction so the error is predictable even with a malformed cluster column;
the Binder-TSL survey variance would override the cluster-robust SE -- route
clustering through survey_design=SurveyDesign(psu=<col>)).
- Cluster must be unit-constant: a nonexistent column, NaN, or within-unit-
varying cluster now raises (mirrors the mass-point path) instead of being
silently ignored.
- Single-cluster guard at the variance-computation site: _nprobust_port.lprobust
NaNs se_rb/se_cl when fewer than two clusters fall in the ACTIVE KERNEL WINDOW
(eC = cluster[ind]) -- a stricter condition than the global cluster count,
since clusters can be separated from the boundary by the bandwidth. This
NaN-couples the downstream t-stat / p-value / CI (att stays finite), matching
the mass-point CR1 single-cluster contract, and also covers the direct
bias_corrected_local_linear API. A window with >=2 clusters is bit-identical,
so the nprobust clustered DGP-4 golden parity is preserved.
- Result metadata reports vcov_type="cr1" + cluster_name; inference_method
stays "analytical_nonparametric".
- Event-study (Phase 2b) cluster threading remains a follow-up (the per-horizon
cband sup-t bootstrap would mix variance families under clustering); that path
still emits the "cluster ignored" warning.
Validated: the estimator clustered SE equals the direct
bias_corrected_local_linear(cluster=...).se_robust / |den| to machine precision,
unweighted and weighted; single/degenerate-cluster fits NaN the inference triple.
REGISTRY note + CHANGELOG + TODO (Phase 2b remains) updated.
Co-Authored-By: Claude Opus 4.8 (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
@@ -37,7 +37,7 @@ The `Origin` column (Actionable tables) and the `PR` column (Deferred tables) bo
37
37
|`TwoWayFixedEffects(vcov_type in {hc2, hc2_bm})` with replicate-weight designs raises `NotImplementedError` (`twfe.py:~233`). The replicate path re-demeans per replicate, which doesn't compose with the full-dummy HC2/HC2-BM build — a correct impl needs per-replicate full-dummy refit. Workaround: `hc1` for replicate-weight CR1. |`twfe.py::fit`| follow-up | Heavy | Low |
38
38
| TWFE's HC2/HC2-BM inline full-dummy build (`twfe.py:280-315`) duplicates the dummy-construction logic in `DifferenceInDifferences(fixed_effects=...)` (`estimators.py:478-486`). Extract a shared helper, or delegate TWFE's HC2/HC2-BM path to DiD's `fixed_effects=` branch (with TWFE-specific cluster-default threading), to reduce drift risk on FE naming / survey behavior / result-surface conventions. Substantive refactor — touches both estimators. |`twfe.py::fit`, `estimators.py::DifferenceInDifferences.fit`| follow-up | Heavy | Low |
39
39
| Decide whether to formally deprecate `CallawaySantAnna.cluster=X` in favor of `survey_design=SurveyDesign(psu=X)` (the bare-cluster path already synthesizes a minimal SurveyDesign). Two equivalent paths = redundant surface. Mirrors the question for ImputationDiD / EfficientDiD / TwoStageDiD. |`staggered.py`| follow-up | Mid | Low |
40
-
|`HeterogeneousAdoptionDiD` continuous paths: thread `cluster=`through`bias_corrected_local_linear` (the Phase-1c wrapper already supports cluster; Phase 2a ignores it with a `UserWarning`). |`had.py`, `local_linear.py`| Phase 2a| Mid | Low |
40
+
|`HeterogeneousAdoptionDiD`**event-study (Phase 2b)**continuous cluster= threading: Phase 2a static path now threads `cluster=`into`bias_corrected_local_linear` (cluster-robust CCT SE, unweighted + weighted). The per-horizon event-study path still ignores `cluster=` with a `UserWarning` because the `cband` sup-t bootstrap normalizes HC-scale perturbations by the analytical SE and would mix variance families under clustering (mirrors the mass-point `weights= + cluster= + cband=True``NotImplementedError`). Needs a per-horizon clustered-bootstrap variance-family reconciliation. |`had.py::_fit_event_study`| Phase 2b| Mid | Low |
41
41
|`SpilloverDiDResults` not registered in `DiagnosticReport`'s `_APPLICABILITY` / `_PT_METHOD` tables, so `DiagnosticReport(spillover_result)` doesn't route to event-study diagnostics. Decide which diagnostics apply (PT, pre-trends power, heterogeneity, design-effect) and add an end-to-end test. |`diagnostic_report.py`| Wave C | Mid | Low |
0 commit comments