refactor(utils): shared FE-dummy design build across DiD/MPD/TWFE#655
refactor(utils): shared FE-dummy design build across DiD/MPD/TWFE#655igerber wants to merge 1 commit into
Conversation
PR ReviewOverall Assessment✅ Looks good — no unmitigated P0/P1 findings. Executive Summary
MethodologyNo findings. The changed paths still build the documented full-dummy FE design and preserve the registry’s dummy-name collision contract: Code Quality
PerformanceNo findings. The DiD/MPD change removes repeated per-column MaintainabilityNo findings beyond the P2 helper validation item above. Tech DebtNo findings. The removed TODO row corresponds to the refactor implemented here: SecurityNo findings. No secrets, file IO, network calls, or unsafe execution surfaces are introduced. Documentation/TestsNo blocking findings. The new utility tests cover name parity with |
e6ef7fe to
9fa2ba6
Compare
The drop-first pd.get_dummies design construction existed as three inline copies — the DifferenceInDifferences and MultiPeriodDiD fixed_effects= loops and the TwoWayFixedEffects HC2/HC2-BM full-dummy path — whose FE naming / dtype / column-order conventions could drift independently (the drift risk the TODO row flagged). All three now delegate to one diff_diff.utils.build_fe_dummy_blocks helper whose names match fe_dummy_names (the reserved-name collision guard) by construction. Outputs bit-identical (A/B vs pristine origin/main on DiD with non-default-order Categorical FE + covariates, TWFE hc2 + hc2_bm, MultiPeriodDiD multi-FE incl. the fe==time skip); the DiD/MPD paths also drop the per-column np.column_stack accumulation for one block stack. Contract tests lock names==fe_dummy_names (plain, Categorical non-default order, numeric) and values==get_dummies. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
9fa2ba6 to
30e8b74
Compare
Summary
pd.get_dummiesdesign construction existed as three inline copies — theDifferenceInDifferencesandMultiPeriodDiDfixed_effects=loops and theTwoWayFixedEffectsHC2/HC2-BM full-dummy path — whose FE naming / dtype / column-order conventions could drift independently. All three now delegate to onediff_diff.utils.build_fe_dummy_blockshelper whose names matchfe_dummy_names(the reserved-name collision guard) by construction. The row offered a bigger alternative (delegate TWFE's path to DiD's branch entirely); the shared-helper option was chosen for the smaller blast radius.origin/mainworktree (backend pinned) on DiD with a non-default-order Categorical FE + covariates, TWFEhc2+hc2_bm, and MultiPeriodDiD multi-FE including thefe == timeskip — every ATT/SE/coefficient-dict/vcov-sum identical. Side benefit: the DiD/MPD paths drop the per-columnnp.column_stackaccumulation (O(k²) copies) for one block stack.fe_dummy_names(plain / Categorical-with-non-default-order / numeric FEs) and values==get_dummieswith float64 dtype. Impacted suites green (416: estimators, vcov-type, utils).Methodology references (required if estimator / math changes)
Validation
tests/test_utils.py::TestBuildFeDummyBlocks(2 contract tests).Security / privacy
Generated with Claude Code