Commit df7a0a3
Round 3: ragged panels, validation refactor, metadata fixes
Addresses two new P1s, two P2s, and two P3s from CI re-review:
P1: ragged panel handling
- Add fit() Step 5a validation: reject groups missing the first global
period with a clear ValueError listing offenders, and drop groups with
interior period gaps with an explicit UserWarning. Eliminates the
silent NaN propagation in _compute_cohort_recentered_inputs that
could crash the cohort enumeration with int(np.nan) or misclassify
late-entry groups' baselines.
- Fix singleton-baseline computation in fit() Step 7 to read the
validated first global period explicitly instead of using
groupby.first() which returned the first OBSERVED row per group.
- Add defensive presence-gating in _compute_cohort_recentered_inputs:
the helper now refuses to run if N_mat[:, 0] has any zero entries
(a fit() validation regression tripwire), and the first-switch
detection loop only counts transitions between adjacent OBSERVED
periods. Even with fit() validation in place, the helper is now
safe to call directly.
- Add 2 regression tests: test_missing_baseline_period_raises_value_error
and test_interior_gap_drops_group_with_warning.
P1: twowayfeweights validation refactor
- Extract _validate_and_aggregate_to_cells helper enforcing the dCDH
validation contract (NaN treatment / NaN outcome / non-binary
treatment all raise ValueError; within-cell varying treatment
emits UserWarning before majority rounding).
- Both fit() and twowayfeweights() now call the helper. Single source
of truth for the validation rules, no drift between the two public
entry points.
- Add 4 regression tests for twowayfeweights() validation:
test_twowayfeweights_rejects_nan_treatment,
test_twowayfeweights_rejects_nan_outcome,
test_twowayfeweights_rejects_non_binary_treatment,
test_twowayfeweights_warns_on_within_cell_rounding.
P2: joiner/leaver metadata
- Fix n_joiner_cells = int(n_10_t_arr.sum()) (was count_nonzero counting
PERIODS, not cells). Same for n_leaver_cells.
- Compute n_joiner_obs and n_leaver_obs as actual observation counts
(sum of n_gt over the joiner/leaver cells across periods), not as
cell totals. For balanced one-obs-per-cell panels they equal
n_*_cells; for individual-level inputs with multiple obs per cell
they can be larger. Update results dataclass docstrings.
P2: parity tests run on JSON without R
- Decouple golden_values fixture from require_r_dcdh. Tests now run
whenever the JSON file exists. R is only needed to regenerate the
JSON via benchmarks/R/generate_dcdh_dynr_test_values.R.
- Verified by running DIFF_DIFF_R=skip pytest tests/test_chaisemartin_dhaultfoeuille_parity.py
— all 5 parity scenarios PASS (was previously skipping entirely).
P3: summary() label rename
- Rename "Groups dropped before estimation:" to "Group filter /
metadata counts:". Label never-switching as "(reported, not dropped)".
Reflects the Round 2 change where never-switching groups participate
in the variance via stable-control roles.
P3: CHANGELOG/ROADMAP consistency
- Remove the CHANGELOG bullet that claimed three paper review files
were committed under docs/methodology/papers/. Replace with a
reference to REGISTRY.md as the canonical methodology surface
(matching the ROADMAP wording).
Tests: 103 dCDH passing (97 + 6 new). Worked example DID_M = 2.5 still
exact. Pure-direction R parity tests still pass at 1e-4 / 5% rtol.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>1 parent 0d70f4d commit df7a0a3
5 files changed
Lines changed: 417 additions & 108 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
22 | | - | |
| 22 | + | |
23 | 23 | | |
24 | 24 | | |
25 | 25 | | |
| |||
0 commit comments