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
fix(inference): fail closed to NaN on non-finite degrees of freedom
The CR2/Bell-McCaffrey guard added in the previous commit can surface a
non-finite (NaN) Satterthwaite DOF for a high-leverage / collinear
nuisance coefficient. Two inference paths did not treat that as
undefined:
- `safe_inference()` rejected `df <= 0` but let a NaN `df` through
(`NaN <= 0` is False), computing a finite t-stat with a NaN p-value /
CI - an inconsistent, partially-finite inference tuple. It now returns
all-NaN for any non-finite `df`.
- `MultiPeriodDiD(vcov_type="hc2_bm")` fell back to the shared residual
`df` for a period effect whose per-coefficient BM DOF was non-finite,
reporting finite t/p/CI for a coefficient whose DOF was declared
unreliable. It now passes the BM DOF through unconditionally on the
hc2_bm path, so a non-finite DOF fails closed via `safe_inference`. The
post-period-average and SunAbraham contrast paths already route their
DOF through `safe_inference`, so the central guard covers them too.
Only coefficients whose BM DOF is non-finite are affected; well-
conditioned treatment / event-study / average contrasts are unchanged.
Also rewrites the REGISTRY `DID_M^pl` placebo formula to display the
backward-difference x switch-direction convention (`stable0_avg -
joiner_avg` / `leaver_avg - stable1_avg`) so it matches the code and the
adjacent sign note.
Regression tests: non-finite `df` in `safe_inference`; monkeypatched
NaN BM DOF failing closed on user-facing MPD period and average contrasts.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0178r8ZK6VRiXbaBiknrWjQB
Single-lag placebo (AER 2020 placebo specification, same section as Theorem 3) — applies the same Theorem 3 logic to `Y_{g,t-1} - Y_{g,t-2}` on cells with 3-period histories:
747
+
Single-lag placebo (AER 2020 placebo specification, same section as Theorem 3) — applies the same Theorem 3 logic to the pre-period first difference on cells with 3-period histories. Writing `d_a(cells) = mean over `cells` of (Y_{g,t-1} - Y_{g,t-2})` for the pre-period forward first difference:
The per-side terms are the code's `placebo_plus_t = stable0_avg - joiner_avg` and `placebo_minus_t = leaver_avg - stable1_avg` — the backward-difference × switch-direction convention of the sign Note below (equivalently, `S_g · (Y_{g,t-2} - Y_{g,t-1})_switcher` minus the same for its stable controls, matching `_compute_multi_horizon_placebos` and R).
757
+
756
758
**Note (sign convention):** the reported `placebo_effect` uses the **backward-difference × switch-direction** convention of the multi-horizon placebo path (`_compute_multi_horizon_placebos`: `switcher_change - ctrl_avg` with `Y_bwd - Y_ref`, times `S_g = +1` joiners / `-1` leavers) and R `did_multiplegt_dyn`. In the phase-1 (`L_max=None`) code this is `placebo_plus_t = stable0_avg - joiner_avg` (joiners) and `placebo_minus_t = leaver_avg - stable1_avg` (leavers). Prior to this the phase-1 path used the opposite (forward-difference) order, so `placebo_effect` was **sign-flipped vs R** on pure-direction panels (magnitude bit-identical); the multi-horizon path was always correct. Pinned by `tests/test_chaisemartin_dhaultfoeuille_parity.py::TestDCDHDynRParity::test_parity_{joiners,leavers}_only`. On **mixed-direction** panels the placebo magnitude additionally carries the documented period-vs-cohort stable-control-set / equal-cell-weighting deviation (see the `**Note (deviation from R DIDmultiplegtDYN):**` above), so it is not gate-asserted against R there.
757
759
758
760
*Phase 2: Multi-horizon event study (Equation 3 and 5 of the dynamic companion paper):*
0 commit comments