feat(callaway): allow_unbalanced_panel — RC-on-panel parity with R did#631
feat(callaway): allow_unbalanced_panel — RC-on-panel parity with R did#631igerber wants to merge 1 commit into
Conversation
|
Overall Assessment Executive Summary
Methodology P1 — Missing panel-structure validation before RC routing P3 — Documented deviations, no action required Code Quality P3 — Stale/unclear inline comment Performance No findings. Maintainability P3 — Existing event-study/bootstrap bucket duplication remains tracked Tech Debt P3 — Deferred covariate/IPW/DR parity is tracked Security No findings. Documentation/Tests P1 — Add tests for invalid panel shapes on the new route Path To Approval
|
70b017b to
f8c25c9
Compare
|
🔁 AI review rerun (requested by @igerber) Head SHA: Overall Assessment ✅ Looks good Executive Summary
Methodology P3 — Documented deviations, no action required P3 — Prior panel-validation methodology issue appears resolved Code Quality P2 — New routing validation precedes required-column checks and normalization Performance No findings. Maintainability P3 — Remaining aggregation bucket duplication is tracked Tech Debt P3 — Deferred survey and covariate/IPW/DR parity work is tracked Security No findings. Documentation/Tests No blocking findings. The added tests cover R golden parity, fixed unit-cohort weighting across simple/event-study/group, bootstrap weighting, metadata, default warning, survey fail-closed behavior, and the three prior invalid-panel-shape cases. See Verification note: attempted |
f8c25c9 to
0ece407
Compare
|
🔁 AI review rerun (requested by @igerber) Head SHA: Overall Assessment Executive Summary
Methodology P1 — RC routing misses complete-case unbalanced panels Code Quality P2 — Routing validation still precedes numeric normalization Performance No findings. Maintainability P3 — Remaining event-study bucket duplication is tracked Tech Debt P3 — Deferred survey and covariate/IPW/DR parity work is tracked Security No findings. Documentation/Tests P2 — Missing test for NaN-outcome-triggered unbalance Path to Approval
|
0ece407 to
b34c750
Compare
|
🔁 AI review rerun (requested by @igerber) Head SHA: Overall Assessment Executive Summary
Methodology P1 — RC-on-panel routes on complete-case balance but estimates on unfiltered rows Code Quality No findings beyond the methodology issue above. Performance No findings. Maintainability No findings. Tech Debt P3 — Deferred survey and covariate/IPW/DR parity work is tracked Security No findings. Documentation/Tests P2 — NaN-outcome regression test does not assert complete-case estimation Path to Approval
|
b34c750 to
93fa908
Compare
Adds CallawaySantAnna(allow_unbalanced_panel=True) matching R did::att_gt(allow_unbalanced_panel=TRUE) on unbalanced panels. When set and the input panel is unbalanced, the pooled observations route through the repeated-cross-section levels estimator (DRDID::reg_did_rc) instead of the default within-cell panel differencing (a different estimand on unbalanced data), and the per-observation influence function is clustered by the original unit. ATT matches R bit-for-bit (cells AND dynamic aggregation): the RC cohort mass and the aggregation pg use fixed UNIT cohort mass (not observation count), and the per-unit WIF divides the per-observation weight-influence-function by each unit's observation count so the unit-clustered sum is not over-counted. The analytical SE matches R up to the documented CR1 sqrt(G/(G-1)) finite-sample factor (diff-diff's cluster path applies the Bessel correction R's att_gt getSE omits). The bootstrap resamples by unit via the same PSU. Inert on balanced panels (byte-identical to the default). Independently, the default path now emits a UserWarning on unbalanced input (previously silent). survey_design= with the flag raises NotImplementedError (deferred). Routing engages only when the panel is actually unbalanced (route-as-RC via an internal flag threaded through the six self.panel sites; the unique-unit guard is naturally bypassed since panel stays True). Verified vs R did 2.5.1 (benchmarks/data/cs_unbalanced_golden.json + TestAllowUnbalancedPanel); 499 CS tests pass (RC / survey / panel paths unchanged). The TODO's "weighting" framing was a mis-diagnosis: the dominant unbalanced divergence is the estimator choice. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0178r8ZK6VRiXbaBiknrWjQB
93fa908 to
b0c0e7d
Compare
Summary
CallawaySantAnna(allow_unbalanced_panel=True)— parity with Rdid::att_gt(allow_unbalanced_panel=TRUE)on unbalanced panels. When the flag is set and the input panel is unbalanced (some units unobserved in some periods), the pooled observations route through the repeated-cross-section levels estimator (DRDID::reg_did_rc) instead of the default within-cell panel differencing (a different estimand on unbalanced data), and the per-observation influence function is clustered by the original unit.pg = n_g/N(not observation count), via a single sharedfixed_cohort_agg_weights()helper used by the analytical simple/event-study paths and the multiplier bootstrap; the per-unit WIF divides the per-observation weight-influence-function by each unit's observation count so the unit-clustered sum is not over-counted.sqrt(G/(G-1))finite-sample factor (diff-diff's cluster path applies the Bessel correction R'satt_gtgetSEomits — same convention class as the fixest cluster band).pre_process_didrecomputes balance and keepspanel=TRUEon balanced input under the flag (verified). Independently, the default path now emits aUserWarningon unbalanced input (previously silent — a no-silent-failures fix) pointing to the flag.NaNoutcome routes to RC and estimates on the row-dropped sample (bit-identical to structural removal), not materializing the cell as non-estimable.(unit, period)rows, time-invariant treatment cohort and cluster per unit — fail-closed, since the RC precompute reads cohort/cluster per observation.allow_unbalanced_panel+used_rc_on_unbalanced_paneland labels the effectivecluster_name="unit", so downstream can distinguish the RC-on-panel estimand from the default.survey_design=combined with the flag raisesNotImplementedError(per-obs vs per-unit weight resolution deferred).Methodology references (required if estimator / math changes)
CallawaySantAnnaunbalanced-panel handling; Rdid::att_gt(allow_unbalanced_panel=TRUE)→panel=FALSE→DRDID::reg_did_rc(repeated-cross-section levels estimator); dynamic/simple/group aggregationpg = n_g/Nweighting + weight-influence-function correction.did2.5.1. Seedocs/methodology/REGISTRY.md(CallawaySantAnna section).sqrt(G/(G-1))finite-sample factor that R'satt_gtgetSEomits (documented Deviation from R; vanishes as G→∞) — the ATT and the routing/complete-case/validation behavior otherwise match R; (2)survey_design=× flag and covariate/ipw/dr × flag R-parity are deferred (fail-closed / TODO).Validation
tests/test_csdid_ported.py::TestAllowUnbalancedPanel(15 tests: per-cell + simple/event-study/group parity vs R, bootstrap-by-unit, user-cluster honored,survey_design=fail-closed, balanced-inert byte-identity, default-path warning, result metadata, panel-structure validation [duplicate rows / changing cohort / time-varying cluster], NaN-outcome complete-case routing [== structural removal], coerciblefirst_treat). New R goldenbenchmarks/data/cs_unbalanced_golden.json(generatorbenchmarks/R/generate_cs_unbalanced_golden.R), verified vs Rdid2.5.1. 587 CS tests pass (RC / survey / panel / triple-diff paths unchanged).Security / privacy
🤖 Generated with Claude Code
https://claude.ai/code/session_0178r8ZK6VRiXbaBiknrWjQB