Repository navigation
feat(v4): item 8b of the 4.0 cut - unified to_dataframe(level='event_study') schema on every results class (M-093 done) - #883
Conversation
…study') schema on every results class (M-093 done)
Since 4.0 every results class's to_dataframe(level="event_study") emits
the EVENT_STUDY_SCHEMA columns (event_time, att, se, t_stat, p_value,
conf_int_lower, conf_int_upper, cband_lower, cband_upper, n, df,
is_reference, estimand) as an identical PREFIX - the frame the unified
EventStudyResults container returns. The routing rule is the M-153 LWDiD
precedent: the AggregationMixin classes (CallawaySantAnna/DMLDiD,
EfficientDiD, ImputationDiD, TwoStageDiD, StackedDiD, ContinuousDiD,
TripleDifference staggered, ChaisemartinDHaultfoeuille, HAD event-study,
DurationDiD) return aggregate("event_study").to_dataframe(); SunAbraham,
LPDiD, WooldridgeDiD and SpilloverDiD return
build_event_study_surface(self).to_dataframe(). WooldridgeDiD appends
cohort_trends / aggregation_weights after the schema (LWDiD's three
config columns are the shipped M-153 precedent); spec section 5 states
the prefix rule in the same diff.
The 3.x bespoke frames are gone: relative_period -> event_time; effect /
att_glob / LPDiD's coefficient -> att; conf_int_lo/hi, conf_low/high and
HAD's conf_int_low/high -> conf_int_lower/upper; n_obs -> n (NaN on the
is_reference row, never a zero count); the dCDH native frame (horizon,
a per-horizon estimand label DID^pl_l / "ref" / DID_l, the n_obs 0
reference row) is retired - event_time on l1_first_switch, estimand
"att", is_reference at 0. The "not stored on 4.0 fits" raises on
CallawaySantAnna / EfficientDiD / TripleDifference and on analytical
ImputationDiD / TwoStageDiD / ContinuousDiD are gone: the level
recomputes from the retained kit (bootstrapped CS/EDiD/DDD fits replay
the fit-time bootstrap on every call); a kit-less or pre-replay legacy
pickle that still carries a stored dict now raises where it returned a
frame. WooldridgeDiD before aggregate("event_study", weights=) raises
instead of returning an empty frame (the weighting scheme is never
chosen silently). New levels: SpilloverDiDResults.to_dataframe(level=
"simple" | "event_study") (default "simple" = the former one-row frame;
aggregate-mode fits raise), HeterogeneousAdoptionDiDEventStudyResults
.to_dataframe(level="event_study"), DurationDiDResults "event_study".
level="group" on CS/EDiD/TripleDiff still raises with the post-fit
pointer (TODO.md row). results_base._ABSENT_SURFACE_HINTS gains a
SunAbrahamResults entry.
Tests: new tests/test_v4_event_study_schema.py (M-093 test_ref) pins the
schema prefix and trailing config columns on 21 producer fits, bit-equality
with the aggregate / builder route (bootstrap relay and replay routes
included), the default-level classes, reference / estimand / count spot
checks per producer, the error contract (Wooldridge pre-aggregate,
Spillover aggregate mode, 2x2x2 DDD, LPDiD absent and empty frames,
Stacked and kit-less legacy pickles, the new bad-level texts) and the
ledger row. tests/test_v4_sentinel_retirement.py keeps the 8a pins (its
frame pin and ledger pin updated); the per-class frame pins in the
stacked, sun_abraham, continuous, wooldridge, lpdid, dCDH, had, staggered,
imputation, efficient, two_stage, staggered_triple_diff, unify_ddd and
storage_flip suites move to the unified schema.
Ledger: M-093 -> done (test_ref tests/test_v4_event_study_schema.py,
tenth amendment note; results.py covers SpilloverDiD only). Spec item 8
complete. REGISTRY (CS, EfficientDiD, Imputation/TwoStage/Stacked relay
and reference-row Notes, DDD, HAD; new to_dataframe-schema Notes for
dCDH, SunAbraham, WooldridgeDiD, LPDiD, DurationDiD, SpilloverDiD), the
migration guide, llms-full.txt, the practitioner guide,
docs/api/wooldridge_etwfe.rst and docs/api/spillover.rst describe the
frame. Notebook 16 re-executed under the Rust backend (outputs refreshed;
no markdown restates a moved number - it leaves the TODO.md tutorial
re-execution row), notebook 19 reads event_time / att / is_reference,
notebook 14's export prose updated; 13/14/16/19/20 nbmake-validated.
Overall assessment: ✅ Looks goodNo unmitigated P0 or P1 findings identified. Executive summary
MethodologyAffected exports: CallawaySantAnna/DMLDiD, EfficientDiD, ImputationDiD, TwoStageDiD, StackedDiD, ContinuousDiD, TripleDifference, dCDH, HAD, DurationDiD, SunAbraham, LPDiD, WooldridgeDiD, and SpilloverDiD. Cross-checked the routes against Registry entries, docstrings, and available checked-in paper reviews. No undocumented methodological change, missing assumption check introduced by this diff, or incorrect inference transformation was identified.
Code QualityNo findings. Delegation removes duplicated export logic, and no new inline inference computations or partial NaN guards were introduced. Performance
MaintainabilityNo findings. Shared builders enforce column ordering and reference normalization consistently. Tech Debt
SecurityNo findings in the changed code or supplied notebook content. Documentation/TestsNo actionable findings. The changelog fragment, migration guide, Registry notes, and schema/error-contract tests accompany the behavior change. All 32 changed Python files parsed with Python 3.9 grammar and compiled in memory. Code cells in all three changed notebooks compiled successfully. These checks do not establish runtime or numerical correctness; the reported full-suite results could not be independently reproduced in this environment. |
Summary
done: every results class'sto_dataframe(level="event_study")emits the unifiedEVENT_STUDY_SCHEMA(event_time, att, se, t_stat, p_value, conf_int_lower, conf_int_upper, cband_lower, cband_upper, n, df, is_reference, estimand) as an identical PREFIX. Routing follows the M-153 LWDiD precedent: theAggregationMixinclasses (CallawaySantAnna/DMLDiD,EfficientDiD,ImputationDiD,TwoStageDiD,StackedDiD,ContinuousDiD,TripleDifferencestaggered,ChaisemartinDHaultfoeuille, HAD event-study,DurationDiD) returnaggregate("event_study").to_dataframe();SunAbraham,LPDiD,WooldridgeDiDandSpilloverDiDreturnbuild_event_study_surface(self).to_dataframe().WooldridgeDiDappendscohort_trends/aggregation_weightsafter the schema (LWDiD's three config columns are the shipped precedent);docs/v4-design.mdsection 5 states the prefix rule in the same diff.relative_period->event_time;effect/att_glob/ LPDiD'scoefficient->att;conf_int_lo/hi,conf_low/high, HAD'sconf_int_low/high->conf_int_lower/upper;n_obs->n, NaN on theis_referencerow; dCDH'shorizon/ per-horizonestimandlabels /n_obs 0reference row ->event_timeonl1_first_switch,estimand "att",is_referenceat 0). The "not stored on 4.0 fits" raises onCallawaySantAnna/EfficientDiD/TripleDifferenceand analyticalImputationDiD/TwoStageDiD/ContinuousDiDare gone - the level recomputes from the retained kit (bootstrapped CS/EDiD/DDD fits replay the fit-time bootstrap per call; kit-less or pre-replay legacy pickles that still carry a stored dict now raise where they returned a frame).WooldridgeDiDbeforeaggregate("event_study", weights=)raises instead of returning an empty frame. New levels:SpilloverDiDResults.to_dataframe(level="simple" | "event_study"),HeterogeneousAdoptionDiDEventStudyResults.to_dataframe(level="event_study"),DurationDiDResults"event_study".level="group"on CS/EDiD/TripleDiff still raises (TODO.md row).donewithtest_reftests/test_v4_event_study_schema.pyand a tenth amendment note; spec item 8 complete. REGISTRY (CS, EfficientDiD, Imputation/TwoStage/Stacked relay and reference-row Notes, DDD, HAD; new to_dataframe-schema Notes for dCDH, SunAbraham, WooldridgeDiD, LPDiD, DurationDiD, SpilloverDiD), the migration guide,llms-full.txt, the practitioner guide,docs/api/wooldridge_etwfe.rstanddocs/api/spillover.rstupdated. Notebook 16 re-executed under the Rust backend (outputs refreshed; no markdown restates a moved number; it leaves the TODO.md tutorial re-execution row), notebook 19 readsevent_time/att/is_reference, notebook 14's export prose updated.Methodology references (required if estimator / math changes)
to_dataframe(level="event_study")frame; spec section 5, ledger row M-093).docs/v4-design.mdsection 5 (unified event-study representation; the prefix rule amended here); REGISTRY to_dataframe-schema Notes.aggregate("event_study")/ builder frames.Validation
tests/test_v4_event_study_schema.py(M-093test_ref: schema prefix + trailing config columns on 21 producer fits incl. bootstrap relay/replay routes, bit-equality with the aggregate / builder route, default-level classes, reference / estimand / count spot checks, the error contract - Wooldridge pre-aggregate, Spillover aggregate mode, 2x2x2 DDD, LPDiD absent/empty frames, Stacked and kit-less legacy pickles, new bad-level texts - and the ledger row).tests/test_v4_sentinel_retirement.pykeeps the 8a pins (frame + ledger pins updated). Per-class frame pins moved to the unified schema intest_stacked_did.py,test_sun_abraham.py,test_continuous_did.py,test_wooldridge.py,test_lpdid.py,test_chaisemartin_dhaultfoeuille.py,test_had.py,test_staggered.py,test_imputation.py,test_efficient_did.py,test_two_stage.py,test_staggered_triple_diff.py,test_v4_unify_ddd.py,test_v4_storage_flip.py.docs/tutorials/16_wooldridge_etwfe.ipynbre-executed (DIFF_DIFF_BACKEND=rust); 13/14/16/19/20 nbmake-validated; t19/t20 drift suites green. Full default suite green; CI-pinned mypy clean; py3.9 compile clean.Security / privacy
Changelog
changelog.d/20261031-v4-pr8b-m093-event-study-schema.md)