From 854e8f37767e6fab841b2d46178b897dd46f0443 Mon Sep 17 00:00:00 2001 From: igerber Date: Sun, 1 Nov 2026 14:06:53 -0500 Subject: [PATCH] feat(v4): item 9b of the 4.0 cut - retire the 5.0-window shims at 4.0 and give the placebo wrappers the calendar-time contract (M-016, M-050..M-058, M-148, M-149, M-151, M-152 removed; M-156, M-157 done) Shim retirement (user decision 2026-11-01: no 4.x debt). The 14 ledger rows scheduled for a 5.0 removal flip to `removed` at 4.0. None of their FutureWarning shims had reached a release (they landed on main after the v3.13.0 tag), so the released 3.x names break at 4.0 without a warned release - stated plainly in the migration guide, the ledger notes and the spec amendments. The overall_* / overall_att_* FutureWarning properties are deleted from the nine results classes and the DMLDiDResults re-declaration (AttributeError now; the canonical att/se/t_stat/p_value/conf_int are the only vocabulary; the __setstate__ legacy-key migration stays so 3.x pickles load); EventStudyResults.period_effects, _PeriodEffectView and the leaf-local stars helper are gone from results_base.py (to_dataframe() is the route); SyntheticControl(inner_max_iter=, inner_min_decrease=) are removed (TypeError; positional slots after them shift up two - the DeprecationWarning DID ship in 3.13.0, so deprecated_in is corrected to 3.13.0); LWDiDResults.pvalue / .ci are gone. diff_diff/_deprecation.py is reduced to the pickle-migration (LEGACY_OVERALL_QUINTET, migrate_renamed_state_keys, migrate_event_study_reference_sentinel) and required-arg (NOT_SUPPLIED, require_arg) helpers; deprecated_field_property, resolve_renamed_kwarg, warn_deprecated_kwarg and deprecated_kwarg_message are deleted with their last consumers. With the descriptors gone, assigning a legacy name to a results instance creates a shadow attribute instead of raising (non-slotted dataclasses) - the naming guard's consumer sweep is the in-repo defense. Placebo wrappers (TODO.md 2(c)-ii row closed; rows M-156 / M-157). run_placebo_test / run_all_placebo_tests take time= as the calendar period column only (spec section 8 rule 1) and require post_periods for every test_type; the 0/1 post dummy the permutation and leave-one-out tests need is derived as data[time].isin(post_periods) by the new diagnostics._resolve_placebo_post (the placebo_timing_test / placebo_group_test idiom), on a copy under the collision-guarded _placebo_post column. A missing time column, missing/empty post_periods, NaN in time, post_periods values absent from the column, or a pre-existing _placebo_post column raise ValueError - on the suite wrapper before its try/except blocks. Through 3.x the wrappers forwarded time itself as the dummy, so on any multi-period calendar column DifferenceInDifferences.fit rejected it and run_all_placebo_tests silently returned the two tests as error dicts (tutorial 04's placebo cell printed ERROR lines; the key-only test assertions hid it). The practitioner snippet, llms-practitioner.txt and llms-full.txt stop advising time='post'; REGISTRY carries the wrapper Note; tests/test_diagnostics.py gains the calendar-contract suite (validation, no-mutation, bitwise parity with the direct callees, real results from run_all on a six-period panel) as the rows' test_ref. Bookkeeping: ledger header + 16 rows (M-156/M-157 added; ROW_COUNT_FLOOR 139, id range (156, 157)); spec ladder, sections 5/8/11, the phase-5 roster and item 9b with dated amendments; migration guide (orientation rows, the Results-fields section, M-016/M-148/M-149/M-151/M-152 appendix Fix cells, two new renames-post rows, the positional-shift paragraph, 115 rows in total); REGISTRY / REPORTING-adjacent prose, docs/api/results.rst and diagnostics.rst, doc-deps notes, the two guides; the unreleased changelog fragments that promised a 5.0 removal reworded; API autosummary stubs drop the retired properties (Sphinx -W green, no further drift); the naming guard's DMLDiDResults.overall_att and _deprecation placebo_effects entries deleted, the two wrappers moved into the rule-1 calendar inventory, and the retired token families' remaining in-repo hits allowlisted; the capture / benchmark getattr fallbacks read the canonical names; tutorial 17 re-executed on the Rust backend (only the practitioner-snippet cell moved). --- TODO.md | 1 - .../speed_review/bench_reversible_dcdh.py | 2 +- changelog.d/20261018-v4-cut-framing.md | 2 +- changelog.d/20261018-v4-pr1-conformance.md | 6 +- changelog.d/20261018-v4-pr2-shim-removals.md | 5 +- changelog.d/20261025-v4-pr4c-ii-remove-mpd.md | 5 +- changelog.d/20261026-v4-pr5b-storage-flip.md | 4 +- ...01-v4-pr9b-shim-retirement-placebo-time.md | 52 ++++ diff_diff/_deprecation.py | 192 +++---------- .../chaisemartin_dhaultfoeuille_results.py | 22 -- diff_diff/continuous_did_results.py | 38 +-- diff_diff/diagnostics.py | 90 +++++- diff_diff/dml_did_results.py | 17 -- diff_diff/efficient_did_results.py | 22 -- diff_diff/guides/llms-full.txt | 48 ++-- diff_diff/guides/llms-practitioner.txt | 12 +- diff_diff/imputation_results.py | 22 -- diff_diff/lwdid_results.py | 8 - diff_diff/practitioner.py | 7 +- diff_diff/results_base.py | 107 +------ diff_diff/stacked_did_results.py | 22 -- diff_diff/staggered_results.py | 22 -- diff_diff/sun_abraham.py | 22 -- diff_diff/synthetic_control.py | 29 -- diff_diff/two_stage_results.py | 22 -- diff_diff/wooldridge_results.py | 28 +- .../diff_diff.CallawaySantAnnaResults.rst | 5 - ...diff.ChaisemartinDHaultfoeuilleResults.rst | 5 - .../diff_diff.ContinuousDiDResults.rst | 9 - .../diff_diff.EfficientDiDResults.rst | 5 - .../diff_diff.EventStudyResults.rst | 1 - .../diff_diff.ImputationDiDResults.rst | 5 - .../diff_diff.StackedDiDResults.rst | 5 - .../diff_diff.SunAbrahamResults.rst | 5 - .../diff_diff.TwoStageDiDResults.rst | 5 - ...iff_diff.dml_did_results.DMLDiDResults.rst | 5 - .../diff_diff.lwdid_results.LWDiDResults.rst | 2 - ...ooldridge_results.WooldridgeDiDResults.rst | 5 - docs/api/diagnostics.rst | 10 +- docs/api/results.rst | 10 +- docs/doc-deps.yaml | 4 +- docs/methodology/REGISTRY.md | 31 ++- docs/migration-4.0.md | 90 +++--- .../tutorials/17_brand_awareness_survey.ipynb | 151 +++++----- docs/v4-deprecations.yaml | 152 +++++----- docs/v4-design.md | 74 ++++- tests/_capture_v4_aggregate_oracles.py | 4 +- tests/test_balance_interop_contract.py | 9 +- tests/test_diagnostics.py | 260 +++++++++++++++++- tests/test_lwdid.py | 20 +- tests/test_methodology_placebo.py | 1 + tests/test_methodology_synthetic_control.py | 38 +-- tests/test_naming_guard.py | 86 ++++-- tests/test_result_aliases.py | 109 ++------ tests/test_v4_matrix.py | 18 +- tests/test_v4_merge_mpd.py | 197 ++----------- tests/test_v4_rename_mechanical.py | 96 +------ tests/test_v4_rename_semantic.py | 3 +- tests/test_v4_storage_flip.py | 65 ++--- 59 files changed, 950 insertions(+), 1342 deletions(-) create mode 100644 changelog.d/20261101-v4-pr9b-shim-retirement-placebo-time.md diff --git a/TODO.md b/TODO.md index bf2fe6ae3..a1b248305 100644 --- a/TODO.md +++ b/TODO.md @@ -41,7 +41,6 @@ Related tracking surfaces: | ContinuousDiD post-fit bootstrap-ES REPLAY: since 4.0 PR 3c a bootstrapped fit relays the event-study surface computed at fit (M-025), so the gap is a genuine post-fit replay (fresh draws, no fit-side cost, a route for pre-4.0 pickles); a seeded replay is tractable - the multiplier draws are seeded (`np.random.default_rng(self.seed)`) - but needs the FULL per-cell `_bootstrap_info` (bread/ee_treated/Psi_eval/dPsi_*/beta_pred) the pruned kit deliberately drops, so shipping it means a kit-payload change with its own memory contract | `diff_diff/continuous_did_aggregation.py`, `diff_diff/continuous_did_results.py` | 2(b) PR-3c | Mid | Low | | practitioner `step_name="heterogeneity"` producer-side collisions: three OTHER estimators' advice steps reuse the key with non-heterogeneity labels (`:975` ContinuousDiD dose-response, `:1022` Triple placebo-group, `:1413` LPDiD WAS arrays), so DiagnosticReport's heterogeneity completion silently drops that unrelated advice from `next_steps` via `_filter_steps` - the same latent collision fixed for StackedDiD in M-024 (renamed to `sub_experiment_balance`). Renaming these changes those estimators' report output; audit + rename with per-estimator pins. | `diff_diff/practitioner.py` | 2(b) PR-2 review R9 | Quick | Low | | PreTrendsPower `violation='linear'` on CS `base_period='varying'` input targets the wrong alternative: `δ_pre = M · \|t\|` assumes level coefficients against a common reference, but varying-base pre-treatment effects are consecutive-period comparisons (constant increments under a linear trend). Both CS-sourced routes now WARN (REGISTRY PreTrendsPower Note), and universal-base GAPPED grids fail closed via the `reference_event_times` common-reference guard; what remains is the varying-base resolution - either transforming the violation vector through each coefficient's actual base mapping (needs per-horizon base provenance) or requiring `base_period='universal'` for the linear benchmark - a per-estimator methodology decision with a hand-calculated linear-violation gate | `diff_diff/pretrends.py` | 2(b) PR-1 R5 | Mid | Medium | -| `run_placebo_test`/`run_all_placebo_tests` expose ONE overloaded `time` param (calendar for timing/group tests, 0/1 post dummy forwarded into the M-137/M-138 callees - works only because a two-period 0/1 calendar column is both); redesign the wrapper signatures (dual `time=`/`post=` params or a split) so each callee gets an honestly-named argument | `diff_diff/diagnostics.py` | 2(c)-ii PR-B | Mid | Medium | | `absorbed_fe_rank` N>=3 general rank: the helper keeps `sum(levels-1)` for 3+ absorbed dims, exact for independent connected dims but an over-count for duplicated/nested triples (measured `a(5),b(4),c==b(4)`: true 7 vs formula 10) and for disconnected N-way graphs. Two-way is component-exact. Deriving general N-way FE rank is a hypergraph problem; do it with a reference (fixest's `fixef.rm`/reghdfe df_a) rather than a guess. See `docs/methodology/variance-conventions.md` D3. | `diff_diff/utils.py` | #variance-inventory | Mid | Low | | `SyntheticControl` conformal (CWZ 2021) AR / innovation-permutation path (Lemmas 5-7) for time-series proxies — the residual-permutation shortcut is only valid for time-permutation-invariant proxies (SC/Lasso/DiD); an AR proxy needs innovation permutation. | `diff_diff/conformal.py`, `diff_diff/synthetic_control_results.py` | CWZ-2021 | Heavy | Low | | Re-run the R-dependent benchmark refresh so `docs/benchmarks.rst`'s TWFE "SE Rel Diff 0.1%" cell reflects the 3.9 K_reference convergence (expected 0.1% -> 0.0%; the table is generated, never hand-edited — the movement is noted in the CHANGELOG entry). | `docs/benchmarks.rst`, `benchmarks/R/` | #variance-inventory | Quick | Low | diff --git a/benchmarks/speed_review/bench_reversible_dcdh.py b/benchmarks/speed_review/bench_reversible_dcdh.py index 1320adcf3..e7c53d93b 100644 --- a/benchmarks/speed_review/bench_reversible_dcdh.py +++ b/benchmarks/speed_review/bench_reversible_dcdh.py @@ -69,7 +69,7 @@ def inspect_placebo(): r = results["dcdh"] results["placebo_summary"] = { "placebo_effect": getattr(r, "placebo_effect", None), - "overall_att": getattr(r, "overall_att", None), + "att": getattr(r, "att", None), "joiners_att": getattr(r, "joiners_att", None), "leavers_att": getattr(r, "leavers_att", None), } diff --git a/changelog.d/20261018-v4-cut-framing.md b/changelog.d/20261018-v4-cut-framing.md index 7f246bf0d..4dc4255dc 100644 --- a/changelog.d/20261018-v4-cut-framing.md +++ b/changelog.d/20261018-v4-cut-framing.md @@ -24,6 +24,6 @@ keeps due enforcement unchanged. The id snapshot and `ROW_COUNT_FLOOR` now include `M-148`/`M-149` (shipped by #838 without extending them) and `M-150` (132 rows). `M-091`'s roster records the four later-born `Diagnostic` containers; `M-050`'s - `code_refs` record that `diff_diff/_deprecation.py` survives 4.0 as 5.0-window + `code_refs` record that `diff_diff/_deprecation.py` survives 4.0 as pickle-migration infrastructure; `M-045`/`M-115` name the legacy-CR1 remap branches their removal deletes. A `maint/3.13` branch is cut at `v3.13.0` for 3.13.x patches. diff --git a/changelog.d/20261018-v4-pr1-conformance.md b/changelog.d/20261018-v4-pr1-conformance.md index df6cdd6e4..cb953ba11 100644 --- a/changelog.d/20261018-v4-pr1-conformance.md +++ b/changelog.d/20261018-v4-pr1-conformance.md @@ -10,13 +10,11 @@ - **`RDDensityTestResult.to_dict()`**: JSON-serializable scalars with native casts (NaN inference passes through), the `*_conventional` block `None` unless `report_all=True`, and the bandwidth-selection table nested under `bandwidths` only when it was estimated. -- `diff_diff._deprecation.deprecated_field_property` takes `removed_in=` so the warning and the - generated docstring name the row's actual removal release. ### Deprecated - **`LWDiDResults.pvalue` and `LWDiDResults.ci`** (ledger rows `M-151`, `M-152`): the - 3.10-born alias properties for `p_value` / `conf_int` now emit a `FutureWarning` and are - removed in 5.0. Read `.p_value` / `.conf_int`. + 3.10-born alias properties for `p_value` / `conf_int` were first shimmed with a `FutureWarning` + and then removed outright at 4.0 (item 9b of the cut). Read `.p_value` / `.conf_int`. ### Breaking Changes - **`LWDiDResults.to_dataframe()` is reshaped onto the shared results schema** (ledger row diff --git a/changelog.d/20261018-v4-pr2-shim-removals.md b/changelog.d/20261018-v4-pr2-shim-removals.md index 1abe34680..2f8e3e6f2 100644 --- a/changelog.d/20261018-v4-pr2-shim-removals.md +++ b/changelog.d/20261018-v4-pr2-shim-removals.md @@ -55,5 +55,6 @@ - `tests/test_v4_matrix.py` removed-status probes: a function row that resolves to a submodule counts as absent; a param row absent from the explicit signature counts as removed despite `**kwargs`. Naming-guard allowlist extended for the kept body-local aliases, `__setstate__` keys - and shared vocabulary. The `diff_diff._deprecation` helpers survive for the remaining - M-031/M-082, PR-3 and 5.0-window shims. + and shared vocabulary. The `diff_diff._deprecation` helpers survived this PR for the remaining + M-031/M-082 and PR-3 shims (item 9b later reduced the module to its pickle-migration and + required-arg helpers). diff --git a/changelog.d/20261025-v4-pr4c-ii-remove-mpd.md b/changelog.d/20261025-v4-pr4c-ii-remove-mpd.md index 009ea6d2d..c4406aa75 100644 --- a/changelog.d/20261025-v4-pr4c-ii-remove-mpd.md +++ b/changelog.d/20261025-v4-pr4c-ii-remove-mpd.md @@ -20,8 +20,9 @@ (`M-083`). Through 3.x `time=` was the deprecated alias for the 0/1 post indicator and emitted a `FutureWarning` (`M-082`); since 4.0 `time` means the event-study calendar column only. Supplying both `post=` and `time=` keeps raising the "pass only post=" error. -- **`EventStudyResults.period_effects` is a `FutureWarning` compatibility view** (`M-016`; - removed in 5.0): on a calendar surface it returns the former calendar-keyed dict of +- **`EventStudyResults.period_effects` is removed** (`M-016`; the compatibility view + this item first shipped was retired at 4.0 by item 9b - no release carried it). Through + the pre-release window, on a calendar surface it returned the former calendar-keyed dict of per-period records (`period, effect, se, t_stat, p_value, conf_int, is_significant, significance_stars`, reference period omitted; `is_significant` is evaluated at the fit's `alpha`, where the removed record hard-coded 0.05; duplicate labels raise). Relative-time diff --git a/changelog.d/20261026-v4-pr5b-storage-flip.md b/changelog.d/20261026-v4-pr5b-storage-flip.md index eafbaa0b2..949dfe850 100644 --- a/changelog.d/20261026-v4-pr5b-storage-flip.md +++ b/changelog.d/20261026-v4-pr5b-storage-flip.md @@ -1,8 +1,8 @@ ### Breaking Changes -- **4.0 cut, storage flip of the headline quintet (`M-050`..`M-058`).** `CallawaySantAnnaResults` (and `DMLDiDResults`), `SunAbrahamResults`, `ImputationDiDResults`, `TwoStageDiDResults`, `StackedDiDResults`, `EfficientDiDResults`, `WooldridgeDiDResults`, `ChaisemartinDHaultfoeuilleResults` and `ContinuousDiDResults` now store the canonical `att` / `se` / `t_stat` / `p_value` / `conf_int` as their native dataclass fields (renamed in slot, so the positional constructor layout is unchanged) and take those names as constructor keywords (`overall_att=` raises `TypeError`). The 3.x `overall_att` / `overall_se` / `overall_t_stat` / `overall_p_value` / `overall_conf_int` names - and on `ContinuousDiDResults` all nine legacy spellings, the `overall_att_*` fields and the former silent `overall_se`-style aliases - are read-only properties that emit a `FutureWarning` and are removed at 5.0; they are absent from `dataclasses.fields()`, `asdict()` and `to_dict()`. 3.x pickles load unchanged (each container's `__setstate__` rewrites the legacy keys on load). The bootstrap draw containers (`bootstrap_results.overall_att_se` and siblings) are unchanged, and `StaggeredTripleDiffResults` keeps its `overall_*` storage until the DDD container unification. +- **4.0 cut, storage flip of the headline quintet (`M-050`..`M-058`).** `CallawaySantAnnaResults` (and `DMLDiDResults`), `SunAbrahamResults`, `ImputationDiDResults`, `TwoStageDiDResults`, `StackedDiDResults`, `EfficientDiDResults`, `WooldridgeDiDResults`, `ChaisemartinDHaultfoeuilleResults` and `ContinuousDiDResults` now store the canonical `att` / `se` / `t_stat` / `p_value` / `conf_int` as their native dataclass fields (renamed in slot, so the positional constructor layout is unchanged) and take those names as constructor keywords (`overall_att=` raises `TypeError`). The 3.x `overall_att` / `overall_se` / `overall_t_stat` / `overall_p_value` / `overall_conf_int` names - and on `ContinuousDiDResults` all nine legacy spellings, the `overall_att_*` fields and the former silent `overall_se`-style aliases - are removed at 4.0 (item 9b retired the `FutureWarning` property shims this item first shipped - reading one raises `AttributeError`); they are absent from `dataclasses.fields()`, `asdict()` and `to_dict()`. 3.x pickles load unchanged (each container's `__setstate__` rewrites the legacy keys on load). The bootstrap draw containers (`bootstrap_results.overall_att_se` and siblings) are unchanged, and `StaggeredTripleDiffResults` keeps its `overall_*` storage until the DDD container unification. - **Report schemas.** `DiagnosticReport.to_dict()` reports `headline_metric.name` and `target_parameter.headline_attribute` as `"att"` for the nine containers above (`"overall_att"` under 3.x); `DIAGNOSTIC_REPORT_SCHEMA_VERSION` 2.0 -> 3.0. `BusinessReport.to_dict()` reports `target_parameter.headline_attribute` likewise; `BUSINESS_REPORT_SCHEMA_VERSION` 3.0 -> 4.0 (its `headline` block carries no name key). `StaggeredTripleDiffResults` keeps `headline_attribute: "overall_att"` until item 6. ### Internal -- Ledger rows `M-050`..`M-058` flip to `shimmed` with `tests/test_v4_storage_flip.py` as their `test_ref`; `diff_diff._deprecation` gains `LEGACY_OVERALL_QUINTET` and `migrate_renamed_state_keys` (5.0-window infrastructure); `DMLDiDResults` re-declares the five shims so the warning names the subclass (naming-guard allowlist reason reworded); `_extract_scalar_headline`'s legacy alias row is deleted; `tests/test_result_aliases.py` pins the post-flip direction (SDDD kept in the 3.x direction); the API autosummary stubs are regenerated; the bootstrap draw containers' payload names are recorded as a REGISTRY `Note` (user decision 2026-10-26); spec item 5 carries its SHIPPED note. +- Ledger rows `M-050`..`M-058` flip to `shimmed` with `tests/test_v4_storage_flip.py` as their `test_ref`; `diff_diff._deprecation` gains `LEGACY_OVERALL_QUINTET` and `migrate_renamed_state_keys` (the pickle-migration infrastructure); `DMLDiDResults` re-declares the five shims so the warning names the subclass (naming-guard allowlist reason reworded); `_extract_scalar_headline`'s legacy alias row is deleted; `tests/test_result_aliases.py` pins the post-flip direction (SDDD kept in the 3.x direction); the API autosummary stubs are regenerated; the bootstrap draw containers' payload names are recorded as a REGISTRY `Note` (user decision 2026-10-26); spec item 5 carries its SHIPPED note. diff --git a/changelog.d/20261101-v4-pr9b-shim-retirement-placebo-time.md b/changelog.d/20261101-v4-pr9b-shim-retirement-placebo-time.md new file mode 100644 index 000000000..3853818a3 --- /dev/null +++ b/changelog.d/20261101-v4-pr9b-shim-retirement-placebo-time.md @@ -0,0 +1,52 @@ +### Breaking Changes + +- **4.0 cut, item 9b: the compatibility shims scheduled for a 5.0 removal are removed at 4.0** (user + decision - no 4.x debt; none of these `FutureWarning` shims ever reached a release, so the released + 3.x names break without a warned release - `docs/migration-4.0.md` is the notice). The 3.x + `overall_att` / `overall_se` / `overall_t_stat` / `overall_p_value` / `overall_conf_int` names on + `CallawaySantAnnaResults` (and `DMLDiDResults`), `SunAbrahamResults`, `ImputationDiDResults`, + `TwoStageDiDResults`, `StackedDiDResults`, `EfficientDiDResults`, `WooldridgeDiDResults`, + `ChaisemartinDHaultfoeuilleResults` and - all nine spellings, `overall_att_*` included - + `ContinuousDiDResults` raise `AttributeError`; read the canonical `att` / `se` / `t_stat` / + `p_value` / `conf_int` (`M-050`..`M-058`). 3.x pickles still load (the `__setstate__` key migration + stays). `EventStudyResults.period_effects` is gone; read `to_dataframe()` or the columnar arrays + (`M-016`). `LWDiDResults.pvalue` / `.ci` are gone; read `.p_value` / `.conf_int` (`M-151`, `M-152`). +- **`SyntheticControl(inner_max_iter=, inner_min_decrease=)` are removed** (`M-148`, `M-149`; the + `DeprecationWarning` shipped in 3.13.0): passing either is a `TypeError`, and the positional + parameters after them (`standardize`, `alpha`, `seed`, `v_cv_t0`) move two slots left - pass + them by keyword. +- **`run_placebo_test` / `run_all_placebo_tests`: `time` is the calendar column only and + `post_periods` is required for every `test_type`** (`M-156`, `M-157`; spec section 8 rule 1). The + 0/1 post dummy the permutation and leave-one-out tests need is derived as + `data[time].isin(post_periods)`; a missing `time` column, missing/empty `post_periods`, missing + values in `time`, `post_periods` values absent from the column, or a pre-existing `_placebo_post` + column raise `ValueError` (on the suite wrapper before any test runs). Calls that passed the 0/1 + indicator as `time=` with `post_periods=[1]` return identical numbers; calls that omitted + `post_periods` must add it. + +### Fixed + +- `run_all_placebo_tests` and `run_placebo_test(test_type="permutation" | "leave_one_out")` no + longer fail on a multi-period calendar column: through 3.x the wrappers forwarded `time` itself as + the 0/1 post dummy, so `DifferenceInDifferences.fit` rejected it and the suite silently reported + the two tests as error dicts (tutorial 04's placebo cell printed `ERROR` lines). The dummy is now + derived from `post_periods`. + +### Internal + +- Ledger rows `M-016`, `M-050`..`M-058`, `M-151`, `M-152` flip to `removed` at 4.0 with + `deprecated_in: null` (no release warned); `M-148`/`M-149` flip to `removed` with + `deprecated_in: 3.13.0`; new behavior rows `M-156`/`M-157` (`tests/test_diagnostics.py`); the + ledger header, spec ladder, section 5/8/9/11 notes and the phase-5 roster carry dated amendments. + `diff_diff._deprecation` is reduced to the pickle-migration (`LEGACY_OVERALL_QUINTET`, + `migrate_renamed_state_keys`, `migrate_event_study_reference_sentinel`) and required-arg + (`NOT_SUPPLIED`, `require_arg`) helpers - `deprecated_field_property`, `resolve_renamed_kwarg`, + `warn_deprecated_kwarg` and `deprecated_kwarg_message` are deleted with their last consumers. + With the descriptors gone, assigning a legacy name to a results instance creates a shadow + attribute instead of raising (the non-slotted dataclasses) - the naming guard's consumer sweep + is the in-repo defense. Naming guard: the `DMLDiDResults.overall_att` surface entry and the + `_deprecation.py` `placebo_effects` consumer entry are deleted, the two placebo wrappers join the + rule-1 calendar `time` inventory, and the retired token families' remaining in-repo hits + (body-local `overall_att` names, the kept `__setstate__` key maps, methodology history) are + allowlisted. API autosummary stubs regenerated; TODO.md's placebo-wrapper redesign row closed; + tutorial 17 re-executed (its stored practitioner snippet echoed the old `time='post'` advice). diff --git a/diff_diff/_deprecation.py b/diff_diff/_deprecation.py index dbae6d134..b1f1ea4ff 100644 --- a/diff_diff/_deprecation.py +++ b/diff_diff/_deprecation.py @@ -1,62 +1,46 @@ -"""Shared deprecation-shim machinery for the 3.9 rename sweep (4.0 program, Phase 2(c)-ii). +"""Private lifecycle helpers of the 4.0 program: pickle-key migration and required-arg sentinels. This module is a deliberate LEAF: it imports only the standard library, never other ``diff_diff`` modules, so every estimator module can import it without -cycles (the ``_base.py`` precedent). It is private and unexported. The 4.0 -removal PRs delete the 3.9 shims that use it, but the module itself SURVIVES -4.0: the 4.0 -> 5.0 ``FutureWarning`` property shims of the results-storage -flip (ledger rows M-016 and M-050..M-058) ride ``deprecated_field_property`` -through the 5.0 window (recorded in M-050's ``code_refs``). - -The helpers implement the ledger's shim contract (``docs/v4-deprecations.yaml``, -groups ``renames-*``; rules in ``docs/v4-design.md`` section 8): - -- Renamed parameter: the NEW name occupies the old name's signature position, - the OLD name moves to the signature tail with the ``NOT_SUPPLIED`` sentinel - default. ``resolve_renamed_kwarg`` maps old to new with a single - ``FutureWarning``, rejects both-supplied calls loudly, and returns the - caller-declared default when neither is passed. -- Dropped parameter (no successor name): the parameter keeps its position - with a sentinel default; the owner calls ``warn_deprecated_kwarg`` when a - value was actually supplied. (The 3.9 fit-time ``aggregate=`` shims were - this shape; all of them were deleted at the 4.0 cut - CallawaySantAnna, - EfficientDiD, StackedDiD, ChaisemartinDHaultfoeuille and - HeterogeneousAdoptionDiD in PR 3b, ImputationDiD / TwoStageDiD / - ContinuousDiD in PR 3c. ``warn_deprecated_kwarg`` has no library caller - any more and is retained per section 8 rule 11 for the 5.0 window.) -- Renamed results field: the dataclass field takes the new name and - ``deprecated_field_property`` builds the read-only warning alias under the - old name (a plain class attribute, so it is a descriptor but NOT a - ``__dataclass_fields__`` entry - the shape ``tests/test_v4_matrix.py`` - asserts for ``shimmed`` field rows). - -Stacklevel contract: ``resolve_renamed_kwarg`` / ``warn_deprecated_kwarg`` -must be called DIRECTLY from the public method that owns the parameter -(user frame -> public method -> helper -> ``warnings.warn`` = stacklevel 3). -``deprecated_field_property``'s getter warns at stacklevel 2 (user frame -> -property getter). - -All warnings are ``FutureWarning`` (visible to end users by default) per the -section 2 category rule: new shims warn with ``FutureWarning`` (the -pre-existing M-001..M-003 ``DeprecationWarning`` sites were removed at the -4.0 cut). +cycles (the ``_base.py`` precedent). It is private and unexported. + +History. Through the 3.9 rename sweep (Phase 2(c)-ii) this module carried the +``FutureWarning`` shim machinery of ``docs/v4-design.md`` section 8 - the +renamed-kwarg resolver, the dropped-kwarg warner and the renamed-results-field +warning property. The 4.0 cut deleted every shim that used them, and item 9b +(2026-11-01) deleted the helpers themselves together with their last consumers +(the ``overall_*`` / ``period_effects`` / ``pvalue`` / ``ci`` property shims of +ledger rows M-016, M-050..M-058, M-151, M-152, which had been scheduled for a +later removal and were instead retired AT 4.0 - no release ever carried them). +What remains: + +- ``NOT_SUPPLIED`` / ``require_arg``: the sentinel default + required-ness + re-validation that ``TwoWayFixedEffects.fit`` and ``TripleDifference.fit`` + use to express "``post`` is required in one mode, ``time`` in the other" + (rows M-082/M-083, M-031/M-085). +- ``LEGACY_OVERALL_QUINTET`` / ``migrate_renamed_state_keys``: the + ``__setstate__`` rewrite that loads 3.x pickles written under the + ``overall_*`` storage names into the canonical quintet fields (spec + section 5; rows M-050..M-058). +- ``migrate_event_study_reference_sentinel``: the ``__setstate__`` rewrite of + the 3.x zero-count reference-row sentinel (row M-093). """ import math -import warnings from typing import Any, Dict, Mapping, Optional __all__: "list[str]" = [] class _NotSupplied: - """Sentinel for renamed/deprecated parameters (M-020 precedent). + """Sentinel default for a parameter whose required-ness depends on the call mode. A plain ``None`` default cannot distinguish "not passed" from "passed - None", so a bare ``None`` default would fire the FutureWarning on EVERY - call (and break the warnings-as-errors ``cls(**est.get_params())`` - round-trip in ``tests/test_base_estimator.py``). The warning must fire - only when the caller actually supplies the argument. + None"; the sentinel lets ``require_arg`` restore Python's missing-argument + ``TypeError`` for the mode in which the parameter is required (the + ``TwoWayFixedEffects.fit`` / ``TripleDifference.fit`` ``post`` vs ``time`` + contract) while the ``cls(**est.get_params())`` round-trip in + ``tests/test_base_estimator.py`` stays silent. """ def __repr__(self) -> str: # pragma: no cover - debugging aid @@ -73,124 +57,19 @@ def __deepcopy__(self, memo: Any) -> "_NotSupplied": NOT_SUPPLIED = _NotSupplied() -def deprecated_kwarg_message(qualname: str, name: str, instruction: str) -> str: - """The single message template every shim warning and test pins.""" - return f"{qualname}({name}=) is deprecated and will be removed in 4.0; {instruction}." - - -def warn_deprecated_kwarg( - qualname: str, name: str, instruction: str, *, stacklevel: int = 3 -) -> None: - """Emit the deprecation warning for a supplied deprecated parameter. - - The caller performs the was-it-supplied check (sentinel or ``None`` - depending on the parameter's legal value space) so that default and - round-trip constructions stay silent. - """ - warnings.warn( - deprecated_kwarg_message(qualname, name, instruction), - FutureWarning, - stacklevel=stacklevel, - ) - - -def resolve_renamed_kwarg( - qualname: str, - old_name: str, - old_value: Any, - new_name: str, - new_value: Any, - *, - default: Any = None, - extra: str = "", -) -> Any: - """Resolve a renamed keyword pair during the 3.9 shim window. - - Parameters - ---------- - qualname : str - The public surface the parameters belong to (``"Class.method"`` or - a module-level function name) - used verbatim in messages. - old_name, old_value : str, Any - The deprecated parameter and what the caller passed (``NOT_SUPPLIED`` - when absent). - new_name, new_value : str, Any - The canonical parameter and what the caller passed (``NOT_SUPPLIED`` - when absent). - default : Any - Returned when neither name was supplied. Required parameters pass - ``NOT_SUPPLIED`` here and re-validate with :func:`require_arg`. - extra : str - Appended to the warning message (e.g. a note that the old name - survives with a different meaning on the same surface, as M-082's - ``time=`` did through 3.x). - - Returns - ------- - Any - The effective value for the canonical parameter. - """ - old_passed = not isinstance(old_value, _NotSupplied) - new_passed = not isinstance(new_value, _NotSupplied) - if old_passed and new_passed: - raise ValueError( - f"{qualname}() got both {old_name}= (deprecated) and {new_name}=; " - f"pass only {new_name}=." - ) - if old_passed: - message = deprecated_kwarg_message(qualname, old_name, f"use {new_name}= instead") - if extra: - message = f"{message} {extra}" - warnings.warn(message, FutureWarning, stacklevel=3) - return old_value - if new_passed: - return new_value - return default - - def require_arg(qualname: str, name: str, value: Any) -> None: - """Re-validate required-ness after the rename mapping. + """Re-validate required-ness of a sentinel-defaulted parameter. Sentinel-defaulted required parameters lose Python's built-in - missing-argument ``TypeError``; this restores it, naming the NEW - parameter. + missing-argument ``TypeError``; this restores it. """ if isinstance(value, _NotSupplied): raise TypeError(f"{qualname}() missing required argument: '{name}'") -def deprecated_field_property( - cls_name: str, old: str, new: str, *, removed_in: str = "4.0" -) -> property: - """Build the read-only warning alias for a renamed results field. - - Assigned WITHOUT an annotation in the dataclass body, so it stays a - plain descriptor (never a ``__dataclass_fields__`` entry). Wording - follows the 3.x ``SyntheticDiDResults.placebo_effects`` renamed-field - alias precedent (M-003, removed at 4.0), with the FutureWarning category - the ledger declares for renamed-field rows. ``removed_in`` names the - release the ledger row schedules the removal for (``"4.0"`` for the 3.9 - rename sweep; ``"5.0"`` for aliases deprecated AT 4.0, e.g. the LWDiD - ``pvalue``/``ci`` rows M-151/M-152) so the warning and the generated - docstring never contradict the row. - """ - - def _get(self: Any) -> Any: - warnings.warn( - f"{cls_name}.{old} is deprecated; use {new} instead. " - f"Will be removed in {removed_in}.", - FutureWarning, - stacklevel=2, - ) - return getattr(self, new) - - _get.__name__ = old - _get.__doc__ = f"Deprecated alias for :attr:`{new}` (removed in {removed_in})." - return property(_get) - - #: The 3.x ``overall_*`` storage names of the nine results containers flipped -#: at the 4.0 cut (ledger rows M-050..M-058), keyed to the canonical quintet +#: at the 4.0 cut (ledger rows M-050..M-058; the names themselves were removed +#: at 4.0, item 9b), keyed to the canonical quintet #: field that now stores each value. Shared by the containers' ``__setstate__`` #: pickle migration (spec section 5); ``ContinuousDiDResults`` keeps a #: module-local map for its deviant ``overall_att_*`` spellings. @@ -211,9 +90,8 @@ def migrate_renamed_state_keys( Returns a NEW dict (the input is never mutated). For each ``old -> new`` pair the legacy key is popped when present and its value seeds the canonical key only when that key is absent - an existing canonical - value wins, and the legacy key never lingers in ``__dict__`` (a - ``deprecated_field_property`` data descriptor would shadow it on read, - but it would survive a re-pickle). Used by the ``__setstate__`` of the + value wins, and the legacy key never lingers in ``__dict__`` (it would + otherwise survive a re-pickle). Used by the ``__setstate__`` of the M-050..M-058 containers; the ``SyntheticDiDResults.__setstate__`` precedent (``results.py``) is the hand-written form of the same rule. """ diff --git a/diff_diff/chaisemartin_dhaultfoeuille_results.py b/diff_diff/chaisemartin_dhaultfoeuille_results.py index 37ea8a885..b1cde5490 100644 --- a/diff_diff/chaisemartin_dhaultfoeuille_results.py +++ b/diff_diff/chaisemartin_dhaultfoeuille_results.py @@ -33,7 +33,6 @@ from diff_diff._deprecation import ( LEGACY_OVERALL_QUINTET, - deprecated_field_property, migrate_renamed_state_keys, ) from diff_diff.aggregation import AggregationMixin, AggregationResult @@ -862,27 +861,6 @@ def _estimand_label(self) -> str: return f"{did_part}{suffix}_{sub_part}" if sub_part else f"{did_part}{suffix}" return base - # --- Deprecated 3.x storage names (ledger row M-057) --- - # FutureWarning from 4.0, removed at 5.0. Plain class attributes (no - # annotation) so they stay descriptors, never dataclass fields - the - # ``shimmed`` field-row shape tests/test_v4_matrix.py asserts - # (the LWDiDResults 3.10-alias precedent). - overall_att = deprecated_field_property( - "ChaisemartinDHaultfoeuilleResults", "overall_att", "att", removed_in="5.0" - ) - overall_se = deprecated_field_property( - "ChaisemartinDHaultfoeuilleResults", "overall_se", "se", removed_in="5.0" - ) - overall_t_stat = deprecated_field_property( - "ChaisemartinDHaultfoeuilleResults", "overall_t_stat", "t_stat", removed_in="5.0" - ) - overall_p_value = deprecated_field_property( - "ChaisemartinDHaultfoeuilleResults", "overall_p_value", "p_value", removed_in="5.0" - ) - overall_conf_int = deprecated_field_property( - "ChaisemartinDHaultfoeuilleResults", "overall_conf_int", "conf_int", removed_in="5.0" - ) - def __repr__(self) -> str: """Concise string representation.""" sig = _get_significance_stars(self.p_value) diff --git a/diff_diff/continuous_did_results.py b/diff_diff/continuous_did_results.py index 9b0aea47a..b8a90e922 100644 --- a/diff_diff/continuous_did_results.py +++ b/diff_diff/continuous_did_results.py @@ -12,7 +12,7 @@ import numpy as np import pandas as pd -from diff_diff._deprecation import deprecated_field_property, migrate_renamed_state_keys +from diff_diff._deprecation import migrate_renamed_state_keys from diff_diff.aggregation import ( AggregationMixin, AggregationResult, @@ -221,42 +221,6 @@ class ContinuousDiDResults(BaseResults, AggregationMixin): _AGGREGATE_SUPPORTED: ClassVar[Tuple[str, ...]] = ("simple", "event_study", "dose") _AGGREGATE_BALANCE_E_TYPES: ClassVar[Tuple[str, ...]] = () - # --- Deprecated 3.x storage names (ledger row M-058) --- - # FutureWarning from 4.0, removed at 5.0. Plain class attributes (no - # annotation) so they stay descriptors, never dataclass fields - the - # ``shimmed`` field-row shape tests/test_v4_matrix.py asserts - # (the LWDiDResults 3.10-alias precedent). - # Nine spellings onto the five canonical fields: the five former - # ``overall_att_*`` fields AND the four former silent ``overall_*`` - # aliases (the M-058 family); ``overall_acrt*`` is a separate estimand. - overall_att = deprecated_field_property( - "ContinuousDiDResults", "overall_att", "att", removed_in="5.0" - ) - overall_att_se = deprecated_field_property( - "ContinuousDiDResults", "overall_att_se", "se", removed_in="5.0" - ) - overall_att_t_stat = deprecated_field_property( - "ContinuousDiDResults", "overall_att_t_stat", "t_stat", removed_in="5.0" - ) - overall_att_p_value = deprecated_field_property( - "ContinuousDiDResults", "overall_att_p_value", "p_value", removed_in="5.0" - ) - overall_att_conf_int = deprecated_field_property( - "ContinuousDiDResults", "overall_att_conf_int", "conf_int", removed_in="5.0" - ) - overall_se = deprecated_field_property( - "ContinuousDiDResults", "overall_se", "se", removed_in="5.0" - ) - overall_t_stat = deprecated_field_property( - "ContinuousDiDResults", "overall_t_stat", "t_stat", removed_in="5.0" - ) - overall_p_value = deprecated_field_property( - "ContinuousDiDResults", "overall_p_value", "p_value", removed_in="5.0" - ) - overall_conf_int = deprecated_field_property( - "ContinuousDiDResults", "overall_conf_int", "conf_int", removed_in="5.0" - ) - def __setstate__(self, state: Dict[str, Any]) -> None: """Migrate 3.x pickles written under the ``overall_*`` storage names. diff --git a/diff_diff/diagnostics.py b/diff_diff/diagnostics.py index 72cc06619..e5f50eccf 100644 --- a/diff_diff/diagnostics.py +++ b/diff_diff/diagnostics.py @@ -191,6 +191,49 @@ def to_dataframe(self) -> pd.DataFrame: return pd.DataFrame([self.to_dict()]) +_PLACEBO_POST_COL = "_placebo_post" + + +def _resolve_placebo_post( + data: pd.DataFrame, time: str, post_periods: Optional[List[Any]] +) -> pd.Series: + """Derive the 0/1 post-period dummy of the placebo wrappers from the calendar column. + + ``time`` is always the calendar period column (never a 0/1 indicator - spec + section 8 rule 1); ``post_periods`` lists its post-treatment values. The + dummy is ``data[time].isin(post_periods)``, the same rule + :func:`placebo_timing_test` and :func:`placebo_group_test` apply internally. + Every malformed input raises ``ValueError`` (never a silent all-zero dummy): + a missing ``time`` column, a missing/empty ``post_periods``, missing values + in ``time`` (``isin`` would classify them as pre-treatment), ``post_periods`` + values absent from the column, or a pre-existing ``_placebo_post`` column. + """ + if time not in data.columns: + raise ValueError(f"time column '{time}' not found in data") + if post_periods is None or len(post_periods) == 0: + raise ValueError( + "post_periods is required: the list of post-treatment values of the " f"'{time}' column" + ) + if data[time].isna().any(): + raise ValueError( + f"time column '{time}' has missing values; drop or fill them before " + "running placebo tests" + ) + # dtype-aware membership (the same machinery as the ``isin`` below): a Python + # set would hash a NumPy datetime64 scalar and the equivalent pandas + # Timestamp differently on some NumPy/pandas versions and reject valid dates. + present = pd.Index(list(post_periods)).isin(pd.Index(data[time].unique())) + missing = [p for p, ok in zip(post_periods, present) if not ok] + if missing: + raise ValueError(f"post_periods values {missing} do not occur in the '{time}' column") + if _PLACEBO_POST_COL in data.columns: + raise ValueError( + f"data already has a '{_PLACEBO_POST_COL}' column; rename it before " + "running placebo tests" + ) + return data[time].isin(post_periods).astype(int) + + def run_placebo_test( data: pd.DataFrame, outcome: str, @@ -223,7 +266,9 @@ def run_placebo_test( treatment : str Name of treatment indicator column (0/1). time : str - Name of time period column. + Name of the calendar period column (never a 0/1 indicator; in a + two-period design pass the period column and + ``post_periods=[]``). unit : str, optional Name of unit identifier column. Required for some test types. test_type : str, default="fake_timing" @@ -237,8 +282,11 @@ def run_placebo_test( Should be a pre-treatment period. fake_treatment_group : list, optional For "fake_group": List of control unit IDs to designate as fake treated. - post_periods : list, optional - List of post-treatment periods. Required for fake_timing test. + post_periods : list + Required. The values of ``time`` that are post-treatment. The + permutation and leave-one-out tests derive their 0/1 post dummy as + ``data[time].isin(post_periods)`` (the same rule the fake-timing and + fake-group tests apply). n_permutations : int, default=1000 For "permutation": Number of random treatment assignments. alpha : float, default=0.05 @@ -272,6 +320,7 @@ def run_placebo_test( ... data, outcome='sales', treatment='treated', time='period', ... unit='unit_id', ... test_type='permutation', + ... post_periods=[2, 3, 4], ... n_permutations=1000, ... seed=42 ... ) @@ -290,6 +339,7 @@ def run_placebo_test( raise ValueError(f"test_type must be one of {valid_types}, got '{test_type}'") if test_type == "fake_timing": + _resolve_placebo_post(data, time, post_periods) # validation only return placebo_timing_test( data=data, outcome=outcome, @@ -306,6 +356,7 @@ def run_placebo_test( raise ValueError("unit is required for fake_group test") if fake_treatment_group is None or len(fake_treatment_group) == 0: raise ValueError("fake_treatment_group is required for fake_group test") + _resolve_placebo_post(data, time, post_periods) # validation only return placebo_group_test( data=data, outcome=outcome, @@ -321,11 +372,14 @@ def run_placebo_test( elif test_type == "permutation": if unit is None: raise ValueError("unit is required for permutation test") + post_dummy = _resolve_placebo_post(data, time, post_periods) + data = data.copy() + data[_PLACEBO_POST_COL] = post_dummy return permutation_test( data=data, outcome=outcome, treatment=treatment, - post=time, + post=_PLACEBO_POST_COL, unit=unit, n_permutations=n_permutations, alpha=alpha, @@ -336,11 +390,14 @@ def run_placebo_test( elif test_type == "leave_one_out": if unit is None: raise ValueError("unit is required for leave_one_out test") + post_dummy = _resolve_placebo_post(data, time, post_periods) + data = data.copy() + data[_PLACEBO_POST_COL] = post_dummy return leave_one_out_test( data=data, outcome=outcome, treatment=treatment, - post=time, + post=_PLACEBO_POST_COL, unit=unit, alpha=alpha, **estimator_kwargs, @@ -866,13 +923,18 @@ def run_all_placebo_tests( treatment : str Treatment indicator column. time : str - Time period column. + Calendar period column (never a 0/1 indicator). unit : str Unit identifier column. pre_periods : list - List of pre-treatment periods. + List of pre-treatment periods (a fake-timing test runs for each one + after the first). post_periods : list - List of post-treatment periods. + Required. The values of ``time`` that are post-treatment; the + permutation and leave-one-out tests derive their 0/1 post dummy as + ``data[time].isin(post_periods)``. A malformed ``post_periods`` (or + ``time`` column) raises ``ValueError`` before any test runs - it is + never recorded as an error dict. n_permutations : int, default=500 Permutations for permutation test. alpha : float, default=0.05 @@ -888,6 +950,10 @@ def run_all_placebo_tests( Dictionary mapping test names to PlaceboTestResults. Keys: "fake_timing_{period}", "permutation", "leave_one_out" """ + post_dummy = _resolve_placebo_post(data, time, post_periods) + dummy_data = data.copy() + dummy_data[_PLACEBO_POST_COL] = post_dummy + results = {} # Fake timing tests for each pre-period (except first) @@ -916,10 +982,10 @@ def run_all_placebo_tests( # Permutation test try: perm_result = permutation_test( - data=data, + data=dummy_data, outcome=outcome, treatment=treatment, - post=time, + post=_PLACEBO_POST_COL, unit=unit, n_permutations=n_permutations, alpha=alpha, @@ -937,10 +1003,10 @@ def run_all_placebo_tests( # Leave-one-out test try: loo_result = leave_one_out_test( - data=data, + data=dummy_data, outcome=outcome, treatment=treatment, - post=time, + post=_PLACEBO_POST_COL, unit=unit, alpha=alpha, **estimator_kwargs, diff --git a/diff_diff/dml_did_results.py b/diff_diff/dml_did_results.py index ef6ec47bd..206630c9a 100644 --- a/diff_diff/dml_did_results.py +++ b/diff_diff/dml_did_results.py @@ -29,7 +29,6 @@ import numpy as np import pandas as pd -from diff_diff._deprecation import deprecated_field_property from diff_diff.results_base import _json_safe_label from diff_diff.staggered_results import CallawaySantAnnaResults @@ -143,22 +142,6 @@ class DMLDiDResults(CallawaySantAnnaResults): bad_control_covariates: Optional[Tuple[str, ...]] = None bad_control_diagnostics: Optional[Dict[Any, Dict[str, Any]]] = field(default=None, repr=False) - # --- Deprecated 3.x storage names (ride ledger row M-050) --- - # The storage is inherited from CallawaySantAnnaResults; the five shims are - # RE-DECLARED here so the FutureWarning names this subclass (plain class - # attributes, never dataclass fields). Removed at 5.0 with the parent's. - overall_att = deprecated_field_property("DMLDiDResults", "overall_att", "att", removed_in="5.0") - overall_se = deprecated_field_property("DMLDiDResults", "overall_se", "se", removed_in="5.0") - overall_t_stat = deprecated_field_property( - "DMLDiDResults", "overall_t_stat", "t_stat", removed_in="5.0" - ) - overall_p_value = deprecated_field_property( - "DMLDiDResults", "overall_p_value", "p_value", removed_in="5.0" - ) - overall_conf_int = deprecated_field_property( - "DMLDiDResults", "overall_conf_int", "conf_int", removed_in="5.0" - ) - def __repr__(self) -> str: folds = f"n_folds={self.n_folds}" if self.effective_n_folds is not None: diff --git a/diff_diff/efficient_did_results.py b/diff_diff/efficient_did_results.py index df1ae14fa..0c6f96c17 100644 --- a/diff_diff/efficient_did_results.py +++ b/diff_diff/efficient_did_results.py @@ -14,7 +14,6 @@ from diff_diff._deprecation import ( LEGACY_OVERALL_QUINTET, - deprecated_field_property, migrate_renamed_state_keys, ) from diff_diff.aggregation import AggregationMixin, AggregationResult, build_total_relay_row @@ -293,27 +292,6 @@ class EfficientDiDResults(BaseResults, AggregationMixin): # the mixin default ("event_study",) - CS precedent, do not redeclare. _AGGREGATE_SUPPORTED = ("simple", "event_study", "group", "total") - # --- Deprecated 3.x storage names (ledger row M-055) --- - # FutureWarning from 4.0, removed at 5.0. Plain class attributes (no - # annotation) so they stay descriptors, never dataclass fields - the - # ``shimmed`` field-row shape tests/test_v4_matrix.py asserts - # (the LWDiDResults 3.10-alias precedent). - overall_att = deprecated_field_property( - "EfficientDiDResults", "overall_att", "att", removed_in="5.0" - ) - overall_se = deprecated_field_property( - "EfficientDiDResults", "overall_se", "se", removed_in="5.0" - ) - overall_t_stat = deprecated_field_property( - "EfficientDiDResults", "overall_t_stat", "t_stat", removed_in="5.0" - ) - overall_p_value = deprecated_field_property( - "EfficientDiDResults", "overall_p_value", "p_value", removed_in="5.0" - ) - overall_conf_int = deprecated_field_property( - "EfficientDiDResults", "overall_conf_int", "conf_int", removed_in="5.0" - ) - def __setstate__(self, state: Dict[str, Any]) -> None: """Migrate 3.x pickles written under the ``overall_*`` storage names. diff --git a/diff_diff/guides/llms-full.txt b/diff_diff/guides/llms-full.txt index cd284a231..e0219940b 100644 --- a/diff_diff/guides/llms-full.txt +++ b/diff_diff/guides/llms-full.txt @@ -151,32 +151,24 @@ print(es.summary()) **Note:** TWFE can be biased with staggered treatment timing and heterogeneous effects. Consider CallawaySantAnna, SunAbraham, or ImputationDiD for staggered designs. -### EventStudyResults.period_effects (compatibility view) +### Per-period effects on the TWFE event-study surface The `MultiPeriodDiD` class, `MultiPeriodDiDResults`, `PeriodEffect` and the `EventStudy` alias were removed at the 4.0 cut (ledger rows M-010/M-011/M-012/ -M-060); `TwoWayFixedEffects(...).fit(..., event_study=True)` is the estimator. -For code that still reads the old per-period dict, the `EventStudyResults` -returned by the TWFE event-study mode exposes `period_effects` as a deprecated -compatibility VIEW (ledger row M-016, removed in 5.0): - -- Reading it emits a `FutureWarning`; `to_dataframe()` (one row per - `event_time`, reference row included) is the successor. -- Calendar-time surfaces only (`time_scale="calendar"`, i.e. the TWFE - event-study mode); on a relative-time surface the attribute raises - `AttributeError`, so `hasattr(es, "period_effects")` is False there. -- A dict keyed by calendar period with the reference period OMITTED (as the - old dict was). Each value is a read-only record with `period`, `effect`, - `se`, `t_stat`, `p_value`, `conf_int`, `is_significant` and - `significance_stars`; `is_significant` is evaluated at the fit's `alpha` - (NaN p-value -> False), not the removed record's fixed 0.05. +M-060); `TwoWayFixedEffects(...).fit(..., event_study=True)` is the estimator +and the `EventStudyResults` it returns is the only per-period surface. The old +calendar-keyed per-period dict has NO successor attribute (the planned 4.0 -> 5.0 +compatibility view, ledger row M-016, was retired at 4.0 - item 9b): read +`to_dataframe()` (one row per `event_time`, reference row included, columns +`att` / `se` / `t_stat` / `p_value` / `conf_int_lower` / `conf_int_upper` / +`is_reference`) or the columnar arrays of the same names. ```python es = twfe.fit(data, outcome='y', treatment='treated', unit='unit_id', event_study=True, time='period', post_periods=[4, 5, 6, 7]) -table = es.to_dataframe() # successor: columnar per-event-time table -pe = es.period_effects # FutureWarning; {period: record}, reference omitted -pe[5].effect, pe[5].conf_int +table = es.to_dataframe() # columnar per-event-time table +row5 = table.loc[table["event_time"] == 5].iloc[0] +row5["att"], (row5["conf_int_lower"], row5["conf_int_upper"]) ``` ### CallawaySantAnna @@ -624,8 +616,6 @@ SyntheticControl( custom_v=None, # diagonal V (len = #predictors); required iff v_method="custom"; forbidden otherwise optimizer_options: dict | None = None, # merged into scipy.optimize.minimize (outer V search; nested/cv) n_starts: int = 4, # multistart count for the outer V search (nested/cv) - inner_max_iter: int | None = None, # DEPRECATED no-op (warns; removed in 5.0): the inner solve is exact (NNLS) - inner_min_decrease: float | None = None, # DEPRECATED no-op (warns; removed in 5.0): the inner solve is exact (NNLS) standardize: str = "std", # "std" (per-row SD, ddof=1) or "none" (deviation from R) alpha: float = 0.05, seed: int | None = None, # seeds the multistart Dirichlet draws @@ -1500,12 +1490,12 @@ results = etwfe_pois.fit(data, outcome='y_count', unit='id', time='t', first_tre section stores the headline inference under the canonical names `att` / `se` / `t_stat` / `p_value` / `conf_int` (4.0 design spec section 5; the storage flipped at the 4.0 cut, ledger rows M-050..M-058) - read -and construct with those. The 3.x `overall_att` / `overall_se` / -`overall_t_stat` / `overall_p_value` / `overall_conf_int` spellings (and -ContinuousDiD's `overall_att_se`-style spellings) are read-only -`FutureWarning` properties that relay the same values and are removed at -5.0; they are not constructor keywords and never appear in `to_dict()`. -3.x pickles load (the legacy keys are rewritten on load). The bootstrap +and construct with those. The 3.x `overall_*` spellings (and +ContinuousDiD's `overall_att_*`-style spellings) are GONE at 4.0 - the +planned FutureWarning window was retired at item 9b before any release +carried it, so reading one raises `AttributeError`; they are not constructor +keywords and never appear in `to_dict()`. 3.x pickles still load (the legacy +keys are rewritten on load). The bootstrap draw containers (`bootstrap_results.overall_att_se` etc.) keep their payload names. External adapters that `getattr(res, "se", None)`-style query the inference surface (e.g. @@ -1941,7 +1931,7 @@ Results container for `ChangesInChanges` under both methods (the `method` field ### LWDiDResults -Results from `LWDiD.fit()` (both timing modes). Flat-native headline fields `att`, `se`, `t_stat`, `p_value`, `conf_int` (when inference is unavailable - e.g. psm, degenerate designs - `se`/`t_stat`/`p_value` and both CI bounds are jointly NaN while `att` is RETAINED: the fail-closed contract), plus `n_obs`, `n_treated`, `n_control`, `df_inference`, `cluster_name`/`n_clusters`, and fit provenance (`rolling`, `estimation_method`, `vcov_type`, `alpha`, `control_group`, `n_bootstrap`, `seed`, `pscore_trim`, `psm_config`, `inference_basis`). Staggered fits also carry `cohort_effects` and `cohort_time_effects` dicts. Methods: `summary(alpha=None)` / `print_summary(alpha=None)` (alpha must equal the fit-time alpha - the family-wide guard), `to_dict()`, `to_dataframe(level='simple')` / `to_csv(path, level='simple')` (4.0: aggregate-backed - `aggregate(level).to_dataframe()` plus the `rolling`/`estimation_method`/`vcov_type` columns; `level` in `'simple'` (one headline row, default), `'group'` (per-cohort rows, staggered only), `'event_study'`; columns `conf_int_lower`/`conf_int_upper`, `n` = treated units - `n_obs`/`n_control` live in `to_dict()`), `aggregate(level)` with levels `'simple'` (view-relay of the fitted headline -> AggregationResult), `'event_study'` (per-event-time effects -> EventStudyResults), and `'group'` (per-cohort effects -> AggregationResult; staggered fits only - common-timing fits raise), and - common-timing `estimation_method='reg'` fits ONLY - `randomization_test()` and `wild_cluster_bootstrap()` (the latter ADDITIONALLY requires a `cluster=` fit - unclustered fits raise; both REPLAY the fitted collapsed design; no data arguments accepted; other fits reject informatively). NOTE: there is NO `overall_att` or `period_effects` attribute, not even as a deprecated alias - those surfaces were retired pre-release (the staggered containers above carry `overall_att` only as a 5.0-removed FutureWarning property); use `.att` for the overall effect and `aggregate('event_study')` for per-period effects. +Results from `LWDiD.fit()` (both timing modes). Flat-native headline fields `att`, `se`, `t_stat`, `p_value`, `conf_int` (when inference is unavailable - e.g. psm, degenerate designs - `se`/`t_stat`/`p_value` and both CI bounds are jointly NaN while `att` is RETAINED: the fail-closed contract), plus `n_obs`, `n_treated`, `n_control`, `df_inference`, `cluster_name`/`n_clusters`, and fit provenance (`rolling`, `estimation_method`, `vcov_type`, `alpha`, `control_group`, `n_bootstrap`, `seed`, `pscore_trim`, `psm_config`, `inference_basis`). Staggered fits also carry `cohort_effects` and `cohort_time_effects` dicts. Methods: `summary(alpha=None)` / `print_summary(alpha=None)` (alpha must equal the fit-time alpha - the family-wide guard), `to_dict()`, `to_dataframe(level='simple')` / `to_csv(path, level='simple')` (4.0: aggregate-backed - `aggregate(level).to_dataframe()` plus the `rolling`/`estimation_method`/`vcov_type` columns; `level` in `'simple'` (one headline row, default), `'group'` (per-cohort rows, staggered only), `'event_study'`; columns `conf_int_lower`/`conf_int_upper`, `n` = treated units - `n_obs`/`n_control` live in `to_dict()`), `aggregate(level)` with levels `'simple'` (view-relay of the fitted headline -> AggregationResult), `'event_study'` (per-event-time effects -> EventStudyResults), and `'group'` (per-cohort effects -> AggregationResult; staggered fits only - common-timing fits raise), and - common-timing `estimation_method='reg'` fits ONLY - `randomization_test()` and `wild_cluster_bootstrap()` (the latter ADDITIONALLY requires a `cluster=` fit - unclustered fits raise; both REPLAY the fitted collapsed design; no data arguments accepted; other fits reject informatively). NOTE: LWDiDResults never had the 3.x `overall_*` headline spelling or a per-period dict (and since 4.0 no container does - item 9b removed them library-wide); use `.att` for the overall effect and `aggregate('event_study')` for per-period effects. ### TROPResults @@ -2230,7 +2220,7 @@ permutation_test(data, outcome, treatment, post, unit, leave_one_out_test(data, outcome, treatment, post, unit, alpha=0.05) ``` -`permutation_test` / `leave_one_out_test` take `post=` (the pre-4.0 `time=` alias was removed in 4.0). The `run_placebo_test` / `run_all_placebo_tests` wrappers keep their `time` parameter. +`permutation_test` / `leave_one_out_test` take `post=` (the pre-4.0 `time=` alias was removed in 4.0). The `run_placebo_test` / `run_all_placebo_tests` wrappers take `time=` as the calendar period column only and derive the 0/1 post dummy for those two tests as `data[time].isin(post_periods)`; `post_periods` is required on both wrappers (4.0, ledger rows M-156 / M-157). All return `PlaceboTestResults` with attributes: `test_type`, `placebo_effect`, `se`, `t_stat`, `p_value`, `conf_int`, `n_obs`, `is_significant`. diff --git a/diff_diff/guides/llms-practitioner.txt b/diff_diff/guides/llms-practitioner.txt index e60783004..484e13af2 100644 --- a/diff_diff/guides/llms-practitioner.txt +++ b/diff_diff/guides/llms-practitioner.txt @@ -83,8 +83,8 @@ results.se # Standard error # Canonical quintet: every estimator result class above exposes the # headline inference as `att` / `se` / `conf_int` / `p_value` / `t_stat` # (spec section 5 of the 4.0 design). The staggered containers' 3.x -# `overall_*` / `overall_att_*` spellings still resolve to the same values -# through the 4.0 -> 5.0 deprecation window; read the canonical names. +# `overall_*` / `overall_att_*` spellings were removed at 4.0 (AttributeError); +# read the canonical names. results.att # headline ATT (ContinuousDiD: the ATT side; ACRT stays overall_acrt) results.se # its standard error ``` @@ -455,10 +455,12 @@ to your estimator's API. Examples: ```python from diff_diff import run_all_placebo_tests -# For simple 2x2 designs (requires binary time indicator, e.g. post=0/1): +# time= is the calendar period column; post_periods lists its post-treatment +# values (the 0/1 post dummy for the permutation / leave-one-out tests is +# derived from them - never pass a 0/1 indicator as time=): placebo = run_all_placebo_tests( - data, outcome='y', treatment='treated', time='post', - unit='unit_id', pre_periods=[0], post_periods=[1], + data, outcome='y', treatment='treated', time='period', + unit='unit_id', pre_periods=[0, 1], post_periods=[2, 3], n_permutations=500, seed=42, ) for test_name, result in placebo.items(): diff --git a/diff_diff/imputation_results.py b/diff_diff/imputation_results.py index b8886bc0e..9e55474c9 100644 --- a/diff_diff/imputation_results.py +++ b/diff_diff/imputation_results.py @@ -14,7 +14,6 @@ from diff_diff._deprecation import ( LEGACY_OVERALL_QUINTET, - deprecated_field_property, migrate_event_study_reference_sentinel, migrate_renamed_state_keys, ) @@ -237,27 +236,6 @@ class ImputationDiDResults(BaseResults, AggregationMixin): # default ("event_study",) - CS precedent, do not redeclare. _AGGREGATE_SUPPORTED = ("simple", "event_study", "group", "total") - # --- Deprecated 3.x storage names (ledger row M-052) --- - # FutureWarning from 4.0, removed at 5.0. Plain class attributes (no - # annotation) so they stay descriptors, never dataclass fields - the - # ``shimmed`` field-row shape tests/test_v4_matrix.py asserts - # (the LWDiDResults 3.10-alias precedent). - overall_att = deprecated_field_property( - "ImputationDiDResults", "overall_att", "att", removed_in="5.0" - ) - overall_se = deprecated_field_property( - "ImputationDiDResults", "overall_se", "se", removed_in="5.0" - ) - overall_t_stat = deprecated_field_property( - "ImputationDiDResults", "overall_t_stat", "t_stat", removed_in="5.0" - ) - overall_p_value = deprecated_field_property( - "ImputationDiDResults", "overall_p_value", "p_value", removed_in="5.0" - ) - overall_conf_int = deprecated_field_property( - "ImputationDiDResults", "overall_conf_int", "conf_int", removed_in="5.0" - ) - def __setstate__(self, state: Dict[str, Any]) -> None: """Migrate 3.x pickles written under the ``overall_*`` storage names. diff --git a/diff_diff/lwdid_results.py b/diff_diff/lwdid_results.py index 3d88be899..4a0680982 100644 --- a/diff_diff/lwdid_results.py +++ b/diff_diff/lwdid_results.py @@ -9,7 +9,6 @@ import numpy as np import pandas as pd -from diff_diff._deprecation import deprecated_field_property from diff_diff.aggregation import AggregationMixin, AggregationResult from diff_diff.results_base import ( _SUMMARY_ALPHA_MESSAGE, @@ -230,13 +229,6 @@ class LWDiDResults(BaseResults, AggregationMixin): # ------------------------------------------------------------------ # # Properties # # ------------------------------------------------------------------ # - # Deprecated 3.10-born aliases for the canonical quintet names (ledger rows - # M-151 / M-152): FutureWarning from 4.0, removed at 5.0. Plain class - # attributes (no annotation) so they stay descriptors, never dataclass - # fields - the ``shimmed`` field-row shape tests/test_v4_matrix.py asserts. - pvalue = deprecated_field_property("LWDiDResults", "pvalue", "p_value", removed_in="5.0") - ci = deprecated_field_property("LWDiDResults", "ci", "conf_int", removed_in="5.0") - @property def is_staggered(self) -> bool: """Whether this result comes from a staggered adoption design.""" diff --git a/diff_diff/practitioner.py b/diff_diff/practitioner.py index ae43246c2..45c1ebce1 100644 --- a/diff_diff/practitioner.py +++ b/diff_diff/practitioner.py @@ -300,10 +300,11 @@ def _placebo_step() -> Dict[str, Any]: ), code=( "from diff_diff import run_all_placebo_tests\n" - "# Requires binary time indicator (post=0/1), not multi-period:\n" + "# time= is the calendar period column; post_periods lists its\n" + "# post-treatment values (the 0/1 post dummy is derived from them):\n" "placebo = run_all_placebo_tests(\n" - " data, outcome='y', treatment='treated', time='post',\n" - " unit='unit_id', pre_periods=[0], post_periods=[1],\n" + " data, outcome='y', treatment='treated', time='period',\n" + " unit='unit_id', pre_periods=[0, 1], post_periods=[2, 3],\n" " n_permutations=500, seed=42)" ), priority="medium", diff --git a/diff_diff/results_base.py b/diff_diff/results_base.py index d2bb92d8a..f6937fcea 100644 --- a/diff_diff/results_base.py +++ b/diff_diff/results_base.py @@ -20,7 +20,6 @@ from __future__ import annotations -import warnings from dataclasses import dataclass, field from typing import Any, Dict, List, Optional, Tuple, Union, cast @@ -67,8 +66,8 @@ class BaseResults: - **Canonical quintet.** ``att``, ``se``, ``t_stat``, ``p_value``, ``conf_int`` bound to ONE coherent inference row. Native fields on every estimator container since the 4.0 storage flip (ledger rows - M-050..M-058; the legacy ``overall_*`` names live on as read-only - FutureWarning properties until 5.0); the last ``overall_*`` container, + M-050..M-058; the legacy ``overall_*`` names are gone - item 9b retired + their compatibility properties at 4.0); the last ``overall_*`` container, ``StaggeredTripleDiffResults``, was retired by the item-6 DDD container unification (row M-014) in favour of the canonical :class:`~diff_diff.TripleDifferenceResults`. @@ -215,59 +214,6 @@ def _json_safe_label(value: Any) -> Any: ) -def _significance_stars(p_value: float) -> str: - """Leaf-local twin of ``results._get_significance_stars`` (NaN -> ""). - - ``results_base`` imports no other ``diff_diff`` module, so the M-016 view - carries its own copy of the convention. - """ - if np.isnan(p_value): - return "" - if p_value < 0.001: - return "***" - if p_value < 0.01: - return "**" - if p_value < 0.05: - return "*" - if p_value < 0.1: - return "." - return "" - - -_PERIOD_EFFECTS_REMOVED_MSG = ( - "EventStudyResults.period_effects is deprecated and will be removed in " - "5.0; use to_dataframe() (one row per event_time, reference row included) " - "or the columnar att/se/t_stat/p_value/conf_int_lower/conf_int_upper " - "arrays instead." -) - -_PERIOD_EFFECTS_SCALE_MSG = ( - "period_effects is a compatibility view of the former calendar-keyed " - "per-period dict (row M-016) and exists only on time_scale='calendar' " - "surfaces; relative-time surfaces never had it - use to_dataframe()." -) - - -@dataclass(frozen=True) -class _PeriodEffectView: - """One row of the deprecated ``EventStudyResults.period_effects`` view (row M-016). - - Private, read-only snapshot of a non-reference row of a calendar surface; - mirrors the per-period record the 4.0 cut removed (row M-012). - ``is_significant`` is evaluated at the surface's ``alpha`` (NaN p-value -> - False); ``significance_stars`` follows the package convention. - """ - - period: Any - effect: float - se: float - t_stat: float - p_value: float - conf_int: Tuple[float, float] - is_significant: bool - significance_stars: str - - @dataclass class EventStudyResults(BaseResults): """Unified event-study representation (spec section 5, row M-092). @@ -668,52 +614,6 @@ def reference_periods(self) -> List[Any]: """ return [_json_safe_label(v) for v in self.event_time[self.is_reference].tolist()] - @property - def period_effects(self) -> Dict[Any, _PeriodEffectView]: - """Deprecated calendar-keyed per-period dict (row M-016; removed in 5.0). - - Compatibility view of the former ``period_effects`` dict: one - :class:`_PeriodEffectView` per NON-reference row, keyed by the row's - calendar label (the reference row is omitted, as the old dict did). - Only ``time_scale="calendar"`` surfaces carry it; reading it on a - relative-time surface raises ``AttributeError`` (so ``hasattr`` is - False there and nothing warns). Reading it emits ``FutureWarning``. - Use :meth:`to_dataframe` (one row per ``event_time``, reference row - included) or the columnar arrays instead. - """ - if self.time_scale != "calendar": - raise AttributeError(_PERIOD_EFFECTS_SCALE_MSG) - # Iterate the array itself (not .tolist(), which turns datetime64[ns] - # labels into integer nanoseconds); the elements hash/compare like the - # calendar labels the old dict was keyed by. - # Uniqueness is checked over EVERY row (reference included): a label - # shared by a reference row and an estimated row would otherwise - # surface the estimate under the reference's key. - labels = list(self.event_time) - if len(set(labels)) != len(labels): - raise ValueError( - "The event-study surface carries duplicate event_time labels " - f"({self.event_time.tolist()}); each label must appear exactly once " - "for a label-keyed period_effects view." - ) - warnings.warn(_PERIOD_EFFECTS_REMOVED_MSG, FutureWarning, stacklevel=2) - out: Dict[Any, _PeriodEffectView] = {} - for i, label in enumerate(self.event_time): - if self.is_reference[i]: - continue - p = float(self.p_value[i]) - out[label] = _PeriodEffectView( - period=label, - effect=float(self.att[i]), - se=float(self.se[i]), - t_stat=float(self.t_stat[i]), - p_value=p, - conf_int=(float(self.conf_int_lower[i]), float(self.conf_int_upper[i])), - is_significant=bool(p < self.alpha), - significance_stars=_significance_stars(p), - ) - return out - # ------------------------------------------------------------------ # Serialization contract # ------------------------------------------------------------------ @@ -1636,8 +1536,7 @@ def build_event_study_surface(results: Any) -> EventStudyResults: it (never a silent empty container). """ if isinstance(results, EventStudyResults): - # A surface is already the surface: never duck-sniff it (the M-016 - # period_effects view is a warning property - hasattr would fire it). + # A surface is already the surface: pass it through untouched. return results # Order matters: dCDH carries both event_study_effects and the split # placebo dict; HAD's columnar container has unique attributes; LPDiD's diff --git a/diff_diff/stacked_did_results.py b/diff_diff/stacked_did_results.py index 6c073e2ea..73834d07b 100644 --- a/diff_diff/stacked_did_results.py +++ b/diff_diff/stacked_did_results.py @@ -13,7 +13,6 @@ from diff_diff._deprecation import ( LEGACY_OVERALL_QUINTET, - deprecated_field_property, migrate_event_study_reference_sentinel, migrate_renamed_state_keys, ) @@ -281,27 +280,6 @@ def _aggregate_compute( estimator=type(self).__name__.replace("Results", ""), ) - # --- Deprecated 3.x storage names (ledger row M-054) --- - # FutureWarning from 4.0, removed at 5.0. Plain class attributes (no - # annotation) so they stay descriptors, never dataclass fields - the - # ``shimmed`` field-row shape tests/test_v4_matrix.py asserts - # (the LWDiDResults 3.10-alias precedent). - overall_att = deprecated_field_property( - "StackedDiDResults", "overall_att", "att", removed_in="5.0" - ) - overall_se = deprecated_field_property( - "StackedDiDResults", "overall_se", "se", removed_in="5.0" - ) - overall_t_stat = deprecated_field_property( - "StackedDiDResults", "overall_t_stat", "t_stat", removed_in="5.0" - ) - overall_p_value = deprecated_field_property( - "StackedDiDResults", "overall_p_value", "p_value", removed_in="5.0" - ) - overall_conf_int = deprecated_field_property( - "StackedDiDResults", "overall_conf_int", "conf_int", removed_in="5.0" - ) - def __repr__(self) -> str: """Concise string representation.""" sig = _get_significance_stars(self.p_value) diff --git a/diff_diff/staggered_results.py b/diff_diff/staggered_results.py index ef165ec62..e56d83680 100644 --- a/diff_diff/staggered_results.py +++ b/diff_diff/staggered_results.py @@ -13,7 +13,6 @@ from diff_diff._deprecation import ( LEGACY_OVERALL_QUINTET, - deprecated_field_property, migrate_event_study_reference_sentinel, migrate_renamed_state_keys, ) @@ -311,27 +310,6 @@ class CallawaySantAnnaResults(BaseResults, AggregationMixin): reference_event_times: Optional[Tuple[Any, ...]] = None _aggregation_kit: Optional[Any] = field(default=None, repr=False, compare=False) - # --- Deprecated 3.x storage names (ledger row M-050) --- - # FutureWarning from 4.0, removed at 5.0. Plain class attributes (no - # annotation) so they stay descriptors, never dataclass fields - the - # ``shimmed`` field-row shape tests/test_v4_matrix.py asserts - # (the LWDiDResults 3.10-alias precedent). - overall_att = deprecated_field_property( - "CallawaySantAnnaResults", "overall_att", "att", removed_in="5.0" - ) - overall_se = deprecated_field_property( - "CallawaySantAnnaResults", "overall_se", "se", removed_in="5.0" - ) - overall_t_stat = deprecated_field_property( - "CallawaySantAnnaResults", "overall_t_stat", "t_stat", removed_in="5.0" - ) - overall_p_value = deprecated_field_property( - "CallawaySantAnnaResults", "overall_p_value", "p_value", removed_in="5.0" - ) - overall_conf_int = deprecated_field_property( - "CallawaySantAnnaResults", "overall_conf_int", "conf_int", removed_in="5.0" - ) - def __setstate__(self, state: Dict[str, Any]) -> None: """Migrate 3.x pickles written under the ``overall_*`` storage names. diff --git a/diff_diff/sun_abraham.py b/diff_diff/sun_abraham.py index ba19c6245..ac943f8cf 100644 --- a/diff_diff/sun_abraham.py +++ b/diff_diff/sun_abraham.py @@ -30,7 +30,6 @@ from diff_diff._base import BaseEstimator from diff_diff._deprecation import ( LEGACY_OVERALL_QUINTET, - deprecated_field_property, migrate_renamed_state_keys, ) from diff_diff.bootstrap_utils import compute_effect_bootstrap_stats @@ -236,27 +235,6 @@ class SunAbrahamResults(BaseResults): unclustered (``cluster=False`` / one-way) fits, under a survey design (the survey block owns PSU reporting) and on pairs-bootstrap fits.""" - # --- Deprecated 3.x storage names (ledger row M-051) --- - # FutureWarning from 4.0, removed at 5.0. Plain class attributes (no - # annotation) so they stay descriptors, never dataclass fields - the - # ``shimmed`` field-row shape tests/test_v4_matrix.py asserts - # (the LWDiDResults 3.10-alias precedent). - overall_att = deprecated_field_property( - "SunAbrahamResults", "overall_att", "att", removed_in="5.0" - ) - overall_se = deprecated_field_property( - "SunAbrahamResults", "overall_se", "se", removed_in="5.0" - ) - overall_t_stat = deprecated_field_property( - "SunAbrahamResults", "overall_t_stat", "t_stat", removed_in="5.0" - ) - overall_p_value = deprecated_field_property( - "SunAbrahamResults", "overall_p_value", "p_value", removed_in="5.0" - ) - overall_conf_int = deprecated_field_property( - "SunAbrahamResults", "overall_conf_int", "conf_int", removed_in="5.0" - ) - def __setstate__(self, state: Dict[str, Any]) -> None: """Migrate 3.x pickles written under the ``overall_*`` storage names. diff --git a/diff_diff/synthetic_control.py b/diff_diff/synthetic_control.py index fdc2b822b..6ca1e5ebd 100644 --- a/diff_diff/synthetic_control.py +++ b/diff_diff/synthetic_control.py @@ -111,14 +111,6 @@ class SyntheticControl(BaseEstimator): outer V search (e.g. ``maxiter``, ``xatol``, ``fatol``). n_starts : int, default 4 Number of starting points for the multistart outer V search. - inner_max_iter : int, optional - **Deprecated, no effect** (``DeprecationWarning`` when passed; removed in - v5.0). The inner simplex solves (the donor weights at every V, and the - conformal proxy) are exact: one non-negative least squares each, with no - iteration budget to set. - inner_min_decrease : float, optional - **Deprecated, no effect** (``DeprecationWarning`` when passed; removed in - v5.0), for the same reason: an exact solve has no stopping tolerance. standardize : {"std", "none"}, default "std" Predictor standardization. ``"std"`` divides each predictor row by its standard deviation across donors+treated (ddof=1), matching R ``Synth``. @@ -144,8 +136,6 @@ def __init__( custom_v: Optional[Any] = None, optimizer_options: Optional[Dict[str, Any]] = None, n_starts: int = 4, - inner_max_iter: Optional[int] = None, - inner_min_decrease: Optional[float] = None, standardize: str = "std", alpha: float = 0.05, seed: Optional[int] = None, @@ -155,25 +145,6 @@ def __init__( self.custom_v = custom_v self.optimizer_options = optimizer_options self.n_starts = n_starts - # Deprecated warn-and-discard (ledger M-148 / M-149): the inner weight solve is - # exact (one NNLS), so neither parameter has any effect. Stored normalized to - # None so a set_params probe re-init warns exactly once for the call that - # passes them and never again on unrelated set_params / clone calls. - for _name, _val in ( - ("inner_max_iter", inner_max_iter), - ("inner_min_decrease", inner_min_decrease), - ): - if _val is not None: - warnings.warn( - f"{_name} is deprecated and has no effect: the synthetic control " - "donor weights are solved exactly (one non-negative least squares), " - "so there is no inner iteration budget or tolerance to set. It will " - "be removed in v5.0.", - DeprecationWarning, - stacklevel=2, - ) - self.inner_max_iter = None - self.inner_min_decrease = None self.standardize = standardize self.alpha = alpha self.seed = seed diff --git a/diff_diff/two_stage_results.py b/diff_diff/two_stage_results.py index b6969a1db..82ddba896 100644 --- a/diff_diff/two_stage_results.py +++ b/diff_diff/two_stage_results.py @@ -14,7 +14,6 @@ from diff_diff._deprecation import ( LEGACY_OVERALL_QUINTET, - deprecated_field_property, migrate_event_study_reference_sentinel, migrate_renamed_state_keys, ) @@ -226,27 +225,6 @@ class TwoStageDiDResults(BaseResults, AggregationMixin): # default ("event_study",) - CS precedent, do not redeclare. _AGGREGATE_SUPPORTED = ("simple", "event_study", "group", "total") - # --- Deprecated 3.x storage names (ledger row M-053) --- - # FutureWarning from 4.0, removed at 5.0. Plain class attributes (no - # annotation) so they stay descriptors, never dataclass fields - the - # ``shimmed`` field-row shape tests/test_v4_matrix.py asserts - # (the LWDiDResults 3.10-alias precedent). - overall_att = deprecated_field_property( - "TwoStageDiDResults", "overall_att", "att", removed_in="5.0" - ) - overall_se = deprecated_field_property( - "TwoStageDiDResults", "overall_se", "se", removed_in="5.0" - ) - overall_t_stat = deprecated_field_property( - "TwoStageDiDResults", "overall_t_stat", "t_stat", removed_in="5.0" - ) - overall_p_value = deprecated_field_property( - "TwoStageDiDResults", "overall_p_value", "p_value", removed_in="5.0" - ) - overall_conf_int = deprecated_field_property( - "TwoStageDiDResults", "overall_conf_int", "conf_int", removed_in="5.0" - ) - def __setstate__(self, state: Dict[str, Any]) -> None: """Migrate 3.x pickles written under the ``overall_*`` storage names. diff --git a/diff_diff/wooldridge_results.py b/diff_diff/wooldridge_results.py index 28653f5cc..61ce4d943 100644 --- a/diff_diff/wooldridge_results.py +++ b/diff_diff/wooldridge_results.py @@ -11,7 +11,6 @@ from diff_diff._deprecation import ( LEGACY_OVERALL_QUINTET, - deprecated_field_property, migrate_renamed_state_keys, ) from diff_diff.results_base import ( @@ -249,9 +248,9 @@ def __setstate__(self, state: Dict[str, Any]) -> None: - the convention every pre-3.9 fit ran under (NOT the 4.0 default). Missing ``unsupported_period_action`` defaults to ``"drop"``. Pickles written before the 4.0 storage flip (``docs/v4-design.md`` - section 5) carry the simple aggregate under ``overall_att`` / - ``overall_se`` / ...; the keys are rewritten to the canonical quintet - BEFORE the update (key renames first, default backfills after). + section 5) carry the simple aggregate under the legacy 3.x storage + keys; the keys are rewritten to the canonical quintet BEFORE the + update (key renames first, default backfills after). """ state = migrate_renamed_state_keys(state, LEGACY_OVERALL_QUINTET) self.__dict__.update(state) @@ -958,27 +957,6 @@ def plot_event_study(self, weights: str = "cell", **kwargs) -> None: se = {k: v["se"] for k, v in (self.event_study_effects or {}).items()} plot_event_study(effects=effects, se=se, alpha=self.alpha, **kwargs) - # --- Deprecated 3.x storage names (ledger row M-056) --- - # FutureWarning from 4.0, removed at 5.0. Plain class attributes (no - # annotation) so they stay descriptors, never dataclass fields - the - # ``shimmed`` field-row shape tests/test_v4_matrix.py asserts - # (the LWDiDResults 3.10-alias precedent). - overall_att = deprecated_field_property( - "WooldridgeDiDResults", "overall_att", "att", removed_in="5.0" - ) - overall_se = deprecated_field_property( - "WooldridgeDiDResults", "overall_se", "se", removed_in="5.0" - ) - overall_t_stat = deprecated_field_property( - "WooldridgeDiDResults", "overall_t_stat", "t_stat", removed_in="5.0" - ) - overall_p_value = deprecated_field_property( - "WooldridgeDiDResults", "overall_p_value", "p_value", removed_in="5.0" - ) - overall_conf_int = deprecated_field_property( - "WooldridgeDiDResults", "overall_conf_int", "conf_int", removed_in="5.0" - ) - def __repr__(self) -> str: n_gt = len(self.group_time_effects) att_str = f"{self.att:.4f}" if not np.isnan(self.att) else "NaN" diff --git a/docs/api/_autosummary/diff_diff.CallawaySantAnnaResults.rst b/docs/api/_autosummary/diff_diff.CallawaySantAnnaResults.rst index 5d7919456..9ff523feb 100644 --- a/docs/api/_autosummary/diff_diff.CallawaySantAnnaResults.rst +++ b/docs/api/_autosummary/diff_diff.CallawaySantAnnaResults.rst @@ -46,11 +46,6 @@ ~CallawaySantAnnaResults.influence_functions ~CallawaySantAnnaResults.is_significant ~CallawaySantAnnaResults.n_clusters - ~CallawaySantAnnaResults.overall_att - ~CallawaySantAnnaResults.overall_conf_int - ~CallawaySantAnnaResults.overall_p_value - ~CallawaySantAnnaResults.overall_se - ~CallawaySantAnnaResults.overall_t_stat ~CallawaySantAnnaResults.panel ~CallawaySantAnnaResults.pscore_fallback ~CallawaySantAnnaResults.pscore_trim diff --git a/docs/api/_autosummary/diff_diff.ChaisemartinDHaultfoeuilleResults.rst b/docs/api/_autosummary/diff_diff.ChaisemartinDHaultfoeuilleResults.rst index d940c62bb..aba18771d 100644 --- a/docs/api/_autosummary/diff_diff.ChaisemartinDHaultfoeuilleResults.rst +++ b/docs/api/_autosummary/diff_diff.ChaisemartinDHaultfoeuilleResults.rst @@ -39,11 +39,6 @@ ~ChaisemartinDHaultfoeuilleResults.is_significant ~ChaisemartinDHaultfoeuilleResults.linear_trends_effects ~ChaisemartinDHaultfoeuilleResults.normalized_effects - ~ChaisemartinDHaultfoeuilleResults.overall_att - ~ChaisemartinDHaultfoeuilleResults.overall_conf_int - ~ChaisemartinDHaultfoeuilleResults.overall_p_value - ~ChaisemartinDHaultfoeuilleResults.overall_se - ~ChaisemartinDHaultfoeuilleResults.overall_t_stat ~ChaisemartinDHaultfoeuilleResults.path_cumulated_event_study ~ChaisemartinDHaultfoeuilleResults.path_effects ~ChaisemartinDHaultfoeuilleResults.path_heterogeneity_effects diff --git a/docs/api/_autosummary/diff_diff.ContinuousDiDResults.rst b/docs/api/_autosummary/diff_diff.ContinuousDiDResults.rst index c38bbeca6..b68021f6b 100644 --- a/docs/api/_autosummary/diff_diff.ContinuousDiDResults.rst +++ b/docs/api/_autosummary/diff_diff.ContinuousDiDResults.rst @@ -40,15 +40,6 @@ ~ContinuousDiDResults.is_significant ~ContinuousDiDResults.n_bootstrap ~ContinuousDiDResults.num_knots - ~ContinuousDiDResults.overall_att - ~ContinuousDiDResults.overall_att_conf_int - ~ContinuousDiDResults.overall_att_p_value - ~ContinuousDiDResults.overall_att_se - ~ContinuousDiDResults.overall_att_t_stat - ~ContinuousDiDResults.overall_conf_int - ~ContinuousDiDResults.overall_p_value - ~ContinuousDiDResults.overall_se - ~ContinuousDiDResults.overall_t_stat ~ContinuousDiDResults.pscore_fallback ~ContinuousDiDResults.pscore_trim ~ContinuousDiDResults.rank_deficient_action diff --git a/docs/api/_autosummary/diff_diff.EfficientDiDResults.rst b/docs/api/_autosummary/diff_diff.EfficientDiDResults.rst index 2a363136c..89d17d46e 100644 --- a/docs/api/_autosummary/diff_diff.EfficientDiDResults.rst +++ b/docs/api/_autosummary/diff_diff.EfficientDiDResults.rst @@ -44,11 +44,6 @@ ~EfficientDiDResults.n_clusters ~EfficientDiDResults.omega_condition_numbers ~EfficientDiDResults.omega_ridge - ~EfficientDiDResults.overall_att - ~EfficientDiDResults.overall_conf_int - ~EfficientDiDResults.overall_p_value - ~EfficientDiDResults.overall_se - ~EfficientDiDResults.overall_t_stat ~EfficientDiDResults.pt_assumption ~EfficientDiDResults.ratio_clip ~EfficientDiDResults.reference_period diff --git a/docs/api/_autosummary/diff_diff.EventStudyResults.rst b/docs/api/_autosummary/diff_diff.EventStudyResults.rst index 230ee595a..5390370cb 100644 --- a/docs/api/_autosummary/diff_diff.EventStudyResults.rst +++ b/docs/api/_autosummary/diff_diff.EventStudyResults.rst @@ -35,7 +35,6 @@ ~EventStudyResults.estimation_spec ~EventStudyResults.event_time_convention ~EventStudyResults.n_kind - ~EventStudyResults.period_effects ~EventStudyResults.post_periods ~EventStudyResults.reference_event_times ~EventStudyResults.reference_period diff --git a/docs/api/_autosummary/diff_diff.ImputationDiDResults.rst b/docs/api/_autosummary/diff_diff.ImputationDiDResults.rst index 3c2bd06db..13629a301 100644 --- a/docs/api/_autosummary/diff_diff.ImputationDiDResults.rst +++ b/docs/api/_autosummary/diff_diff.ImputationDiDResults.rst @@ -36,11 +36,6 @@ ~ImputationDiDResults.is_significant ~ImputationDiDResults.leave_one_out ~ImputationDiDResults.n_clusters - ~ImputationDiDResults.overall_att - ~ImputationDiDResults.overall_conf_int - ~ImputationDiDResults.overall_p_value - ~ImputationDiDResults.overall_se - ~ImputationDiDResults.overall_t_stat ~ImputationDiDResults.pretrend_results ~ImputationDiDResults.significance_stars ~ImputationDiDResults.survey_metadata diff --git a/docs/api/_autosummary/diff_diff.StackedDiDResults.rst b/docs/api/_autosummary/diff_diff.StackedDiDResults.rst index d1733ae5c..3c7eb5af4 100644 --- a/docs/api/_autosummary/diff_diff.StackedDiDResults.rst +++ b/docs/api/_autosummary/diff_diff.StackedDiDResults.rst @@ -48,11 +48,6 @@ ~StackedDiDResults.n_stacked_obs ~StackedDiDResults.n_sub_experiments ~StackedDiDResults.n_treated_units - ~StackedDiDResults.overall_att - ~StackedDiDResults.overall_conf_int - ~StackedDiDResults.overall_p_value - ~StackedDiDResults.overall_se - ~StackedDiDResults.overall_t_stat ~StackedDiDResults.reference_event_times ~StackedDiDResults.significance_stars ~StackedDiDResults.survey_metadata diff --git a/docs/api/_autosummary/diff_diff.SunAbrahamResults.rst b/docs/api/_autosummary/diff_diff.SunAbrahamResults.rst index 3e6385ac4..d09bf76b1 100644 --- a/docs/api/_autosummary/diff_diff.SunAbrahamResults.rst +++ b/docs/api/_autosummary/diff_diff.SunAbrahamResults.rst @@ -39,11 +39,6 @@ ~SunAbrahamResults.inference_df ~SunAbrahamResults.is_significant ~SunAbrahamResults.n_clusters - ~SunAbrahamResults.overall_att - ~SunAbrahamResults.overall_conf_int - ~SunAbrahamResults.overall_p_value - ~SunAbrahamResults.overall_se - ~SunAbrahamResults.overall_t_stat ~SunAbrahamResults.reference_observed ~SunAbrahamResults.reference_period ~SunAbrahamResults.significance_stars diff --git a/docs/api/_autosummary/diff_diff.TwoStageDiDResults.rst b/docs/api/_autosummary/diff_diff.TwoStageDiDResults.rst index ae5a74857..391b6b18f 100644 --- a/docs/api/_autosummary/diff_diff.TwoStageDiDResults.rst +++ b/docs/api/_autosummary/diff_diff.TwoStageDiDResults.rst @@ -35,11 +35,6 @@ ~TwoStageDiDResults.event_study_vcov_index ~TwoStageDiDResults.is_significant ~TwoStageDiDResults.n_clusters - ~TwoStageDiDResults.overall_att - ~TwoStageDiDResults.overall_conf_int - ~TwoStageDiDResults.overall_p_value - ~TwoStageDiDResults.overall_se - ~TwoStageDiDResults.overall_t_stat ~TwoStageDiDResults.significance_stars ~TwoStageDiDResults.survey_metadata ~TwoStageDiDResults.vcov_type diff --git a/docs/api/_autosummary/diff_diff.dml_did_results.DMLDiDResults.rst b/docs/api/_autosummary/diff_diff.dml_did_results.DMLDiDResults.rst index 899b96cd1..42afa6443 100644 --- a/docs/api/_autosummary/diff_diff.dml_did_results.DMLDiDResults.rst +++ b/docs/api/_autosummary/diff_diff.dml_did_results.DMLDiDResults.rst @@ -57,11 +57,6 @@ ~DMLDiDResults.n_clusters ~DMLDiDResults.n_folds ~DMLDiDResults.outcome_learner - ~DMLDiDResults.overall_att - ~DMLDiDResults.overall_conf_int - ~DMLDiDResults.overall_p_value - ~DMLDiDResults.overall_se - ~DMLDiDResults.overall_t_stat ~DMLDiDResults.panel ~DMLDiDResults.propensity_learner ~DMLDiDResults.pscore_fallback diff --git a/docs/api/_autosummary/diff_diff.lwdid_results.LWDiDResults.rst b/docs/api/_autosummary/diff_diff.lwdid_results.LWDiDResults.rst index 8ad5e8d66..d6b349471 100644 --- a/docs/api/_autosummary/diff_diff.lwdid_results.LWDiDResults.rst +++ b/docs/api/_autosummary/diff_diff.lwdid_results.LWDiDResults.rst @@ -34,7 +34,6 @@ ~LWDiDResults.cband_crit_value ~LWDiDResults.cband_method ~LWDiDResults.cband_n_bootstrap - ~LWDiDResults.ci ~LWDiDResults.cluster_name ~LWDiDResults.cohort_effects ~LWDiDResults.cohort_time_effects @@ -53,7 +52,6 @@ ~LWDiDResults.params ~LWDiDResults.pscore_trim ~LWDiDResults.psm_config - ~LWDiDResults.pvalue ~LWDiDResults.ri_pvalue ~LWDiDResults.seed ~LWDiDResults.vcov diff --git a/docs/api/_autosummary/diff_diff.wooldridge_results.WooldridgeDiDResults.rst b/docs/api/_autosummary/diff_diff.wooldridge_results.WooldridgeDiDResults.rst index c1f799096..f7d6647fd 100644 --- a/docs/api/_autosummary/diff_diff.wooldridge_results.WooldridgeDiDResults.rst +++ b/docs/api/_autosummary/diff_diff.wooldridge_results.WooldridgeDiDResults.rst @@ -40,11 +40,6 @@ ~WooldridgeDiDResults.n_control_units ~WooldridgeDiDResults.n_obs ~WooldridgeDiDResults.n_treated_units - ~WooldridgeDiDResults.overall_att - ~WooldridgeDiDResults.overall_conf_int - ~WooldridgeDiDResults.overall_p_value - ~WooldridgeDiDResults.overall_se - ~WooldridgeDiDResults.overall_t_stat ~WooldridgeDiDResults.survey_metadata ~WooldridgeDiDResults.unsupported_period_action ~WooldridgeDiDResults.vcov_type diff --git a/docs/api/diagnostics.rst b/docs/api/diagnostics.rst index b2f22d2bf..3c61cf2fd 100644 --- a/docs/api/diagnostics.rst +++ b/docs/api/diagnostics.rst @@ -8,7 +8,10 @@ Placebo tests and diagnostic tools for validating DiD assumptions. run_placebo_test ---------------- -Main dispatcher for running different types of placebo tests. +Main dispatcher for running different types of placebo tests. Since 4.0 ``time`` is +the calendar period column only and ``post_periods`` is required for every +``test_type``: the 0/1 post dummy the permutation and leave-one-out tests need is +derived as ``data[time].isin(post_periods)`` (ledger row M-156). .. autofunction:: diff_diff.run_placebo_test @@ -120,7 +123,10 @@ Example run_all_placebo_tests --------------------- -Run comprehensive suite of diagnostic tests. +Run comprehensive suite of diagnostic tests. ``time`` is the calendar column and the +post dummy is derived from ``post_periods`` (ledger row M-157); a malformed +``post_periods`` or ``time`` column raises ``ValueError`` before any test runs instead of +being recorded as an error dict. .. autofunction:: diff_diff.run_all_placebo_tests diff --git a/docs/api/results.rst b/docs/api/results.rst index 65ec0b2ff..15bac3900 100644 --- a/docs/api/results.rst +++ b/docs/api/results.rst @@ -63,11 +63,11 @@ Shared bases introduced by the 4.0 API-unification program. ``BaseResults`` is the estimator-results base; ``Diagnostic`` marks diagnostic result containers (which carry no inference row); ``EventStudyResults`` is the unified per-event-time -representation. On calendar-time surfaces (``TwoWayFixedEffects`` event-study -mode) ``EventStudyResults.period_effects`` is a deprecated compatibility view -of the removed ``MultiPeriodDiDResults.period_effects`` (FutureWarning, ledger -M-016, removed in 5.0): a dict keyed by calendar period with the reference -period omitted; ``to_dataframe()`` is the successor. +representation. The removed ``MultiPeriodDiDResults.period_effects`` dict has +no successor attribute (its planned compatibility view, ledger M-016, was +retired at 4.0): ``EventStudyResults.to_dataframe()`` - one row per +``event_time``, reference row included - and the columnar arrays are the +per-period route. .. autoclass:: diff_diff.BaseResults :no-index: diff --git a/docs/doc-deps.yaml b/docs/doc-deps.yaml index 60d3f13dd..05b506a94 100644 --- a/docs/doc-deps.yaml +++ b/docs/doc-deps.yaml @@ -1603,10 +1603,10 @@ sources: note: "BaseResults / Diagnostic marker / EventStudyResults contract (4.0 program Phase 2; ledger rows M-091/M-092/M-093)" - path: docs/migration-4.0.md type: user_guide - note: "EventStudyResults.period_effects compatibility view (ledger row M-016) - worked example in the MultiPeriodDiD merge section" + note: "M-016 period_effects removed at 4.0 (item 9b) - the to_dataframe() route in the MultiPeriodDiD merge section" - path: diff_diff/guides/llms-full.txt type: user_guide - note: "EventStudyResults.period_effects compatibility view block (M-016 reader)" + note: "per-period effects block: to_dataframe() route (M-016 view removed at 4.0, item 9b)" diff_diff/bootstrap_utils.py: drift_risk: medium diff --git a/docs/methodology/REGISTRY.md b/docs/methodology/REGISTRY.md index 8494b8f67..93825c525 100644 --- a/docs/methodology/REGISTRY.md +++ b/docs/methodology/REGISTRY.md @@ -2,7 +2,7 @@ This document provides the academic foundations and key implementation requirements for each estimator in diff-diff. It serves as a reference for contributors and users who want to understand the theoretical basis of the methods. -**Result-class field naming.** Since the 4.0 storage flip (ledger rows M-050..M-058, PR 5b) the headline scalar inference fields are stored under ONE native naming pattern on every estimator results container: the canonical quintet `att` / `se` / `t_stat` / `p_value` / `conf_int` (`DiDResults`, `SyntheticDiDResults`, `TROPResults`, `TripleDifferenceResults`, `HeterogeneousAdoptionDiDResults`, `ChangesInChangesResults` always stored them this way; `CallawaySantAnnaResults`, `DMLDiDResults`, `SunAbrahamResults`, `ImputationDiDResults`, `TwoStageDiDResults`, `StackedDiDResults`, `EfficientDiDResults`, `WooldridgeDiDResults`, `ChaisemartinDHaultfoeuilleResults` and `ContinuousDiDResults` flipped from their 3.x `overall_*` / `overall_att_*` storage at the cut, with the fields renamed in slot so the generated `__init__` positional layout is unchanged). The last `overall_*` container, `StaggeredTripleDiffResults`, was retired by the item-6 DDD container unification (row M-014): `TripleDifferenceResults` serves both DDD designs with native canonical storage. The 3.x names (`overall_att`, `overall_se`, `overall_t_stat`, `overall_p_value`, `overall_conf_int`; on `ContinuousDiDResults` also the `overall_att_se` / `overall_att_t_stat` / `overall_att_p_value` / `overall_att_conf_int` spellings and the former silent `overall_se`-style aliases - nine spellings in all) live on as read-only `FutureWarning` properties built by `diff_diff._deprecation.deprecated_field_property`, removed at 5.0; `DMLDiDResults` re-declares the five shims so the warning names the subclass. The deprecated names are plain descriptors, not dataclass fields: they do NOT appear in `dataclasses.fields()` / `dataclasses.asdict()` / `to_dict()` output (spec section 5 forbids deprecated names in serialized output), they are not constructor keywords (`CallawaySantAnnaResults(overall_att=...)` raises `TypeError`), and assignment to one raises `AttributeError`. 3.x pickles load: every flipped container's `__setstate__` rewrites the legacy keys through `diff_diff._deprecation.migrate_renamed_state_keys` (an existing canonical key wins; the legacy key never lingers in `__dict__`). The canonical field is authoritative for documentation, semantics, and computation; the deprecated properties are pure read-throughs and inherit the `safe_inference()` joint-NaN consistency contract automatically. `ContinuousDiDResults` keeps its separate `overall_acrt` / `overall_acrt_se` / ... fields: ACRT is a distinct estimand, not a spelling of the headline. +**Result-class field naming.** Since the 4.0 storage flip (ledger rows M-050..M-058, PR 5b) the headline scalar inference fields are stored under ONE native naming pattern on every estimator results container: the canonical quintet `att` / `se` / `t_stat` / `p_value` / `conf_int` (`DiDResults`, `SyntheticDiDResults`, `TROPResults`, `TripleDifferenceResults`, `HeterogeneousAdoptionDiDResults`, `ChangesInChangesResults` always stored them this way; `CallawaySantAnnaResults`, `DMLDiDResults`, `SunAbrahamResults`, `ImputationDiDResults`, `TwoStageDiDResults`, `StackedDiDResults`, `EfficientDiDResults`, `WooldridgeDiDResults`, `ChaisemartinDHaultfoeuilleResults` and `ContinuousDiDResults` flipped from their 3.x `overall_*` / `overall_att_*` storage at the cut, with the fields renamed in slot so the generated `__init__` positional layout is unchanged). The last `overall_*` container, `StaggeredTripleDiffResults`, was retired by the item-6 DDD container unification (row M-014): `TripleDifferenceResults` serves both DDD designs with native canonical storage. The 3.x names (`overall_att`, `overall_se`, `overall_t_stat`, `overall_p_value`, `overall_conf_int`; on `ContinuousDiDResults` also the `overall_att_se` / `overall_att_t_stat` / `overall_att_p_value` / `overall_att_conf_int` spellings and the former silent `overall_se`-style aliases - nine spellings in all) are GONE at 4.0: item 9b of the cut (2026-11-01) retired the planned 4.0 -> 5.0 `FutureWarning` property shims before any release carried them (an unwarned break of the released 3.x names, documented in `docs/migration-4.0.md`), so reading one raises `AttributeError`, none appears in `dataclasses.fields()` / `dataclasses.asdict()` / `to_dict()` output, and none is a constructor keyword (`CallawaySantAnnaResults(att=...)` is the only spelling; the 3.x keyword raises `TypeError`). 3.x pickles load: every flipped container's `__setstate__` rewrites the legacy keys through `diff_diff._deprecation.migrate_renamed_state_keys` (an existing canonical key wins; the legacy key never lingers in `__dict__`). The canonical field is authoritative for documentation, semantics, and computation. `ContinuousDiDResults` keeps its separate `overall_acrt` / `overall_acrt_se` / ... fields: ACRT is a distinct estimand, not a spelling of the headline. - **Note:** Bootstrap draw containers (`CSBootstrapResults`, `SABootstrapResults`, `ImputationBootstrapResults`, `TwoStageBootstrapResults`, `EDiDBootstrapResults`: `overall_att_se` / `overall_att_ci` / `overall_att_p_value`; `DCDHBootstrapResults`: `overall_se` / `overall_ci` / `overall_p_value`) keep their estimator-local payload names through the 4.0 storage flip (user decision 2026-10-26): they hold no point estimate, so the headline-quintet contract does not apply, `overall_att_se` literally names "the SE of the overall ATT", and `ci` is not `conf_int`. `results.bootstrap_results.overall_att_se` therefore stays valid and warning-free. @@ -215,8 +215,8 @@ independent full-refit enumeration in `tests/test_wild_bootstrap.py::test_wcr_ma required (the midpoint default died with the class), the post-period average `avg_att` has no successor field (compute it from the surface: the `tests/helpers/event_study_average.py` arithmetic), `period_effects` - survives as a FutureWarning view on `EventStudyResults` (row M-016, - removed 5.0), and `unit=` auto-clusters the pooled spec at unit. The + has no successor attribute (its planned 4.0 -> 5.0 view on `EventStudyResults` + was retired at 4.0, row M-016, item 9b; read `to_dataframe()`), and `unit=` auto-clusters the pooled spec at unit. The methodology below is stated in the removed class's vocabulary and is kept verbatim as the archive record ([M-090], 4.0 cut item 9). @@ -419,7 +419,7 @@ where V is the VCV sub-matrix for post-treatment δ_e coefficients. - [ ] Supports unit and time FE via absorption - [ ] Endpoint binning for distant event times - [x] Average ATT correctly accounts for covariance between period effects -- [x] Returned `PeriodEffect` records with confidence intervals (through 3.x; since 4.0 the per-period rows live on `EventStudyResults`, with `period_effects` surviving as the M-016 FutureWarning view) +- [x] Returned `PeriodEffect` records with confidence intervals (through 3.x; since 4.0 the per-period rows live on `EventStudyResults` - `to_dataframe()`; the M-016 `period_effects` view was retired at 4.0, item 9b) - [x] Supports both balanced and unbalanced panels --- @@ -496,10 +496,11 @@ retired at 4.0 with it, row M-007 done). (no-silent-failures). This mode-level error fires BEFORE the survey/Conley front doors (contrast the static precedence contract in the Conley section). - **Note:** `EventStudyResults.period_effects` (the M-016 compatibility view, - calendar surfaces only, removed 5.0) evaluates `is_significant` at the - surface's `alpha`, not the removed per-period record's fixed 0.05 (user - decision 2026-10-25); duplicate calendar labels raise rather than silently - collapse. + calendar surfaces only) was retired at 4.0 (item 9b, 2026-11-01) before any + release carried it; the surface's `to_dataframe()` and columnar arrays are + the only per-period route. HISTORY: the view evaluated `is_significant` at + the surface's `alpha`, not the removed per-period record's fixed 0.05 (user + decision 2026-10-25). - **Note:** The clustered `hc2_bm` contrast batch keeps the former post-period-average column as the batch-relative noise-floor guard's anchor (`_cr2_bm_dof_inner`: a contrast is NaN'd when its scaled max|B| falls @@ -1368,7 +1369,7 @@ The multiplier bootstrap uses random weights w_i with E[w]=0 and Var(w)=1: - **Note:** Non-survey DR path also includes nuisance IF corrections (PS + OR), matching the survey path structure (Phase 7a). Previously used plug-in IF only. As of v3.7 the non-survey reg and ipw paths carry their corrections too (OR estimation-effect / PS score), so the nuisance-IF treatment is method-uniform. - **Note (post-fit aggregate() - rows M-020/M-117):** `fit(aggregate=, balance_e=)` was deprecated in 3.9 and REMOVED at 4.0 (PR 3b of the cut: `fit()` computes no event-study/group surface, so the legacy `event_study_effects`/`group_effects` fields - and `cband_crit_value`/`event_study_vcov`/`event_study_vcov_index`/`event_study_df` - are None after EVERY fit, `to_dataframe(level='event_study')` returns `aggregate('event_study').to_dataframe()` - the unified `EVENT_STUDY_SCHEMA` frame - since the 4.0 cut ([M-093] item 8b; bootstrapped fits replay the fit-time bootstrap on every call), while `to_dataframe(level='group')` still raises on fresh fits with the post-fit pointer, and `survey_design` is positional slot 7) in favor of post-fit `results.aggregate(type, balance_e=)` on the fit-retained aggregation kit ('simple' relays the stored overall inference; 'event_study'/'group' recompute from the kit — on BOOTSTRAPPED fits they REPLAY the fit-time multiplier bootstrap from the kit's `BootstrapReplaySpec` RNG-state snapshot: percentile se/CI/cband matching a fit-time aggregation to BLAS reassociation (~1 ULP, `assert_allclose`, never bit-identity; the discrete percentile p-value is a count statistic outside that claim), df cleared (no analytical provenance beside percentile inference), vcov/vcov_index from the same bootstrap draws (see the bootstrap event-study covariance Note above). **Note (df_survey two-channel contract):** the container-level `df_survey` SCALAR - the fit's resolved scalar inference df (`EventStudyResults.df_survey`) - DELIBERATELY persists on bootstrapped replayed (CS, DMLDiD, EfficientDiD publish it identically) and relayed (ImputationDiD/TwoStageDiD/ContinuousDiD, 4.0 PR 3c) surfaces: it is a live consumer channel (HonestDiD's container branches consume it as the `df=` for their critical values on the containers they admit), and only the PER-ROW `df` column is the per-row inference-provenance channel that percentile bootstrap clears. A bootstrapped ImputationDiD survey fit with `pretrends=True` clears the per-row channel even though the lead rows retained analytical survey-df inference (the bootstrap loop skips pre-period horizons) - the shipped producer convention, backend-stamped and failing closed on a Rust-vs-NumPy weight-backend mismatch or a pre-replay legacy pickle — the M-027 per-level policy, with the former blanket fail-closed retired). `DiagnosticReport` now derives the event-study container internally on plain fits (raw `event_study_effects` absent), so its `parallel_trends`, `pretrends_power`, and `sensitivity` checks run on every CS fit — CS containers are M-093-admitted into `compute_pretrends_power` / `HonestDiD.sensitivity_analysis` with pinned raw-route parity, and the raw field, when present (the requested-but-empty `{}` included), always takes precedence. `heterogeneity` is unaffected (it reads `group_time_effects` on plain fits). Bootstrapped fits now DERIVE successfully via the percentile-bootstrap replay: the derived container carries the covariance of the bootstrap draws as `vcov`, so `parallel_trends` runs the joint Wald test and `pretrends_power`/`sensitivity` use the full covariance (the Bonferroni and diagonal-covariance fallbacks remain where no bootstrap covariance is stored); the replay's re-emitted fit-time warnings are recorded and republished per section. The fail-closed skip remains for kit-less/legacy pickles, backend-mismatched replays, and bootstrapped ImputationDiD/TwoStageDiD/ContinuousDiD fits whose fit-materialized surface was fail-soft cleared (since 4.0 PR 3c those three RELAY the surface computed at fit - see their bootstrapped-fit relay Notes). The recompute levels' replay RE-EMITS the fit-time bootstrap warnings by design (the relay levels' no-re-warn convention is scoped to 'simple'/'total'). (8a, [M-093]: the carrier's reference-only horizons carry `is_reference: True` and no `n_groups`.) -- **Note (post-fit `aggregate('total')` - the estimator-owned total incremental outcome, 3.10):** (a) **Estimand:** `total = C x overall_att` with `se = C x overall_se`, CI scaled by `C`, `t`/`p`/`df` inherited unchanged - an exact relay CONDITIONAL ON THE REALIZED AGGREGATION MASS (never call it unqualified "exact"): `C` is the design-fixed complete-case treated-observation count on every admitted routing, the inherited SE prices the overall's full inference (WIF/estimated-share terms included) but treats `C` as fixed at its realized value, and the random-mass `att*dC` variance term is deliberately out of scope (the DEFERRED survey/RC remainder). Every SE branch is homogeneous of degree 1 in the scaling, so the relay is exact under analytical AND percentile-bootstrap inference. (b) **Fail-closed routing gates**, in order: (1) repeated-cross-section-routed fits (`panel=False`, or genuinely-unbalanced `allow_unbalanced_panel=True` fits - a balanced panel with the flag stays panel-routed and admitted) raise `NotImplementedError` - the RC cohort mass counts UNITS and weights every kept post period by it, a ~T-fold overcount of treated observations on a T-wave RCS (execution-verified); (2) fits declaring a `survey_design=` raise - the realized-mass relay omits the survey mass-uncertainty term and design-aware population-scale totals are not implemented (retained weight scale differs by design family: analytic pweight resolves normalized to sum = n and CS retains no PER-OBSERVATION raw-weight record - the `survey_metadata` scalars report raw-scale `sum_weights`/`weight_range` provenance, but not the per-obs raw vector a population-scale total would need; CS accepts pweight only - while replicate designs retain raw scale); the gate reads the fit-time `is_survey_fit` KIT snapshot, so bare-`cluster=` fits are ADMITTED (their synthesized all-ones internal design is not a survey) and a post-fit edit of the public `survey_metadata` cannot bypass it; the DECLARATION BOUNDARY is deliberate - an explicit unweighted `SurveyDesign(psu=...)` fit is numerically identical to bare `cluster=` yet fails closed by declaration (declaring survey_design opts into the survey contract; conservative, never wrong numbers; the message points at the `cluster=` route); a combined survey+RC fit deterministically gets the RC message; (3) the coincidence check: on bare-`cluster=` fits `_aggregate_simple` weights kept cells by the synthesized cohort masses (full cohort size per kept cell), and when kept cells have INCOMPLETE treated support that realized mass diverges from the complete-case count - the same overcount class as (1) - so the fit raises rather than publishing an ambiguous mass; clean bare-`cluster=` fits (the normal case) are admitted with `C` equal to both counts. Panel-routed pre-upgrade (legacy) kits missing the fit-time snapshots raise the refit message - never a mutable-field or public-replay fallback. (c) **Filter-replay contract:** `C` replays `_aggregate_simple`'s cell selection verbatim over the immutable `agg_gt_cells` fit-time kit snapshot (EDiD snapshot discipline; the kit builder stashes `(g, t, effect, n_treated)` 4-tuples in dict order) - anticipation filter FIRST (cells with `g - anticipation <= t < g` COUNT as post, the business-facing subtlety; universal-base reference cells are excluded by this filter alone, their base period being `< g - anticipation` by construction), finite-effect mask SECOND, weight = cohort mass where `fixed_cohort_agg_weights` provides one else per-cell complete-case `n_treated` (the source's `agg_weight` fallback is deliberately absent - RC-only, gated out), pairwise `np.sum`; the overall scalars come from the same stored fields the `'simple'` relay reads, so a post-fit mutation of `group_time_effects` moves NEITHER factor and `total == C x aggregate('simple').att` cannot desynchronize (pinned by a mutation test). NEVER use `agg_total_weight` (the RC all-units WIF mass). (d) **NaN rules:** empty post set / all-NaN effects give `C = NaN` and an all-NaN row WITHOUT re-emitting the fit-time UserWarnings (the RELAY-level no-re-warn convention — the NaN row is the signal; the recompute levels' bootstrap replay re-emits by design); with finite `C` the relay is VERBATIM - inherited NaNs (a degenerate fit's `(nan, nan)` CI beside finite att/se = 0) pass through, mirroring `aggregate('simple')` per the repo's non-estimable-row convention (att/n are never blanked by NaN inference fields); the ONLY additional blanking is true float overflow (a finite overall value made non-finite BY the `x C` scaling - unreachable in practice, guarded per this code family's finiteness convention). (e) **Bootstrap:** 'total' is a RELAY level - available on bootstrapped fits with the df column NaN (the M-027 per-level policy). (f) **Container conventions:** single row, `level="total"`, `label=["total"]`, `target=["total"]` (a total incremental outcome, NOT an ATT - the single-non-att neutral "estimate" rendering applies), `n = [C]` with `n_kind="obs"`, `weight=[1.0]`, per-class df carrier (CS: `resolve_inference_df` - finite on admitted bare-`cluster=` fits, NaN on plain analytical panel fits). Per-level `n` semantics are deliberately DIFFERENT: 'total''s `n` is the treated observations entering the support, while 'simple''s `n` stays treated+control units by contract - `n_kind` is per-container and each is documented, satisfying the never-conflate rule. (g) **MMM admission:** both `diff_diff.mmm` exporters accept the total container ALONE - `scale` (numeric or `"auto"`) is rejected as double-counting; for overall-total exports this route supersedes `scale="auto"` (see the MMM section). The total remains meaningful only for outcomes additive in levels - the same unverifiable caveat every scale route carries. +- **Note (post-fit `aggregate('total')` - the estimator-owned total incremental outcome, 3.10):** (a) **Estimand:** `total = C x att` with `se = C x se`, CI scaled by `C`, `t`/`p`/`df` inherited unchanged - an exact relay CONDITIONAL ON THE REALIZED AGGREGATION MASS (never call it unqualified "exact"): `C` is the design-fixed complete-case treated-observation count on every admitted routing, the inherited SE prices the overall's full inference (WIF/estimated-share terms included) but treats `C` as fixed at its realized value, and the random-mass `att*dC` variance term is deliberately out of scope (the DEFERRED survey/RC remainder). Every SE branch is homogeneous of degree 1 in the scaling, so the relay is exact under analytical AND percentile-bootstrap inference. (b) **Fail-closed routing gates**, in order: (1) repeated-cross-section-routed fits (`panel=False`, or genuinely-unbalanced `allow_unbalanced_panel=True` fits - a balanced panel with the flag stays panel-routed and admitted) raise `NotImplementedError` - the RC cohort mass counts UNITS and weights every kept post period by it, a ~T-fold overcount of treated observations on a T-wave RCS (execution-verified); (2) fits declaring a `survey_design=` raise - the realized-mass relay omits the survey mass-uncertainty term and design-aware population-scale totals are not implemented (retained weight scale differs by design family: analytic pweight resolves normalized to sum = n and CS retains no PER-OBSERVATION raw-weight record - the `survey_metadata` scalars report raw-scale `sum_weights`/`weight_range` provenance, but not the per-obs raw vector a population-scale total would need; CS accepts pweight only - while replicate designs retain raw scale); the gate reads the fit-time `is_survey_fit` KIT snapshot, so bare-`cluster=` fits are ADMITTED (their synthesized all-ones internal design is not a survey) and a post-fit edit of the public `survey_metadata` cannot bypass it; the DECLARATION BOUNDARY is deliberate - an explicit unweighted `SurveyDesign(psu=...)` fit is numerically identical to bare `cluster=` yet fails closed by declaration (declaring survey_design opts into the survey contract; conservative, never wrong numbers; the message points at the `cluster=` route); a combined survey+RC fit deterministically gets the RC message; (3) the coincidence check: on bare-`cluster=` fits `_aggregate_simple` weights kept cells by the synthesized cohort masses (full cohort size per kept cell), and when kept cells have INCOMPLETE treated support that realized mass diverges from the complete-case count - the same overcount class as (1) - so the fit raises rather than publishing an ambiguous mass; clean bare-`cluster=` fits (the normal case) are admitted with `C` equal to both counts. Panel-routed pre-upgrade (legacy) kits missing the fit-time snapshots raise the refit message - never a mutable-field or public-replay fallback. (c) **Filter-replay contract:** `C` replays `_aggregate_simple`'s cell selection verbatim over the immutable `agg_gt_cells` fit-time kit snapshot (EDiD snapshot discipline; the kit builder stashes `(g, t, effect, n_treated)` 4-tuples in dict order) - anticipation filter FIRST (cells with `g - anticipation <= t < g` COUNT as post, the business-facing subtlety; universal-base reference cells are excluded by this filter alone, their base period being `< g - anticipation` by construction), finite-effect mask SECOND, weight = cohort mass where `fixed_cohort_agg_weights` provides one else per-cell complete-case `n_treated` (the source's `agg_weight` fallback is deliberately absent - RC-only, gated out), pairwise `np.sum`; the overall scalars come from the same stored fields the `'simple'` relay reads, so a post-fit mutation of `group_time_effects` moves NEITHER factor and `total == C x aggregate('simple').att` cannot desynchronize (pinned by a mutation test). NEVER use `agg_total_weight` (the RC all-units WIF mass). (d) **NaN rules:** empty post set / all-NaN effects give `C = NaN` and an all-NaN row WITHOUT re-emitting the fit-time UserWarnings (the RELAY-level no-re-warn convention — the NaN row is the signal; the recompute levels' bootstrap replay re-emits by design); with finite `C` the relay is VERBATIM - inherited NaNs (a degenerate fit's `(nan, nan)` CI beside finite att/se = 0) pass through, mirroring `aggregate('simple')` per the repo's non-estimable-row convention (att/n are never blanked by NaN inference fields); the ONLY additional blanking is true float overflow (a finite overall value made non-finite BY the `x C` scaling - unreachable in practice, guarded per this code family's finiteness convention). (e) **Bootstrap:** 'total' is a RELAY level - available on bootstrapped fits with the df column NaN (the M-027 per-level policy). (f) **Container conventions:** single row, `level="total"`, `label=["total"]`, `target=["total"]` (a total incremental outcome, NOT an ATT - the single-non-att neutral "estimate" rendering applies), `n = [C]` with `n_kind="obs"`, `weight=[1.0]`, per-class df carrier (CS: `resolve_inference_df` - finite on admitted bare-`cluster=` fits, NaN on plain analytical panel fits). Per-level `n` semantics are deliberately DIFFERENT: 'total''s `n` is the treated observations entering the support, while 'simple''s `n` stays treated+control units by contract - `n_kind` is per-container and each is documented, satisfying the never-conflate rule. (g) **MMM admission:** both `diff_diff.mmm` exporters accept the total container ALONE - `scale` (numeric or `"auto"`) is rejected as double-counting; for overall-total exports this route supersedes `scale="auto"` (see the MMM section). The total remains meaningful only for outcomes additive in levels - the same unverifiable caveat every scale route carries. **Reference implementation(s):** - R: `did::att_gt()` (Callaway & Sant'Anna's official package) @@ -1490,7 +1491,7 @@ Dynamic placebos `DID^{pl}_l` look backward from each group's reference period, - **Note (Phase 2 cost-benefit delta SE):** When `L_max >= 2`, `att` holds the cost-benefit `delta`. Its SE is computed via the delta method from per-horizon SEs: `SE(delta) = sqrt(sum w_l^2 * SE(DID_l)^2)`, treating horizons as independent (conservative under Assumption 8). When bootstrap is enabled, per-horizon bootstrap SEs flow through the delta-method formula, so `se` reflects bootstrap-derived per-horizon uncertainty but the delta aggregation itself uses normal-theory (not bootstrap percentile). This is an intentional exception to the general bootstrap-inference-surface contract: `p_value` and `conf_int` for `delta` use `safe_inference(delta, delta_se)`, not percentile bootstrap, because the delta is a derived aggregate rather than a directly bootstrapped estimand. -- **Note (post-fit `aggregate()` is a view - row M-026):** `results.aggregate('event_study')` and `results.aggregate('simple')` are pure VIEWS over stored fields - nothing is recomputed. `'event_study'` returns the unified `EventStudyResults` container (Phase-1 `L_max=None` fits: the 2-row l=1 view; `L_max >= 1`: the multi-horizon `l1_first_switch` surface); `'simple'` a one-row `AggregationResult` relaying `overall_att/se/t/p/CI` bit-exactly with the estimand-aware `target` label (DID_M / DID_1 / delta, or the trends-linear first-difference label whose overall row is all-NaN by design). Because it is a view, BOOTSTRAP FITS ARE PERMITTED - the library-wide per-level relay rule (since M-027 the recompute adopters' 'simple' relays are permitted on bootstrap fits too; their kit-based recompute levels REPLAY the fit-time multiplier bootstrap on CallawaySantAnna, DMLDiD (via the inherited CS kit channel), and EfficientDiD, while the three hybrids ImputationDiD/TwoStageDiD/ContinuousDiD RELAY the percentile surface materialized at fit since the fit-time keyword was removed at 4.0 (PR 3c) - a recompute must never silently substitute analytical inference); here each row relays exactly the inference the fit stored, including the delta's analytical-with-df numbers under bootstrap per the `Note (Phase 2 cost-benefit delta SE)` above (the view's `df` column resolves from the actual inference path, not the bootstrap-cleared `event_study_df` channel). The dCDH event-study CONTAINER is deliberately rejected by `compute_honest_did`/`compute_pretrends_power` - its l1 placebo semantics need HonestDiD's native dCDH branch (mandatory reinterpretation warning + horizon trimming); pass the results object itself. +- **Note (post-fit `aggregate()` is a view - row M-026):** `results.aggregate('event_study')` and `results.aggregate('simple')` are pure VIEWS over stored fields - nothing is recomputed. `'event_study'` returns the unified `EventStudyResults` container (Phase-1 `L_max=None` fits: the 2-row l=1 view; `L_max >= 1`: the multi-horizon `l1_first_switch` surface); `'simple'` a one-row `AggregationResult` relaying `att/se/t/p/CI` bit-exactly with the estimand-aware `target` label (DID_M / DID_1 / delta, or the trends-linear first-difference label whose overall row is all-NaN by design). Because it is a view, BOOTSTRAP FITS ARE PERMITTED - the library-wide per-level relay rule (since M-027 the recompute adopters' 'simple' relays are permitted on bootstrap fits too; their kit-based recompute levels REPLAY the fit-time multiplier bootstrap on CallawaySantAnna, DMLDiD (via the inherited CS kit channel), and EfficientDiD, while the three hybrids ImputationDiD/TwoStageDiD/ContinuousDiD RELAY the percentile surface materialized at fit since the fit-time keyword was removed at 4.0 (PR 3c) - a recompute must never silently substitute analytical inference); here each row relays exactly the inference the fit stored, including the delta's analytical-with-df numbers under bootstrap per the `Note (Phase 2 cost-benefit delta SE)` above (the view's `df` column resolves from the actual inference path, not the bootstrap-cleared `event_study_df` channel). The dCDH event-study CONTAINER is deliberately rejected by `compute_honest_did`/`compute_pretrends_power` - its l1 placebo semantics need HonestDiD's native dCDH branch (mandatory reinterpretation warning + horizon trimming); pass the results object itself. - **Note (dynamic placebo SE - library extension):** Dynamic placebos `DID^{pl}_l` (negative horizons in `placebo_event_study`) now have analytical SE and bootstrap SE when `L_max >= 1`. The placebo IF uses the same cohort-recentered structure as positive horizons, applied to backward outcome differences `Y_{g, F_g-1-l} - Y_{g, F_g-1}` with the dual-eligibility control pool (forward + backward observation required). The paper's Theorem 1 variance result is stated for `DID_l`, not `DID^{pl}_l` - this extension applies the same IF/variance structure to the placebo estimand as a library enhancement. The single-period placebo `DID_M^pl` (`L_max=None`) retains NaN SE because the per-period aggregation path has no IF derivation. @@ -2588,7 +2589,7 @@ The pooled estimator is `DID^{CBWSDID}_e = Σ_a (N^D_a/N^D_Ω)(Δ̄^D_{a,e} − - **Note:** Provenance exposure (4.0 program row M-092, no numeric change): the event-time sub-block of the pooled-regression coefficient covariance is now persisted as `StackedDiDResults.event_study_vcov` (+ `event_study_vcov_index` = the estimated event times; the synthesized reference period is not a regression column and is absent). Persisted in every inference mode - analytical hc1/hc2_bm sandwich, survey replicate refit, and survey TSL all reassign the coefficient covariance the reported ES SEs are read from, so the marginal SEs are always this matrix's diagonal. `event_study_df` records, per event time, the df actually passed to `safe_inference` (the per-event Bell-McCaffrey Satterthwaite contrast df under `hc2_bm`, the scalar survey df under survey designs, NaN on the normal-theory hc1 path and on hc2_bm fail-closed rows). DiagnosticReport's parallel-trends check consumes the persisted VCV via its joint-Wald path for hc1/survey fits; `hc2_bm` fits are routed to Bonferroni over the BM-adjusted per-row p-values instead (the chi-square Wald reference would discard the CR2/BM small-sample correction; multi-constraint AHT/HTZ CR2 testing is tracked in DEFERRED.md - see REPORTING.md "hc2_bm parallel-trends policy"). - **Note:** Inference is conditional-on-the-estimated-weights cluster-robust (the existing `hc1`/`hc2_bm` path with `W_{sa}` as the WLS weights) — the paper's default. The paper's weight-re-estimating bootstrap is NOT implemented in v1 (deliberate scope; entropy balancing is smooth so the Abadie–Imbens (2008) nonsmooth-matching bootstrap caveat does not apply). `cluster` is orthogonal to `b_{sa}` (weights conditioned-on); default `unit` matches the paper. - **Note:** v1 scope — only `balance="entropy"` with `weighting="aggregate"`. `balance` + `population`/`sample_share` and `balance` + `survey_design=` raise `NotImplementedError`; matching-based balancing and the repeated `0→1/1→0` episode extension are out of scope. -- **Note (post-fit aggregate() - row M-024):** `fit(aggregate=)` was deprecated in 3.9 and REMOVED at 4.0 (PR 3b of the cut; its `group`/`all`/unknown ValueErrors died with it - `aggregate('group')` already fails closed in the mixin - and `population`/`survey_design`/`covariates` are positional slots 6/7/8) and the event-study surface is ALWAYS materialized at fit — the pooled regression always included the event-time interactions, so only the extraction and, under `hc2_bm`, the per-event Bell-McCaffrey contrast DOFs were fit-time-gated; both gates are gone (the param had been behaviorally inert since 3.9). `aggregate('event_study')` returns the unified `EventStudyResults` container (a pure view; no recomputation, so every stored inference mode — survey TSL, replicate refit, hc2_bm fail-close — relays faithfully); `aggregate('simple')` relays `overall_att/se/t/p/CI` bit-exactly with `target="att"` (the scalar is the equally-weighted post-period average, NOT the per-event-time trimmed aggregate ATT `θ^e_κ` — `describe_target_parameter` remains the estimand's prose source of truth), `n = n_treated_units` with `n_kind="units"` (TREATED-only scope: under dynamic clean-control definitions a later-treated unit is treated in its own sub-experiment and a clean control in earlier ones, so the treated and control unit sets OVERLAP and no disjoint total exists — cross-container `n` comparisons vs CallawaySantAnna's treated+control convention are out of contract), and `df = inference_df` (the stored overall inference's exact provenance). Consequences of the always-computed surface: plain-fit `summary()` renders the event-study table, `to_dataframe('event_study')` is total on 3.9+ fits, `plot_event_study` renders plain fits, DiagnosticReport's `heterogeneity` (always) and `parallel_trends` (`kappa_pre >= 2`) checks run on plain fits, and the `hc2_bm` overall-ATT contrast now shares the m>1 batch of `_compute_cr2_bm_contrast_dof`, whose degeneracy guard is batch-relative — the noise-floor carve-out on the `Wald_test(test="HTZ")$df_denom` parity claim in the variance-families bullets above widens quantitatively (unchanged behavior when tripped: fail-closed NaN inference plus linalg's loud noise-floor UserWarning; the absolute floor already made this reachable at m=1). +- **Note (post-fit aggregate() - row M-024):** `fit(aggregate=)` was deprecated in 3.9 and REMOVED at 4.0 (PR 3b of the cut; its `group`/`all`/unknown ValueErrors died with it - `aggregate('group')` already fails closed in the mixin - and `population`/`survey_design`/`covariates` are positional slots 6/7/8) and the event-study surface is ALWAYS materialized at fit — the pooled regression always included the event-time interactions, so only the extraction and, under `hc2_bm`, the per-event Bell-McCaffrey contrast DOFs were fit-time-gated; both gates are gone (the param had been behaviorally inert since 3.9). `aggregate('event_study')` returns the unified `EventStudyResults` container (a pure view; no recomputation, so every stored inference mode — survey TSL, replicate refit, hc2_bm fail-close — relays faithfully); `aggregate('simple')` relays `att/se/t/p/CI` bit-exactly with `target="att"` (the scalar is the equally-weighted post-period average, NOT the per-event-time trimmed aggregate ATT `θ^e_κ` — `describe_target_parameter` remains the estimand's prose source of truth), `n = n_treated_units` with `n_kind="units"` (TREATED-only scope: under dynamic clean-control definitions a later-treated unit is treated in its own sub-experiment and a clean control in earlier ones, so the treated and control unit sets OVERLAP and no disjoint total exists — cross-container `n` comparisons vs CallawaySantAnna's treated+control convention are out of contract), and `df = inference_df` (the stored overall inference's exact provenance). Consequences of the always-computed surface: plain-fit `summary()` renders the event-study table, `to_dataframe('event_study')` is total on 3.9+ fits, `plot_event_study` renders plain fits, DiagnosticReport's `heterogeneity` (always) and `parallel_trends` (`kappa_pre >= 2`) checks run on plain fits, and the `hc2_bm` overall-ATT contrast now shares the m>1 batch of `_compute_cr2_bm_contrast_dof`, whose degeneracy guard is batch-relative — the noise-floor carve-out on the `Wald_test(test="HTZ")$df_denom` parity claim in the variance-families bullets above widens quantitatively (unchanged behavior when tripped: fail-closed NaN inference plus linalg's loud noise-floor UserWarning; the absolute floor already made this reachable at m=1). - **Note (container admission - rows M-024/M-093):** `compute_honest_did` and `compute_pretrends_power` admit Stacked-sourced `aggregate('event_study')` containers. Provenance: `base_period` is the class-level constant `"universal"` (every sub-experiment normalizes against the single omitted reference `e = -1 - anticipation`) and `reference_event_times` is the singleton `(-1 - anticipation,)`. Requirements: `kappa_pre >= 2` (the default `kappa_pre=1` grid minus the reference has ZERO estimated pre-periods — both consumers reject), and for HonestDiD a non-singular FULL retained event-study covariance — the validated sub-block spans the retained pre AND post rows (`allow_singular=False` per Rambachan-Roth's eigenvalues-bounded-away-from-zero assumption on the full Σ — keep `kappa_pre` small relative to the cluster count; PreTrendsPower keeps its documented singular support). The container's scalar `df_survey` is None on analytical fits — the generic resolver's fallback reads `df_inference`, a name StackedDiD does not carry (`inference_df` is the overall POST-AVERAGE contrast df, not a design-level cluster df, and misusing it for FLCI critical values would be worse than the asymptotic default) — so honest FLCI critical values are NORMAL-THEORY on analytical Stacked fits, diverging from bare-`cluster=` CS fits (which thread `G-1`); survey fits thread the survey df and replicate-undefined designs the fail-closed `0.0` sentinel (NaN FLCI CIs; the identified-set `lb`/`ub` bounds are df-independent and stay finite). Withheld-inference rows (finite `se`, non-finite `p_value` — the hc2_bm BM-DOF fail-close and replicate-undefined designs both emit this shape) are ADMITTED with a source-scoped warning in BOTH consumers: bounds/power consume only the point estimates and covariance, which remain valid; the warning is shape-descriptive because containers are publicly constructible. A rank-dropped interior event-time column (NaN-se row) fails closed via honest's consecutive-grid ValueError with a Stacked-specific remedy (no `balance_e` — Stacked's aggregate() has no such level). REFERENCE-SUPPORT GUARD: `fit()` raises when either omitted-reference cell (treated / control) carries no POSITIVE COMPOSED WLS WEIGHT — Equation 3 is Q-weighted, so raw row presence is not support: a gapped panel can empty the cell outright (IC1 checks only min/max periods), aggregate Q-weights zero a sub-experiment's controls at any event time where that sub-experiment has no treated rows, and survey pweights can zero a cell — without the guard the design rank-drops and QR pivoting silently re-normalizes against an arbitrary horizon while the surface certifies `delta_0 = 0` at the reference. Partial support (some cohorts' own ref period absent while the POOLED omitted category is populated) still fits: the coefficient-vector normalization is real; the per-cohort baseline raggedness is estimand composition, the documented ragged-window behavior. - **Note (reference-row marking, 4.0, [M-093] item 8a):** the always-materialized `event_study_effects` reference row (`e = -1 - anticipation`) carries `"is_reference": True` and no `n_obs` key (3.x: the `n_obs == 0` sentinel, retired); `summary()` keys its `[ref: h]` line on the marker; `plot_event_study(results)` and `DiagnosticReport` locate the reference by the key; 3.x pickles are migrated on load (`__setstate__`). Since 8b `to_dataframe(level='event_study')` is the unified `EVENT_STUDY_SCHEMA` frame (`n` NaN on the `is_reference` row). @@ -2631,7 +2632,7 @@ The interaction coefficient `δ_{g,t}` identifies `ATT(g, t)` under parallel tre - **Note (reference-period normalization, W2025 Eq. 6.1/6.4 — `never_treated` OLS only):** this applies to the **lead-and-lag** specification, i.e. `control_group="never_treated"` on the OLS path, which is the only configuration that emits pre-treatment cells (`include_pre`). The default `not_yet_treated` (and both nonlinear paths) emit `t >= g − anticipation` cells only — the lag-only specification — so no cell is omitted from those designs and no placebo cells are produced; the reference period is still computed there, but solely to detect cohorts that have none (see the unidentified-cohort Note). For the lead-and-lag specification, for each treated cohort `g` the design OMITS one cell — the reference period — because the cohort's full cell block spans the cohort indicator `1{G_i = g}`, which the unit fixed effects absorb. The paper is explicit: Eq. 6.1 excludes `dg_i · f(g-1)_t` "so that `s = g − 1` is the reference period", and Eq. 6.4 describes the saturated regression as a collection of 2×2 DiDs using `g − 1` as reference. The library omits `ref(g) = max{t : t < g − anticipation AND cohort g is OBSERVED at t}` — `g − 1` on a balanced panel, and the cohort's own latest available pre-period otherwise. The support must be **per-cohort**: a panel-wide rule omits an identically-zero column whenever that cohort is unobserved at the panel's latest pre-period, leaving the collinearity intact. Anchored to Stata `jwdid ... never`, which omits the same cell for every cohort (`tests/test_etwfe_cs_stata_parity.py::TestNeverTreatedVsStataJwdid`). **Before v3.9 the reference was emitted and generic QR rank detection dropped an arbitrary column instead** — on `mpdta` that silently removed two genuine post-treatment effects, `(2004, 2004)` and `(2006, 2007)` (issue #724). - **Note (reference-period sensitivity, W2025 Section 6.1):** the choice of reference is a NORMALIZATION, not an identifying assumption. The paper notes any set of pre-treatment periods may serve, and the pre-trend `t`-test is numerically identical whichever is used. `g − 1` is the paper's canonical choice and the one this library implements. **The point estimates are NOT invariant to it** — only the two-sided pre-trend test is (Section 6.1). Different references give different finite-sample 2×2 contrasts, all consistent for ATT(g, t) under parallel trends: on `mpdta`, ATT(2007, 2007) computed by hand against references 2006/2005/2004/2003 gives −0.0261 / −0.0571 / −0.0599 / −0.0294. This is exactly why issue #724 mattered — QR dropped `g2007_t2005`, silently making 2005 the reference, and the estimator returned −0.0571: a correct 2×2 DiD against the wrong baseline. It also means the unbalanced-panel fallback (the cohort's latest AVAILABLE pre-period) yields a different contrast from `g − 1`, which is legitimate but should be read as such. - **Note (unidentified cohorts — library identification limit):** a cohort with NO observed period before `g − anticipation` has no reference cell, so none of its ATTs are identified. The library warns naming the cohort and **excludes its observations** from the estimation sample. *(Conditional since comparison-support filtering landed: when the filter removes that cohort's rows first, the cohort never reaches this path and is instead named by the zero-cell / fully-dropped warning above. The observations are excluded either way; only which warning names them differs.)* Excluding only its columns would be worse than the original bug: `_filter_sample` retains every treated row, so the cohort would join the omitted baseline beside the controls and load its treatment effect onto the time fixed effects — measured on `mpdta` at `anticipation=1`, that moved `ATT(2006, 2006)` by 0.0077 and `ATT(2006, 2007)` by 0.0055, past the 5e-3 this library treats as material. Exclusion can cascade (the removed rows may have been another cohort's not-yet-treated comparison), so the surviving sample is re-checked and `fit()` raises when no comparison observations remain rather than returning an all-NaN fit. This is a **library limit, not the paper's last-cohort result** — W2025's "the last cohort `T` plays the role of the never-treated group" is conditioned on there being no never-treated group at all, a different case. -- **Note (per-period comparison support, W2025 Section 5.4 — the cell half):** a period at which **no unit is untreated** carries no identified `ATT(g, t)`, because there is no untreated outcome to difference against. With the default `unsupported_period_action="drop"`, such periods are removed from the estimation sample **before the solve**. The eligible set is keyed on the regression baseline, not on the method: on the lead-and-lag branch (`never_treated` + OLS) only never-treated rows qualify, because every `(g, t)` except each cohort's reference is emitted, so a later cohort's rows sit in their own indicator; elsewhere not-yet-treated rows qualify too. **The omitted reference cells do NOT count as support** even though they sit in the baseline: cohort `h`'s indicator is absorbed by the unit FE (`1{h,t} = D_h − Σ_{t'≠t} h_{t'}`), so the period dummy stays reproducible from the emitted cells and the design remains collinear — measured on a panel with never-treated units through `t=4` and cohorts 3, 6 through `t=6`, retaining `t=5` on the strength of `ref(6)=5` raises on the lost `(6,1)`, while dropping it fits at `overall_att = 1.0171`. On an all-eventually-treated panel this yields exactly Eq. 5.15's cell set: cohort `G_max` is the reference and receives nothing. Anchored to Stata `jwdid` (`tests/test_etwfe_cs_stata_parity.py::TestAllEventuallyTreatedVsStataJwdid`): identical cell set, identical `N` (764 of 955), ATTs agreeing to ~1e-15. +- **Note (per-period comparison support, W2025 Section 5.4 — the cell half):** a period at which **no unit is untreated** carries no identified `ATT(g, t)`, because there is no untreated outcome to difference against. With the default `unsupported_period_action="drop"`, such periods are removed from the estimation sample **before the solve**. The eligible set is keyed on the regression baseline, not on the method: on the lead-and-lag branch (`never_treated` + OLS) only never-treated rows qualify, because every `(g, t)` except each cohort's reference is emitted, so a later cohort's rows sit in their own indicator; elsewhere not-yet-treated rows qualify too. **The omitted reference cells do NOT count as support** even though they sit in the baseline: cohort `h`'s indicator is absorbed by the unit FE (`1{h,t} = D_h − Σ_{t'≠t} h_{t'}`), so the period dummy stays reproducible from the emitted cells and the design remains collinear — measured on a panel with never-treated units through `t=4` and cohorts 3, 6 through `t=6`, retaining `t=5` on the strength of `ref(6)=5` raises on the lost `(6,1)`, while dropping it fits at `att = 1.0171`. On an all-eventually-treated panel this yields exactly Eq. 5.15's cell set: cohort `G_max` is the reference and receives nothing. Anchored to Stata `jwdid` (`tests/test_etwfe_cs_stata_parity.py::TestAllEventuallyTreatedVsStataJwdid`): identical cell set, identical `N` (764 of 955), ATTs agreeing to ~1e-15. - **Note:** Comparison-support filtering is configurable via `unsupported_period_action="drop" | "error"` (ledger M-147). The default `"drop"` preserves the reduction and warnings described here. `"error"` raises `ValueError` before period removal or design construction, naming unsupported periods and their observation count; it does not fit an unidentified full-sample design. The same positive-weight, anticipation-aware predicate applies on OLS, logit and Poisson, independently of `rank_deficient_action`. Only existing pre-filter checks (configuration, cohort, and survey design) retain precedence; later covariate, nonlinear-outcome, and explicit-cluster checks are not preflighted, so unsupported-period refusal may precede those errors. For valid survey designs with unsupported periods, `"error"` raises `ValueError` and `"drop"` retains the survey-domain `NotImplementedError`. Other identification guards and unidentified-cohort exclusion are unchanged. Successful results retain the configured policy through aggregation and serialization and expose it via `summary()` and `to_dict()`. - **Note (the reduction is always reported — deviation from `jwdid`):** Stata `jwdid` performs the same reduction **silently**, reporting only a smaller `N`. This library warns on every fit that drops rows, naming the periods, the observation count, and the branch-correct cause, and separately naming any cohort left with no estimated cells or stripped of every row. Neither warning is gated on `rank_deficient_action` — that setting governs how rank warnings surface, not whether the estimation sample changed. Being explicit is a deliberate improvement, not a numerical deviation: the estimates are identical. - **Note (reference movement under filtering):** on the `not_yet_treated` branch a reference period can never be filtered — reference eligibility (`t < g − anticipation`) IS the support predicate's own second disjunct evaluated at that cohort's rows — and the estimator asserts this at runtime, raising if it is ever violated (silent renormalization is the issue #724 defect class). On the `never_treated` + OLS branch it CAN move, because eligibility there does not imply never-treated presence: measured, dropping `t=5` moves `ref(6)` from 5 to 4. That is legitimate — the ATTs stay correctly labelled and validly identified, just normalized against a different baseline period — so it **warns** naming the cohort and both periods rather than raising. A reference that becomes `None` is not a move: that cohort is unidentified and the exclusion path already warns by name. @@ -2929,7 +2930,8 @@ Full maintainer paper reviews (equation-level detail, replication targets): `doc columns - the 3.10 bespoke frame (`term`/`cohort` column, trailing `Overall` row, `n_treated`/`n_control`/`n_obs`, `ci_lower`/`ci_upper`) is gone (ledger row M-153); `to_csv(level=)` follows. The 3.10-born alias properties `pvalue`/`ci` (duplicates of - `p_value`/`conf_int`) emit a FutureWarning from 4.0 and are removed at 5.0 (M-151/M-152). + `p_value`/`conf_int`) are removed at 4.0 (M-151/M-152; item 9b retired their planned + FutureWarning window before any release carried it). - **Note:** Registry entry authored with the paper reviews ahead of the implementation (PR #588, third-party contribution under maintainer revision; MERGED 2026-08-20). The checklist boxes were finalized with the merge; the implementation-specific Notes in this section (control pools, RI convention, overall conventions, Sec 4.3 rejection, API conformance) are the maintainer finalization of that pass. - **Note:** Maintainer validation suite: `tests/test_methodology_lwdid.py` was the ACCEPTANCE BAR for PR #588's final round (passed unmodified; #588 merged 2026-08-20): it is written against the agreed post-rename API. The earlier xfail scaffolding is retired. Goldens: `benchmarks/data/lwdid_walmart_eventstudy_golden.json` (printed Tables A4/A5; point halves consumed, printed SE halves superseded), `benchmarks/data/real/castle_lw_subset.csv`, and `benchmarks/data/lwdid_stata_golden.json` (full-precision authors'-package parity, `benchmarks/stata/generate_lwdid_golden.do`; schema checked ungated by `tests/test_lwdid_stata_golden_schema.py`). - **Note:** Pre-release API-conformance decisions (locked 2026-08-15/16; no deprecation-ledger rows are owed because LWDiD never shipped on main): `estimation_method=` with house-CS values `reg`/`ipw`/`dr` plus the LWDiD-only `psm`; `vcov_type=` (name locked; accepted sets finalized in the maintainer fix wave: `reg` accepts `{classical, hc1, hc2, hc3}` — `hc3` retained as the paper-mandated extra via a shared linalg extension; `ipw`/`dr` accept `hc1` only, because the influence-function variance is always used on those paths and any other value would be silently inert; `psm` accepts `hc1` as its only configuration value while its inference is UNAVAILABLE (NaN; the Abadie-Imbens matching variance is a DEFERRED item); `cluster=` composes only with `hc1` (CR1) and is rejected for `psm`; the `"cluster"` mode value retired in favor of clustering-by-`cluster=`, `hc0`/`hc4`/`conley` not offered); `seed=` with default `None`; `pscore_trim=`; `cluster=` on the constructor; the `LW` alias, module-level `lwdid()` wrapper, `lwdid_trend_diagnostics` module, and `overall_att`/`period_effects`/`period_specific` surfaces retired (per-period effects live on the unified post-fit event-study surface). @@ -3995,7 +3997,7 @@ Classic synthetic control (donor/unit weights only) for a single treated unit, d **Estimator (Section 2.3):** - Predictor matrices `X1` (k×1 treated) / `X0` (k×J donors) = covariates `Z` + linear combinations of pre-period outcomes (`predictors` averaged over `predictor_window`, `special_predictors`, and/or per-period outcome lags `pre_period_outcomes`). Canonical row order: predictor averages → special predictors → outcome lags (the row *order* matches R `Synth::dataprep`; aggregation semantics differ — see the `na.rm` deviation below). -- **Inner solve:** `W*(V) = argmin_W (X1 − X0 W)' diag(V) (X1 − X0 W)` s.t. `w_j ≥ 0, Σ w_j = 1`. Implemented by folding `V^½` into the predictors (`packed = [V^½·X0 | V^½·X1]`) and solving the simplex least squares exactly, by one non-negative least squares, `utils._sc_weight_nnls(zeta=0)`: on the simplex `X1 − X0 W = −(X0 − X1 1')W`, so with `Z = V^½(X0 − X1 1')`, `u = argmin_{u ≥ 0} ||[Z/s; 1']u − [0; 1]||` and `W = u / 1'u` for any scale `s > 0` (for `u = cW` the objective is `c²||ZW||²/s² + (c − 1)²`, whose least value over `c`, `a/(1 + a)` with `a = ||ZW||²/s²`, increases with `||ZW||²`). Each donor column of `Z` is first scaled to unit norm (`B = Z D`, `w = D v`, the constraint row becoming `(D1)'v = 1`), so the conditioning is per donor: a donor 1e16 times the others' scale cannot push them below the active set's per-column tolerance, and finite inputs anywhere in the normal float64 range (column norms and their reciprocals between about 1e-300 and 1e300) give the same weights. Outside it the solve fails closed as a non-converged uniform solve rather than a silent vertex: a non-finite `Z` (a donor-minus-treated difference that overflows), a column norm that overflows (entries near 1e308), or a subnormal column whose reciprocal norm overflows; every intermediate is checked finite before the active set runs, which itself fails closed on a non-finite problem. The entering column is chosen on the gradient in the original units, so the vertex an exact-fit tie returns is the same as the un-scaled reduction's (a convention; see the ties Note below). Lawson-Hanson's active set (`utils._nnls_lawson_hanson`) terminates in finitely many steps, so the solve has no iteration budget to tune; a solve that nonetheless exhausts the step guard (50 steps per donor, not reached in practice) returns its current feasible iterate flagged non-converged, and the fail-closed paths below (treated fit, placebo / leave-one-out refits, conformal grid points) handle it as any other non-converged solve. `inner_max_iter` / `inner_min_decrease` are deprecated no-ops (`DeprecationWarning` when passed, discarded to `None`; ledger M-148 / M-149, removed at 5.0). +- **Inner solve:** `W*(V) = argmin_W (X1 − X0 W)' diag(V) (X1 − X0 W)` s.t. `w_j ≥ 0, Σ w_j = 1`. Implemented by folding `V^½` into the predictors (`packed = [V^½·X0 | V^½·X1]`) and solving the simplex least squares exactly, by one non-negative least squares, `utils._sc_weight_nnls(zeta=0)`: on the simplex `X1 − X0 W = −(X0 − X1 1')W`, so with `Z = V^½(X0 − X1 1')`, `u = argmin_{u ≥ 0} ||[Z/s; 1']u − [0; 1]||` and `W = u / 1'u` for any scale `s > 0` (for `u = cW` the objective is `c²||ZW||²/s² + (c − 1)²`, whose least value over `c`, `a/(1 + a)` with `a = ||ZW||²/s²`, increases with `||ZW||²`). Each donor column of `Z` is first scaled to unit norm (`B = Z D`, `w = D v`, the constraint row becoming `(D1)'v = 1`), so the conditioning is per donor: a donor 1e16 times the others' scale cannot push them below the active set's per-column tolerance, and finite inputs anywhere in the normal float64 range (column norms and their reciprocals between about 1e-300 and 1e300) give the same weights. Outside it the solve fails closed as a non-converged uniform solve rather than a silent vertex: a non-finite `Z` (a donor-minus-treated difference that overflows), a column norm that overflows (entries near 1e308), or a subnormal column whose reciprocal norm overflows; every intermediate is checked finite before the active set runs, which itself fails closed on a non-finite problem. The entering column is chosen on the gradient in the original units, so the vertex an exact-fit tie returns is the same as the un-scaled reduction's (a convention; see the ties Note below). Lawson-Hanson's active set (`utils._nnls_lawson_hanson`) terminates in finitely many steps, so the solve has no iteration budget to tune; a solve that nonetheless exhausts the step guard (50 steps per donor, not reached in practice) returns its current feasible iterate flagged non-converged, and the fail-closed paths below (treated fit, placebo / leave-one-out refits, conformal grid points) handle it as any other non-converged solve. The former `inner_max_iter` / `inner_min_decrease` constructor params (inert since #838) were removed at 4.0 (ledger M-148 / M-149; their `DeprecationWarning` shipped in 3.13.0); passing either is a `TypeError`. - **Note (deviation from R):** ties in `W`. R `Synth` solves the same quadratic program to tolerance with an interior-point method (`ipop`, or `LowRankQP`). Where the treated predictors lie inside the donors' convex hull (more donors than predictors, a common case), the optimum is a whole face of the simplex, every point of which matches `X1` exactly: the NNLS returns a vertex of that face (at most `k + 1` donors), where an interior-point method tends to a point inside it. The active set is diff-diff's own rather than `scipy.optimize.nnls`, whose implementation changed across SciPy releases and with it the vertex returned at a tie: on Tutorial 25, SciPy 1.11 to 1.18 gave five different sets of placebo RMSPE ratios, where the Frank-Wolfe solve's agreed to 2e-5, and 1.15 an unbounded constant-effect confidence set. With `_nnls_lawson_hanson` the tutorial's placebo ratios agree to the sixth decimal under SciPy 1.11 to 1.18 (SciPy 1.10 moves one placebo either way, through the outer `V` search). Which exact match is reported is then a solver convention, not something the data identify (Abadie and L'Hour (2021) add a penalty that selects one); and every `V` attains the zero inner loss, so the nested `V` search can rank `V` only by which vertex the solve returns: `V` is not identified either (Tutorial 25's search keeps its uniform start). HISTORY: before #838 the inner problem was solved by Frank-Wolfe with a budget of `inner_max_iter` steps, which on ordinary factor-model panels stopped on the budget short of the optimum: the treated fit failed closed or `in_space_placebo()` dropped placebos as failed, and the nested search ranked each `V` by where the truncated solve had stopped. - **`V` selection (`v_method`):** `"nested"` chooses diagonal PSD `V` minimizing the pre-period **outcome** MSPE `mean((Z1 − Z0·W*(V))²)` over all pre periods; `"cv"` chooses `V` by **out-of-sample cross-validation** (ADH 2015 §; Abadie 2021 Eq. 9 — see the per-window re-aggregation Note); `"inverse_variance"` uses the closed-form `v_h = 1/Var(X_{h·})` (Abadie 2021 §3.2(a); no search); `"custom"` skips the search and uses a user-supplied `custom_v` (trace-normalized). `mspe_v` reports the selected `V`'s objective value — the pre-period MSPE under `"nested"`, the held-out validation-window MSPE under `"cv"`, and `None` for the search-free `"custom"`/`"inverse_variance"` paths (not comparable across methods). - **Effect:** gap path `α̂_1t = Y_1t − Σ_j w_j·Y_jt`; `att` = mean post-period gap; `pre_rmspe` = pre-period fit diagnostic. @@ -6388,6 +6390,7 @@ where `ATT*_b` are the `B` valid permutation estimates and the `+1` includes the **Deviations:** - **Note (permutation inference is not `safe_inference`):** the permutation path uses the RI p-value + a null-distribution percentile interval (not an effect CI) and a null-mean `placebo_effect`. This is the standard randomization-inference convention and deliberately differs from the project-wide `safe_inference` t-based inference (which assumes a sampling-distribution SE the permutation test does not have). +- **Note (wrapper calendar contract, 4.0 item 9b - ledger rows M-156 / M-157):** `run_placebo_test` / `run_all_placebo_tests` take `time` as the calendar period column only (spec section 8 rule 1) and derive the 0/1 post dummy for the permutation and leave-one-out tests as `data[time].isin(post_periods)` - the rule `placebo_timing_test` / `placebo_group_test` already apply internally. `post_periods` is REQUIRED on both wrappers for every `test_type` (no midpoint default), while the direct `placebo_timing_test` / `placebo_group_test` callees keep their second-half-of-periods fallback. A missing `time` column, a missing/empty `post_periods`, missing values in `time`, `post_periods` values absent from the column, or a pre-existing `_placebo_post` column raise `ValueError` - on the suite wrapper BEFORE its per-test `try`/`except` blocks, so a malformed argument is never swallowed into an error dict. Through 3.x the wrappers forwarded `time` itself as the dummy, so on any multi-period calendar column the two dummy-consuming tests raised (`validate_binary`) and the suite silently reported them as error dicts. - **Note (leave-one-out spread):** `leave_one_out_test`'s reported `se`/`t_stat`/`p_value`/CI summarize the dispersion of the per-drop ATTs (a robustness signal), not a design-based jackknife standard error; the per-unit effects are the primary output. - **Note (BDM scope):** BDM (2004) is primarily about serial-correlation-robust *standard errors* (parametric AR, block bootstrap, cluster/arbitrary VCV, time aggregation). Those inference corrections are out of scope for this diagnostic surface — diff-diff's cluster-robust SE and bootstrap paths cover them under the relevant estimators. This entry covers only the placebo-law / randomization-inference diagnostics that BDM motivate. diff --git a/docs/migration-4.0.md b/docs/migration-4.0.md index 0188cd6d0..31f057ac1 100644 --- a/docs/migration-4.0.md +++ b/docs/migration-4.0.md @@ -21,14 +21,14 @@ updated, CI fails. | constructor-hygiene | `covariates=` moves from the constructor to `fit()` | 1 | §7b | | df-convention-flip | The `df_convention` default flips to `"cluster"` on seven estimators - numbers move | 7 | §6 | | diagnostic-family | Bacon is re-homed out of the estimator roster into the diagnostics family | 1 | §7b | -| field-flip | Nine results containers rename `overall_att` to the canonical `att` | 9 | §5 | +| field-flip | Nine results containers rename `overall_att` to the canonical `att`; the old names are removed at 4.0, not shimmed | 9 | §5 | | function-wrappers | Eight module-level wrapper functions are removed; call the classes | 8 | §7 | -| lwdid-results-contract | `LWDiDResults` joins the uniform results contract: the `pvalue`/`ci` alias properties warn in favour of `p_value`/`conf_int` (two rows); `to_dataframe()` is reshaped ([M-153], prose) | 2 | §7b | +| lwdid-results-contract | `LWDiDResults` joins the uniform results contract: the `pvalue`/`ci` alias properties are removed in favour of `p_value`/`conf_int` (two rows); `to_dataframe()` is reshaped ([M-153], prose) | 2 | §7b | | merge-ddd | `StaggeredTripleDifference` is absorbed by `TripleDifference` | 5 | §2 | | merge-mpd | `MultiPeriodDiD` is absorbed by `TwoWayFixedEffects(event_study=True)` | 5 | §2 | | merge-qdid | `QDiD` is absorbed by `ChangesInChanges(method="qdid")` | 2 | §2 | | obligation-sdid-params | Two SyntheticDiD constructor params (inert since 3.0.0) are removed | 3 | §7b | -| obligation-sc-inner-budget | Two SyntheticControl constructor params (inert since the exact NNLS inner solve) warn at 4.0 and are removed at 5.0 | 2 | §7b | +| obligation-sc-inner-budget | Two SyntheticControl constructor params (inert since the exact NNLS inner solve; `DeprecationWarning` in 3.13.0) are removed at 4.0 | 2 | §7b | | obligation-warning-retirements | A transition `FutureWarning` stops being emitted | 1 | §7b | | policy-auto-cluster | SpilloverDiD auto-clusters on `unit` unless you pass `cluster=`; `cluster=False` becomes the uniform explicit off-switch | 1 | §6 | | renames-cohort | `cohort=` becomes `first_treat=` | 1 | §3 | @@ -36,7 +36,7 @@ updated, CI fails. | renames-control-group | `clean_control=` becomes `control_group=` | 2 | §3 | | renames-dcdh | dCDH's `group=`/`controls=` become `unit=`/`covariates=` | 4 | §3 | | renames-level | Aggregation-level params and their accepted values unify on `level=`/`"event_study"` | 5 | §3 | -| renames-post | The post-dummy `time=` becomes `post=` | 3 | §3 | +| renames-post | The post-dummy `time=` becomes `post=`; the placebo wrappers take `time` as the calendar column and derive the dummy from `post_periods` | 5 | §3 | | renames-robust-drop | `robust=` is dropped in favour of `vcov_type=` - read §3, the translation is per-estimator | 4 | §3 | | results-contract | The zero-count reference sentinel is retired and every results class's `to_dataframe(level="event_study")` emits the unified `EVENT_STUDY_SCHEMA` ([M-093]) | 1 | §5 | @@ -87,13 +87,12 @@ results = TwoWayFixedEffects().fit( ) ``` -- **`results.period_effects` still works, with a `FutureWarning`** ([M-016]; removed in 5.0). - On a `TwoWayFixedEffects` event-study surface it returns the same calendar-keyed dict of - per-period records (`period, effect, se, t_stat, p_value, conf_int, is_significant, - significance_stars`; the reference period omitted; `is_significant` at the fit's `alpha`). - The successor is the surface itself: `results.to_dataframe()` has one row per `event_time` - (reference row included) and the columnar `att`/`se`/`p_value`/`conf_int_lower`/ - `conf_int_upper` arrays. +- **`results.period_effects` is removed** ([M-016]; the `MultiPeriodDiDResults` dict of + per-period records has no 4.0 equivalent - the compatibility view once planned for a 4.0 -> + 5.0 window was retired at 4.0, item 9b, before any release carried it, so reading it is an + `AttributeError`). The successor is the surface itself: `results.to_dataframe()` has one row + per `event_time` (reference row included) and the columnar `att`/`se`/`p_value`/ + `conf_int_lower`/`conf_int_upper` arrays. ```python # 4.0 - the per-period surface replaces the period_effects dict @@ -179,11 +178,16 @@ release: 4.0, passing `time=` in static mode (`TwoWayFixedEffects`) or 2x2x2 mode (`TripleDifference`) **raises `ValueError`** rather than being silently reinterpreted. If you pass a post dummy, rename it to `post=`; if you pass a calendar column to an event-study or staggered fit, leave it alone. +The placebo wrappers `run_placebo_test` / `run_all_placebo_tests` follow the same rule from 4.0 +([M-156], [M-157]): `time=` is the calendar column, `post_periods=` (now required for every +`test_type`) lists its post-treatment values, and the 0/1 dummy the permutation and +leave-one-out tests need is derived as `data[time].isin(post_periods)`. Through 3.x the wrappers +forwarded `time` itself as that dummy, which only worked on a two-period 0/1 column. **Dropped parameters are not always deletions.** A parameter with no successor in the appendix may still need a replacement call — see its `Fix` cell. -**Dropped `fit()` parameters shift the positional slots after them.** Removing the fit-time +**Dropped parameters shift the positional slots after them.** Removing the fit-time `aggregate=`/`balance_e=` keywords moves every later positional parameter left: on `CallawaySantAnna`, `ImputationDiD` and `TwoStageDiD` `survey_design` moves from slot 9 to 7; on `EfficientDiD` `survey_design` 9 -> 7 and `store_eif` 10 -> 8; on `StackedDiD` (`aggregate` @@ -192,7 +196,9 @@ sat before them) `population`/`survey_design`/`covariates` move 7/8/9 -> 6/7/8; `L_max`, `covariates`, `trends_linear`, `trends_nonparam`, `honest_did`, `heterogeneity`, `design2` and `survey_design` each move one slot left (7..14 -> 6..13). `HeterogeneousAdoptionDiD` and `did_had_pretest_workflow` shift nothing (everything after the removed slot is keyword-only; -a seventh positional on `fit()` now raises). Pass these by keyword. +a seventh positional on `fit()` now raises). On the `SyntheticControl` constructor, removing +`inner_max_iter`/`inner_min_decrease` (slots 5-6) moves `standardize`, `alpha`, `seed` and +`v_cv_t0` two slots left ([M-148], [M-149]). Pass these by keyword. ## Post-fit aggregation @@ -291,13 +297,15 @@ replicate-weight `ImputationDiD`/`TwoStageDiD` designs: the plain fit's replicat ## Results fields Nine containers rename `overall_att` to the canonical `att`. Since 4.0 the canonical name -is the stored dataclass field and the old name is a read-only warning-emitting property -(removed at 5.0); the new accessor already worked under 3.x, so a codebase that migrated +is the stored dataclass field and the old name is **gone**: reading it raises `AttributeError`. +This is an unwarned break of a released 3.x name - the `FutureWarning` property once planned +for a 4.0 -> 5.0 window was retired at 4.0 (item 9b) before any release carried it, so this +guide is the notice. The new accessor already worked under 3.x, so a codebase that migrated early sees no change. ```python att = results.att # canonical: the stored field -att = results.overall_att # FutureWarning since 4.0, removed at 5.0 +att = results.overall_att # AttributeError at 4.0 ``` **The whole inference quintet moves, not just the point estimate.** Wherever a container @@ -317,7 +325,7 @@ names in the same step: `overall_att_se`, `overall_att_t_stat`, `overall_att_p_value` and `overall_att_conf_int` — with the `att` infix — and they flipped to the same canonical names. If you grep for `overall_se` you will miss them. Since 4.0 all nine of its legacy -spellings warn: the five former fields AND the four former silent `overall_se` / +spellings are gone: the five former fields AND the four former silent `overall_se` / `overall_t_stat` / `overall_p_value` / `overall_conf_int` aliases. ``` @@ -331,10 +339,11 @@ the legacy `overall_*` keys to the canonical names on load (an existing canonica so the loaded object stores the quintet natively and `overall_*` never reappear in its `__dict__` or in a re-pickle. -**The legacy names are read-only.** Assigning `results.overall_se = x` raises -`AttributeError`; assign the canonical field (`results.se = x`) instead. The bootstrap draw -containers (`results.bootstrap_results.overall_att_se` and friends) are NOT part of this -flip: they hold no point estimate, keep their payload names, and emit no warning. +**Assign the canonical field** (`results.se = x`). The legacy names are no longer +descriptors, so `results.overall_se = x` silently creates an unrelated instance attribute that +nothing reads - grep your fixtures for `overall_` writes. The bootstrap draw containers +(`results.bootstrap_results.overall_att_se` and friends) are NOT part of this flip: they hold +no point estimate and keep their payload names. **Report schemas.** `DiagnosticReport.to_dict()` reports `headline_metric.name` and `target_parameter.headline_attribute` as `"att"` for the nine containers (`"overall_att"` @@ -448,7 +457,7 @@ each one with its target. Smaller items that do not fit the families above — two inert `SyntheticDiD` constructor parameters, two `SyntheticControl` inner-solve parameters that became inert when the donor weights moved to an exact non-negative least squares ([M-148], [M-149]: `DeprecationWarning` -at 4.0, removed at 5.0), the `covariates=` constructor-to-`fit()` move, a retired transition warning, the +in 3.13.0, removed at 4.0), the `covariates=` constructor-to-`fit()` move, a retired transition warning, the Bacon roster re-homing, and the family-wide `anticipation` validation below. The appendix lists the ledger-derived removals/flips; [M-144], [M-145], and [M-146] are behavior tightenings, [M-147] is an additive policy, and [M-153] is a results-frame reshape. These have no @@ -527,8 +536,9 @@ they expose no such knob. `DMLDiDResults` inherits `CallawaySantAnnaResults` sto `overall_att` family rides [M-050] with the parent. `LWDiDResults` joined the uniform results contract in the cut's conformance PR: `summary(alpha=None)` / `print_summary(alpha=None)` under the [M-146] guard, the reshaped `to_dataframe(level=...)` / `to_csv(level=...)` described -under "Remaining 4.0 changes" ([M-153]), and FutureWarnings on its `pvalue`/`ci` alias -properties ([M-151], [M-152]: read `.p_value` / `.conf_int`; removed at 5.0). `DurationDiDResults` +under "Remaining 4.0 changes" ([M-153]), and the removal of its `pvalue`/`ci` alias +properties ([M-151], [M-152]: read `.p_value` / `.conf_int`; the FutureWarning window once planned +for them was retired at 4.0, item 9b). `DurationDiDResults` gains `aggregate("simple")` (a verbatim relay of the stored bootstrap inference, `df` NaN) and `RDDensityTestResult` gains `to_dict()`. @@ -589,7 +599,7 @@ but now emit a `FutureWarning`, and they no longer appear in `dir()`/`vars()`. ## Appendix: every 4.0 change -One row per ledger row that requires action at 4.0 — 113 in total. `Old` and `New` are the +One row per ledger row that requires action at 4.0 — 115 in total. `Old` and `New` are the ledger's own locators. An em dash in `New` means the ledger names no successor **locator**; it does not mean no action is required, so read the `Fix` cell. @@ -623,15 +633,15 @@ does not mean no action is required, so read the `Fix` cell. | M-130 | df-convention-flip | `diff_diff:StackedDiD[df_convention]` | — | The `df_convention` default flips to `"cluster"`; pass `df_convention="residual"` to keep 3.x numbers. | | M-131 | df-convention-flip | `diff_diff:ImputationDiD[df_convention]` | — | The `df_convention` default flips to `"cluster"`; pass `df_convention="residual"` to keep 3.x numbers. | | M-090 | diagnostic-family | `diff_diff:BaconDecomposition` | — | Bacon moves out of the estimator roster into the diagnostics family - update docs references and imports of the roster, not call sites. | -| M-050 | field-flip | `diff_diff:CallawaySantAnnaResults.overall_att` | `diff_diff:CallawaySantAnnaResults.att` | Read and construct with `att`; `overall_att` is a read-only FutureWarning property since 4.0; 3.x pickles load unchanged. | -| M-051 | field-flip | `diff_diff:SunAbrahamResults.overall_att` | `diff_diff:SunAbrahamResults.att` | Read and construct with `att`; `overall_att` is a read-only FutureWarning property since 4.0; 3.x pickles load unchanged. | -| M-052 | field-flip | `diff_diff:ImputationDiDResults.overall_att` | `diff_diff:ImputationDiDResults.att` | Read and construct with `att`; `overall_att` is a read-only FutureWarning property since 4.0; 3.x pickles load unchanged. | -| M-053 | field-flip | `diff_diff:TwoStageDiDResults.overall_att` | `diff_diff:TwoStageDiDResults.att` | Read and construct with `att`; `overall_att` is a read-only FutureWarning property since 4.0; 3.x pickles load unchanged. | -| M-054 | field-flip | `diff_diff:StackedDiDResults.overall_att` | `diff_diff:StackedDiDResults.att` | Read and construct with `att`; `overall_att` is a read-only FutureWarning property since 4.0; 3.x pickles load unchanged. | -| M-055 | field-flip | `diff_diff:EfficientDiDResults.overall_att` | `diff_diff:EfficientDiDResults.att` | Read and construct with `att`; `overall_att` is a read-only FutureWarning property since 4.0; 3.x pickles load unchanged. | -| M-056 | field-flip | `diff_diff:WooldridgeDiDResults.overall_att` | `diff_diff:WooldridgeDiDResults.att` | Read and construct with `att`; `overall_att` is a read-only FutureWarning property since 4.0; 3.x pickles load unchanged. | -| M-057 | field-flip | `diff_diff:ChaisemartinDHaultfoeuilleResults.overall_att` | `diff_diff:ChaisemartinDHaultfoeuilleResults.att` | Read and construct with `att`; `overall_att` is a read-only FutureWarning property since 4.0; 3.x pickles load unchanged. | -| M-058 | field-flip | `diff_diff:ContinuousDiDResults.overall_att` | `diff_diff:ContinuousDiDResults.att` | Read and construct with `att`; `overall_att` is a read-only FutureWarning property since 4.0; 3.x pickles load unchanged. | +| M-050 | field-flip | `diff_diff:CallawaySantAnnaResults.overall_att` | `diff_diff:CallawaySantAnnaResults.att` | Read and construct with `att`; `overall_att` (and the sibling `overall_*` names) are removed at 4.0 - `AttributeError`, no warned release preceded it; 3.x pickles load unchanged. | +| M-051 | field-flip | `diff_diff:SunAbrahamResults.overall_att` | `diff_diff:SunAbrahamResults.att` | Read and construct with `att`; `overall_att` (and the sibling `overall_*` names) are removed at 4.0 - `AttributeError`, no warned release preceded it; 3.x pickles load unchanged. | +| M-052 | field-flip | `diff_diff:ImputationDiDResults.overall_att` | `diff_diff:ImputationDiDResults.att` | Read and construct with `att`; `overall_att` (and the sibling `overall_*` names) are removed at 4.0 - `AttributeError`, no warned release preceded it; 3.x pickles load unchanged. | +| M-053 | field-flip | `diff_diff:TwoStageDiDResults.overall_att` | `diff_diff:TwoStageDiDResults.att` | Read and construct with `att`; `overall_att` (and the sibling `overall_*` names) are removed at 4.0 - `AttributeError`, no warned release preceded it; 3.x pickles load unchanged. | +| M-054 | field-flip | `diff_diff:StackedDiDResults.overall_att` | `diff_diff:StackedDiDResults.att` | Read and construct with `att`; `overall_att` (and the sibling `overall_*` names) are removed at 4.0 - `AttributeError`, no warned release preceded it; 3.x pickles load unchanged. | +| M-055 | field-flip | `diff_diff:EfficientDiDResults.overall_att` | `diff_diff:EfficientDiDResults.att` | Read and construct with `att`; `overall_att` (and the sibling `overall_*` names) are removed at 4.0 - `AttributeError`, no warned release preceded it; 3.x pickles load unchanged. | +| M-056 | field-flip | `diff_diff:WooldridgeDiDResults.overall_att` | `diff_diff:WooldridgeDiDResults.att` | Read and construct with `att`; `overall_att` (and the sibling `overall_*` names) are removed at 4.0 - `AttributeError`, no warned release preceded it; 3.x pickles load unchanged. | +| M-057 | field-flip | `diff_diff:ChaisemartinDHaultfoeuilleResults.overall_att` | `diff_diff:ChaisemartinDHaultfoeuilleResults.att` | Read and construct with `att`; `overall_att` (and the sibling `overall_*` names) are removed at 4.0 - `AttributeError`, no warned release preceded it; 3.x pickles load unchanged. | +| M-058 | field-flip | `diff_diff:ContinuousDiDResults.overall_att` | `diff_diff:ContinuousDiDResults.att` | Read and construct with `att`; `overall_att` (and the sibling `overall_*` names) are removed at 4.0 - `AttributeError`, no warned release preceded it; 3.x pickles load unchanged. | | M-070 | function-wrappers | `diff_diff:imputation_did` | — | Call the estimator class directly instead of `imputation_did`. | | M-071 | function-wrappers | `diff_diff:two_stage_did` | — | Call the estimator class directly instead of `two_stage_did`. | | M-072 | function-wrappers | `diff_diff:stacked_did` | — | Call the estimator class directly instead of `stacked_did`. | @@ -640,8 +650,8 @@ does not mean no action is required, so read the `Fix` cell. | M-075 | function-wrappers | `diff_diff:triple_difference` | — | Call the estimator class directly instead of `triple_difference`. | | M-076 | function-wrappers | `diff_diff:bacon_decompose` | — | Call the estimator class directly instead of `bacon_decompose`. | | M-077 | function-wrappers | `diff_diff:chaisemartin_dhaultfoeuille` | — | Call the estimator class directly instead of `chaisemartin_dhaultfoeuille`. | -| M-151 | lwdid-results-contract | `diff_diff:LWDiDResults.pvalue` | `diff_diff:LWDiDResults.p_value` | Read `.p_value`; the alias warns from 4.0 and is removed at 5.0. | -| M-152 | lwdid-results-contract | `diff_diff:LWDiDResults.ci` | `diff_diff:LWDiDResults.conf_int` | Read `.conf_int`; the alias warns from 4.0 and is removed at 5.0. | +| M-151 | lwdid-results-contract | `diff_diff:LWDiDResults.pvalue` | `diff_diff:LWDiDResults.p_value` | Read `.p_value`; the alias is removed at 4.0 (`AttributeError`). | +| M-152 | lwdid-results-contract | `diff_diff:LWDiDResults.ci` | `diff_diff:LWDiDResults.conf_int` | Read `.conf_int`; the alias is removed at 4.0 (`AttributeError`). | | M-013 | merge-ddd | `diff_diff:StaggeredTripleDifference` | `diff_diff:TripleDifference.fit[first_treat]` | See the merges section for the worked before/after. | | M-014 | merge-ddd | `diff_diff:StaggeredTripleDiffResults` | — | Both designs return `TripleDifferenceResults` (`results.design` says which); read `att` / `se` / ... where you read `overall_*`; event-study and cohort surfaces are `results.aggregate(...)`. 3.x staggered pickles cannot load. | | M-085 | merge-ddd | `diff_diff:TripleDifference.fit[time]` | — | In 2x2x2 mode, `time=` raises - pass `post=` for the 0/1 post dummy. `time=` now means the staggered calendar column only. | @@ -650,12 +660,12 @@ does not mean no action is required, so read the `Fix` cell. | M-010 | merge-mpd | `diff_diff:MultiPeriodDiD` | `diff_diff:TwoWayFixedEffects.fit[event_study]` | See the merges section for the worked before/after. | | M-011 | merge-mpd | `diff_diff:MultiPeriodDiDResults` | — | Read the unified event-study surface on the merged `TwoWayFixedEffects` results instead of this container. | | M-012 | merge-mpd | `diff_diff:PeriodEffect` | — | Superseded by the unified event-study representation - read the event-study surface rather than per-period `PeriodEffect` objects. | -| M-016 | merge-mpd | `diff_diff:EventStudyResults.period_effects` | — | Not removed at 4.0: `period_effects` is a `FutureWarning` property view over the unified event-study surface (calendar surfaces only; keyed by calendar period, reference omitted), removed at 5.0. Read `to_dataframe()` or the columnar arrays instead. | +| M-016 | merge-mpd | `diff_diff:EventStudyResults.period_effects` | — | Removed at 4.0 (`AttributeError`; the compatibility view once planned for a 5.0 window was retired before any release carried it). Read `to_dataframe()` - one row per `event_time`, reference row included - or the columnar arrays instead. | | M-083 | merge-mpd | `diff_diff:TwoWayFixedEffects.fit[time]` | — | In static mode (`event_study=False`), `time=` raises - pass `post=` for the 0/1 post dummy. `time=` now means the calendar column only. | | M-015 | merge-qdid | `diff_diff:QDiD` | `diff_diff:ChangesInChanges[method]` | See the merges section for the worked before/after. | | M-143 | merge-qdid | `diff_diff:ChangesInChangesResults.estimator` | `diff_diff:ChangesInChangesResults.method` | See the merges section for the worked before/after. | -| M-148 | obligation-sc-inner-budget | `diff_diff:SyntheticControl[inner_max_iter]` | — | Drop it. The donor weights are solved exactly (one non-negative least squares) since #842, so there is no inner iteration budget; passing it warns `DeprecationWarning` at 4.0 and is a `TypeError` at 5.0. The former Frank-Wolfe budget is NOT a knob to carry over to `optimizer_options=` (that is the outer `V` search). | -| M-149 | obligation-sc-inner-budget | `diff_diff:SyntheticControl[inner_min_decrease]` | — | Drop it. An exact inner solve has no stopping tolerance; passing it warns `DeprecationWarning` at 4.0 and is a `TypeError` at 5.0. Do NOT translate it into `optimizer_options={"xatol": ...}` - that tolerance governs the outer `V` search, a different quantity. | +| M-148 | obligation-sc-inner-budget | `diff_diff:SyntheticControl[inner_max_iter]` | — | Drop it. The donor weights are solved exactly (one non-negative least squares) since #842, so there is no inner iteration budget; passing it warned `DeprecationWarning` in 3.13.0 and is a `TypeError` at 4.0. Positional callers: `standardize`/`alpha`/`seed`/`v_cv_t0` moved up two slots - pass them by keyword. The former Frank-Wolfe budget is NOT a knob to carry over to `optimizer_options=` (that is the outer `V` search). | +| M-149 | obligation-sc-inner-budget | `diff_diff:SyntheticControl[inner_min_decrease]` | — | Drop it. An exact inner solve has no stopping tolerance; passing it warned `DeprecationWarning` in 3.13.0 and is a `TypeError` at 4.0 (positional slots after it shift up two - pass later params by keyword). Do NOT translate it into `optimizer_options={"xatol": ...}` - that tolerance governs the outer `V` search, a different quantity. | | M-001 | obligation-sdid-params | `diff_diff:SyntheticDiD[lambda_reg]` | — | Drop it. It has been IGNORED since 3.0.0, so 3.x already auto-computes regularization - do NOT copy its value into `zeta_omega=`, which activates an override that was inert and changes weights, ATT and inference. Set `zeta_omega=` only as a deliberate new choice. | | M-002 | obligation-sdid-params | `diff_diff:SyntheticDiD[zeta]` | — | Drop it. It has been IGNORED since 3.0.0, so 3.x already auto-computes regularization - do NOT copy its value into `zeta_lambda=`, which activates an override that was inert and changes weights, ATT and inference. Set `zeta_lambda=` only as a deliberate new choice. | | M-003 | obligation-sdid-params | `diff_diff:SyntheticDiDResults.placebo_effects` | `diff_diff:SyntheticDiDResults.variance_effects` | Read `variance_effects` instead of `placebo_effects`. | @@ -703,6 +713,8 @@ does not mean no action is required, so read the `Fix` cell. | M-030 | renames-post | `diff_diff:DifferenceInDifferences.fit[time]` | `diff_diff:DifferenceInDifferences.fit[post]` | Rename the keyword: `time=` becomes `post=`. | | M-137 | renames-post | `diff_diff:permutation_test[time]` | `diff_diff:permutation_test[post]` | Rename the keyword: `time=` becomes `post=`. | | M-138 | renames-post | `diff_diff:leave_one_out_test[time]` | `diff_diff:leave_one_out_test[post]` | Rename the keyword: `time=` becomes `post=`. | +| M-156 | renames-post | `diff_diff:run_placebo_test[time]` | — | Pass the calendar column as `time=` and list its post-treatment values in `post_periods=` (now required for every `test_type`); the 0/1 dummy for the permutation / leave-one-out tests is derived as `data[time].isin(post_periods)`. Code that passed the 0/1 indicator as `time=` with `post_periods=[1]` returns identical numbers; code that passed it without `post_periods` must add `post_periods=[1]`; code that passed a multi-period calendar column now gets real permutation / leave-one-out results instead of error dicts. | +| M-157 | renames-post | `diff_diff:run_all_placebo_tests[time]` | — | Same contract on the suite wrapper: pass the calendar column as `time=` and list its post-treatment values in `post_periods=` (now required for every `test_type`); the 0/1 dummy for the permutation / leave-one-out tests is derived as `data[time].isin(post_periods)`. Code that passed the 0/1 indicator as `time=` with `post_periods=[1]` returns identical numbers; code that passed it without `post_periods` must add `post_periods=[1]`; code that passed a multi-period calendar column now gets real permutation / leave-one-out results instead of error dicts. A malformed `post_periods` / `time` column now raises before any test runs rather than being swallowed into error dicts. | | M-045 | renames-robust-drop | `diff_diff:DifferenceInDifferences[robust]` | — | Drop `robust=True` (already the `hc1` default); `robust=False` becomes `vcov_type="classical"`. | | M-046 | renames-robust-drop | `diff_diff:TripleDifference[robust]` | — | Drop it. `TripleDifference` accepts only `vcov_type="hc1"`, so `robust=` never changed its inference - do NOT write `vcov_type="classical"`, which raises. | | M-047 | renames-robust-drop | `diff_diff:HeterogeneousAdoptionDiD[robust]` | — | HAD's legacy default is non-robust: `robust=True` becomes `vcov_type="hc1"`; drop `robust=False`. | diff --git a/docs/tutorials/17_brand_awareness_survey.ipynb b/docs/tutorials/17_brand_awareness_survey.ipynb index fc13892eb..a2e920a4a 100644 --- a/docs/tutorials/17_brand_awareness_survey.ipynb +++ b/docs/tutorials/17_brand_awareness_survey.ipynb @@ -51,10 +51,10 @@ "id": "7c6c8ec1", "metadata": { "execution": { - "iopub.execute_input": "2026-08-10T22:40:42.719610Z", - "iopub.status.busy": "2026-08-10T22:40:42.718984Z", - "iopub.status.idle": "2026-08-10T22:40:43.435847Z", - "shell.execute_reply": "2026-08-10T22:40:43.435408Z" + "iopub.execute_input": "2026-11-01T18:47:45.169485Z", + "iopub.status.busy": "2026-11-01T18:47:45.169423Z", + "iopub.status.idle": "2026-11-01T18:47:45.905898Z", + "shell.execute_reply": "2026-11-01T18:47:45.905300Z" } }, "outputs": [], @@ -105,10 +105,10 @@ "id": "c6960896", "metadata": { "execution": { - "iopub.execute_input": "2026-08-10T22:40:43.437105Z", - "iopub.status.busy": "2026-08-10T22:40:43.437005Z", - "iopub.status.idle": "2026-08-10T22:40:43.447254Z", - "shell.execute_reply": "2026-08-10T22:40:43.446893Z" + "iopub.execute_input": "2026-11-01T18:47:45.907181Z", + "iopub.status.busy": "2026-11-01T18:47:45.907070Z", + "iopub.status.idle": "2026-11-01T18:47:45.917561Z", + "shell.execute_reply": "2026-11-01T18:47:45.917220Z" } }, "outputs": [ @@ -174,10 +174,10 @@ "id": "53cf1176", "metadata": { "execution": { - "iopub.execute_input": "2026-08-10T22:40:43.448115Z", - "iopub.status.busy": "2026-08-10T22:40:43.448056Z", - "iopub.status.idle": "2026-08-10T22:40:43.454094Z", - "shell.execute_reply": "2026-08-10T22:40:43.453787Z" + "iopub.execute_input": "2026-11-01T18:47:45.918693Z", + "iopub.status.busy": "2026-11-01T18:47:45.918612Z", + "iopub.status.idle": "2026-11-01T18:47:45.925419Z", + "shell.execute_reply": "2026-11-01T18:47:45.925080Z" } }, "outputs": [ @@ -288,16 +288,16 @@ "id": "c03c6b19", "metadata": { "execution": { - "iopub.execute_input": "2026-08-10T22:40:43.455041Z", - "iopub.status.busy": "2026-08-10T22:40:43.454990Z", - "iopub.status.idle": "2026-08-10T22:40:43.527034Z", - "shell.execute_reply": "2026-08-10T22:40:43.526646Z" + "iopub.execute_input": "2026-11-01T18:47:45.926566Z", + "iopub.status.busy": "2026-11-01T18:47:45.926499Z", + "iopub.status.idle": "2026-11-01T18:47:45.999365Z", + "shell.execute_reply": "2026-11-01T18:47:45.998963Z" } }, "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA94AAAHqCAYAAADyGZa5AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjExLjEsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvctoD+AAAAAlwSFlzAAAPYQAAD2EBqD+naQAAl6RJREFUeJzt3Qd4VFX6x/Ffekgg9N5BehdFpNkrioqia1/d1bX33XWLu+vqrr27q2tvf3uv2AsoIoJIB+m9k4QAIckk/+c9NzPpIYFMpn0/z3Of3Llzc+fk3iQz733PeU9cUVFRkQAAAAAAQFDEB+ewAAAAAACAwBsAAAAAgCAj4w0AAAAAQBAReAMAAAAAEEQE3gAAAAAABBGBNwAAAAAAQUTgDQAAAABAEBF4AwAAAAAQRATeAAAAAAAEUWIwDw4AiG2nnXaasrKyAo/j4+OVnp6uXr166fzzz1fv3r1D2r4333xTjzzyiN599101aNCg2n3vu+8+ffjhh7rwwgt15pln1lsbY8GMGTPctZg/f77y8vLUtm1bHXbYYRo3bpz7fQmVH3/8UX/605/2uJ/9Lj/77LO6+eabNWLEiHppGwAgssQVFRUVhboRAIDodNBBB6lv3776xz/+4R4XFBRo69ateuqpp/Tdd9/p5ZdfVp8+fULWvmeeeUa33XabC/yqC/B2796tUaNGqUuXLsrOztbEiRMVFxdXr22NRvYR5NZbb9Urr7yic845R0cddZS7DgsWLNCTTz6p3NxcPfTQQyG7QbNr1y5t3LixTCD+5z//WTfddJNGjx4d2N64cWN3g6lly5ZKS0sLSVsBAOGNjDcAIKgsk9y5c+fA4+7du7tge/jw4XrppZf0z3/+M+yvgAXaFgRa9tOy3d9++60LxLFv/vOf/7ig+4knnnC/D34WaB977LG66KKL9Lvf/c71SLDgNtS/uytWrHBfW7RoUWa7adKkSb23DwAQORjjDQCod5bVTEpKUqNGjdzjnJwcl+20DPgll1yik08+WS+88IJ7zufzue3WxXvs2LE69dRTdccddygzMzOQlbTv/eKLL1x38PHjx7suypbJLt3N3Y7z9NNP61e/+pVOPPFE3X777dq5c2eN2vv666/rwAMP1P7776/99ttPL774YuA5y87a61sw7rd+/Xq3zdpZ2sUXX6x77rnHrW/fvl0PP/ywC+SPO+44nX322S4AtXb6s6t2DOvebs9NmDBBU6ZMcc/l5+e7bL1tt3Ny+eWXa9q0aYHXsQy+fa+17S9/+Yv7eU8//fQyx6/Jccw333yjSy+91B3Dhg5YhnrdunW13qe8bdu26fHHH3f7lw66/VJTU91xNmzY4Npo1/mEE07Qo48+WmHfq666SjfccEONf67qzu3eWLJkiTue9eIo/fiHH35wN2vsvJxxxhnud9p6T9h1t99TuyaPPfaYCgsLA8eydft9P++881zb7cbD119/vddtAwCEBwJvAEC9dy+2ANi6bf/mN78JBBsrV650gZYFG9ddd12gC/rf//53Nw77rLPO0v333+8CEctA33jjjWW+1wIcGx98yy236JprrtF7773nugX72f7+4/zrX/9yQbl1Y94Ty3Ja0GbBvDnppJP01VdfBQLLHj16uO7nts3PAjBr06effhrYtmnTJhegWtd763JvgdXkyZN17bXXuhsGFpw98MADLhg1dlPAjvHvf/9bV199tQvSevbs6X5eCyT/+9//upsQFsj7x8xbIGksO2/fa8GwZY/vuusunXLKKbr33ntdF27/edvTcfwBtQXG9rydw7lz57q2+4PFmuxTGfvZrZ1HHHFElftYVtnO10cffeSyz926ddOrr77qfof87Dp88skn7sZITX+uqs7t3rLfOzue/0aO//EVV1yhfv36ufM/YMAA97tpNw/s98V6etgNF2uf3djx++Mf/+hu2BxzzDHuuWHDhumyyy5zvUMAABHMxngDABAMw4YNKxo8eHDRkUce6ZbDDz+8aMiQIUU9e/YsuvHGG4u2b9/u9svKynLbrrzyyjLfv3bt2qJevXoVvfXWW2W2/+9//3Pbd+7cWZSTk+O+95JLLimzz3/+85+i3r17u+fnzJnj9il/nHPOOcdtt32qctddd7mfYceOHe7x+vXr3XHvvffewD433HBD0fHHH1/m8VFHHeWObT+DeeONN4r69evnfuaPPvrIPbds2bIyr3XppZcWnXjiiW7966+/dvs88cQTZfZ5//333XY7Rml/+MMfig4++OCi3bt3F3377bdun0cffbTMPhdffHHRCSecUOPj/O1vfysaOXJkmeet/cuXLy8qKChwj2uyT2UeeOAB9/orVqwoqs61115b1Ldv3yKfz1f0xRdfuO/58ccfy/wu9O/f3/0O1fTnqurc7on/+8ofe968eW77p59+WubxLbfcEtjHfn9s26mnnlrme08++eTA773/ur344otl9rntttvc76D/ZwQARB4y3gCAoBo8eLDr4uxfLON6wQUXuCrWlm0szTKapTVt2lQff/yxG09tWeT/+7//c4XarCuuZT1LdxUfNGhQme9t1aqVy35aN3bLrpry2VXrDlwdy0y//fbbLvvoL5rVunVrV7naspTWpdlYBe7Fixe7btHm+++/169//WslJCS47sb+zLAVm2vYsKEOPvhgffbZZ0pJSdGXX37pis394Q9/cF2gd+zYUaYNNia+NNvfvq982w8//HBt2bJFs2fPDmwbOHBghXNiXdxrehy7dpapty7y1l4739Z+y0Tbz2Zqsk9lLCtskpOTq70G1kbrHm/Xwgqa2fjq999/P/C89Wyw65qRkVHr81P+3Na10r+T9vtjS//+/cvsY2PXrdu9v+3GsuLl227n1f+7BACIPBRXAwDUa4EqM2TIEK1du9YF1dYl11+YyoKq8uN8LbCycbB2HAtaLNCzQOu5554r05XZ9i3Npi4zFqBbYGjBmH9MuZ9Voa6OdR+377UxtqUDOeumbot1cbau8WPGjHFj1m2csLXRKmFbMG5BuwVLFkjZjQP/jQb7WWzqKev63KZNG9cN+YADDnAV35cuXVqmDc2bNy/z2I5tgagVHyvNxg4bC/7957P8FGlWid1/zmpyHOuebgGvjWm3btOJiYnu5oHdVPBX9a7JPpWxKcPM5s2b3TmoirXTzoE/QLcu+e+8844bu25jqRctWlRmfHdNfi67MVDZua1rlVXKr27aOmu7XSMb/12anV/jv7EDAIg8BN4AgJCwQmUWeFtgW1VFaMug2jjsK6+80o1z9QfTFojXhgXcFnjZmOLSAbqNta3Oa6+95saa29jr8qzitgWbFnhbIGeBsxVYs4yy3WiwwNIy2xZc//TTTy5Qt8ylefDBB932//3vfzrkkEMCx6xJRtMCN8tcW++BytjNi59//rlOjmOs8Jgtli2eNGmSu+Fh2W2rRu7PqNdkn/LsZoWxGxLls8B+luW1c2c9Dvys8J7VCLCbHFOnTnXtHDlyZK1+runTp6s+1HbKOWu7ZcWranuzZs3qqGUAgPpGV3MAQEgsX77cBdJWZK0qs2bNcl+tIJo/6DZz5sxxX0sX2aqOBcWmfMBV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1HP38ol/rBh0O8Xb3rxo747dqG3VwTUVwgHUAoF3mLr//vv15Zdf6oMPPlCbNm0C22+66SZdfPHF+uyzz1zm2wL0H3/8UcuWLXPrgwYN0sCBAwOZ2Lfeeku/+tWv9PHHHysnJ0eHHXaY2rdvr++++05LlizR4MGDNWDAgMD+b7zxhtt/4sSJLst8xBFHqGXLloHXr+71rJtiRkZGma6KU6dO1S+//KI+ffq45+bPn69x48YF2mYZFvtZsrKyNHLkSO233357fc6qa5sFx/azXXDBBYGs9A8//KCtW7fq2GOPrXF7rP32Gg0bNnTnskmTJoHn7Hx9+umnmj59utLS0tzPDAAAgmzFt1L22r37XrLWAOoJgXcY2r59u5577jnddtttZYJuc+2116pdu3YuiE5PT9dZZ53lugZaAL169Wr961//0q233uqyrxZs3nvvvS6Q7Ny5s1atWuUC+iOPPFKbNm1yWXR7jSeeeEIjRoxw+993331699133f6WZb777rv1zDPPqH///tq9e3e1r7dt27ZA5te6mv/jH/9wgagF79YF3QJyC1gt8PZniV955RV37F27dun222/Xk08+6dpSW3tq28aNG93rnX322YHA+5tvvnE3BSzwrkl7Hn744UB3982bN7vnn3/+eXfDwfzmN79Rr1693HP2mtYzYfTo0XXyOwEAAMrJXCXNeln64fGanZpOB0vdDiVrDSAkYjbwtoxoVSxA9AdTdbVvbcbN/fzzzy6QPPjggys816xZM1166aVufcWKFRozZowuv/zywPGvv/56F2hb4GcseLZA94wzznDrNm2MZXMtoPQ//9FHHwWCS8vaWib3mmuucY//9re/ueD8//7v/7R+/fo9vp7frFmz3LhBy9h36dJFubm5Oumkk8rsY691zjnnuMVcddVVbqz03gTetWlbVaprj2XR//Of/+ill15ygb157733tGbNGnXq1Mk9PvPMM11vgZkzZ+qFF15wGXYCbwAA6pBVGJ//nvTzi9KySVV0L6/CYX+RunJDHEBoxGzg7S8mU5m2bdu6cdV+Ns7aX/W1POuGbRlkP8sWW9BcngVlNWXdno1lpKtjWemrr77ardvYaus6XtkYa38ga13Trb2l52y17LllwkvzB57mlFNOcZnkHTt21Pj1jHWTHzJkiAu6jWXATzjhBNfFvTTrzu3Xs2dP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ixfr2muv1RFHHKGnnnpKa9euVVxcnNavX6+33npLxx13nM4880wXlAMAgPDm8/n01VdfucXWw87uHOn7/3rrcQnSyGtC3SIAAIKb8X7sscf0zjvv6IwzztCNN96o1q1bV9gnJydHn3zyiR544AEXiN92221KTk7eu5YBAICgKioq0rp16wLrYWf6015XczNggtSsa6hbBABAcANvC7TffvttJSUlVblPw4YNNX78eLdYAL5r1y4CbwAAUHv5ud4UYk6cNPo6ziIAIPoD75NOOqnK5yzTnZ6e7rqd+x199NH73joAABCbZr4g5Wzw1vucKLXsFeoWAQAQmqrmM2fO1OGHH66hQ4fqoIMO0vvvv78vhwMAAJB8+dLkB0rOxJgbOCsAgNgNvG+//XZdfvnlmjdvnp5++mn94x//UH5+ft21DgAAxJ7Zr0lZK731/Y6S2g4KdYsAAKifwPtPf/qTFixYUGZbbm6uWrRooYSEBLVs2dJVRC0oKNi3FgEAgNhV6JMm3VPymGw3ACCWxniPHTvWVTPv2bOnrrnmGrVr105XXHGFrr/+eqWmprpx3pb9btCgQXBbDAAAote8d6Qti731zqOkTsND3SIAAOov8B41apRGjBjhKpv/+te/1pFHHqnf/e53+uabb7RmzRq1atVKjRs33vcWAQCAepGYmKgzzzwzfM62TWk26d6Sx2OuD2VrAAAIzRjv+Ph4N1XYu+++64LsU045RS+//LI6d+5M0A0AAPbNoo+lDbO99fZDpW6HcUYBALEVeNv47bfeeku33XabC7bPOeccvf7661q7dq1OPPFEvfPOOyqyO9UAAAB7le2+u+Tx6BukUtOUAgAQE4G3VTC3gLtRo0aaPn26LrroIjVr1kx//etf9fjjj+uLL75w2XAb6w0AAMKf3VSfPHmyW2w9pJZ9I62e5q236if1PDa07QEAIBRjvO1N+eGHH1b37t3d4wMOOEDbt293gXinTp30wAMP6Oeff67LtgEAgCCynmqrVq1y68OHh7iIWZls93U2vi2UrQEAIDSB94ABA1w388MOO0yLFi1y04dZ0F3aoEHMswkAAGpp1Q9exts06y71O4VTCACIKjW+nfz3v/9dBx54oOtm3rBhQz3zzDPBbRkAAIgN35TKdo+6VopPCGVrAAAIXcY7PT3dTR8GAABQZ9bNkn752FvP6CAN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lMDPnJgYFR8LqhVr1zgWcY2jH9c4+vki5H+1P/70x6PViSuKgvlDtmzZouXLl4e6GQAAAACAGNOlSxc1b948+gNvu2OflZXl7trHx9N7HgAAAAAQXJbp3r17txo3brzHXmNREXgDAAAAABCuSA8DAAAAABBE0V9FJYxY54LHH39cv/71r5WcnBzq5qAO5efn69NPP9WqVavUpk0bHX300WrQoAHnOIrk5ubqww8/1MaNGzV48GANHz481E1CkFghF/tf/dvf/lbp6emc5yixY8cOPfbYY2W2paam6tJLLw1Zm1D31q5d696Preun/a8eNmwYpzlK/PDDD/r2228rfe7qq69muGkUdd3+7LPPtGzZMrVr107HHHNM1MRNZLzr0Z133ql77rnHBWmIrg/pp556qp599lnl5OTopZde0oknnqitW7eGummoww/sdo0/+ugjbd++XTfccIP+9a9/cX6j1L333qv//ve/7m8b0WPu3Ln6v//7P1cPxr9Ey4c5eKZOnarjjz9es2bN0rZt23Tttdfqvvvu4/RECavjVPrv15b33ntPX3/9teLi4kLdPNRR0P2b3/xG999/v/tM/fzzz2vChAlhP3NVTZHxrgdr1qzRrbfeqmnTptXHy6GevfLKK643gwXc9qZg/zQsSHvmmWd03XXXcT2igF3Lpk2buiyoOeyww3Tuuee6TFmzZs1C3TzUoenTp+v111/nnEahefPmaeDAgbrssstC3RQEgb0P/+Uvf3Ef0u2rGTdunAvGER0OOOAAt/hZVtSKK1twRuAdHX766Sf3Nzt58mT3+cp6rthnrk8++UQnnXSSIh0Z73rw8MMPq2XLlnrooYfq4+VQzzp16uQCbH9Fffvao0cP1yUZ0TNFxDXXXFMmA26VK5OSkkLaLtQte4O3D+wEZtEbeHfr1k1vvvmmnnjiCc2cOTPUTUId92iw4V4XXXRRYFufPn3c8D5EH+uRdMstt+jKK69U+/btQ90c1KHCwsLAHN52Q8WWmsyRHQnIeNeDm2++2XVnmz9/fn28HOrZEUccUeaxdY2xO3WlAzVEtrFjxwY+uFvPBhs/+O9//1uNGjUKddNQhx588EEXmFlXVRsWhOhi78Hr1q1z6zbky26K/+53v2OMd5RYvHix2rZtq7S0NL366qvKzs7WiBEj1Ldv31A3DUFgWe6EhASdc845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", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA94AAAHqCAYAAADyGZa5AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjExLjIsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvgI3uAAAAAAlwSFlzAAAPYQAAD2EBqD+naQAAl6RJREFUeJzt3Qd4VFX6x/Ffekgg9N5BehdFpNkrioqia1/d1bX33XWLu+vqrr27q2tvf3uv2AsoIoJIB+m9k4QAIckk/+c9NzPpIYFMpn0/z3Of3Llzc+fk3iQz733PeU9cUVFRkQAAAAAAQFDEB+ewAAAAAACAwBsAAAAAgCAj4w0AAAAAQBAReAMAAAAAEEQE3gAAAAAABBGBNwAAAAAAQUTgDQAAAABAEBF4AwAAAAAQRATeAAAAAAAEUWIwDw4AiG2nnXaasrKyAo/j4+OVnp6uXr166fzzz1fv3r1D2r4333xTjzzyiN599101aNCg2n3vu+8+ffjhh7rwwgt15pln1lsbY8GMGTPctZg/f77y8vLUtm1bHXbYYRo3bpz7fQmVH3/8UX/605/2uJ/9Lj/77LO6+eabNWLEiHppGwAgssQVFRUVhboRAIDodNBBB6lv3776xz/+4R4XFBRo69ateuqpp/Tdd9/p5ZdfVp8+fULWvmeeeUa33XabC/yqC/B2796tUaNGqUuXLsrOztbEiRMVFxdXr22NRvYR5NZbb9Urr7yic845R0cddZS7DgsWLNCTTz6p3NxcPfTQQyG7QbNr1y5t3LixTCD+5z//WTfddJNGjx4d2N64cWN3g6lly5ZKS0sLSVsBAOGNjDcAIKgsk9y5c+fA4+7du7tge/jw4XrppZf0z3/+M+yvgAXaFgRa9tOy3d9++60LxLFv/vOf/7ig+4knnnC/D34WaB977LG66KKL9Lvf/c71SLDgNtS/uytWrHBfW7RoUWa7adKkSb23DwAQORjjDQCod5bVTEpKUqNGjdzjnJwcl+20DPgll1yik08+WS+88IJ7zufzue3WxXvs2LE69dRTdccddygzMzOQlbTv/eKLL1x38PHjx7suypbJLt3N3Y7z9NNP61e/+pVOPPFE3X777dq5c2eN2vv666/rwAMP1P7776/99ttPL774YuA5y87a61sw7rd+/Xq3zdpZ2sUXX6x77rnHrW/fvl0PP/ywC+SPO+44nX322S4AtXb6s6t2DOvebs9NmDBBU6ZMcc/l5+e7bL1tt3Ny+eWXa9q0aYHXsQy+fa+17S9/+Yv7eU8//fQyx6/Jccw333yjSy+91B3Dhg5YhnrdunW13qe8bdu26fHHH3f7lw66/VJTU91xNmzY4Npo1/mEE07Qo48+WmHfq666SjfccEONf67qzu3eWLJkiTue9eIo/fiHH35wN2vsvJxxxhnud9p6T9h1t99TuyaPPfaYCgsLA8eydft9P++881zb7cbD119/vddtAwCEBwJvAEC9dy+2ANi6bf/mN78JBBsrV650gZYFG9ddd12gC/rf//53Nw77rLPO0v333+8CEctA33jjjWW+1wIcGx98yy236JprrtF7773nugX72f7+4/zrX/9yQbl1Y94Ty3Ja0GbBvDnppJP01VdfBQLLHj16uO7nts3PAjBr06effhrYtmnTJhegWtd763JvgdXkyZN17bXXuhsGFpw98MADLhg1dlPAjvHvf/9bV199tQvSevbs6X5eCyT/+9//upsQFsj7x8xbIGksO2/fa8GwZY/vuusunXLKKbr33ntdF27/edvTcfwBtQXG9rydw7lz57q2+4PFmuxTGfvZrZ1HHHFElftYVtnO10cffeSyz926ddOrr77qfof87Dp88skn7sZITX+uqs7t3rLfOzue/0aO//EVV1yhfv36ufM/YMAA97tpNw/s98V6etgNF2uf3djx++Mf/+hu2BxzzDHuuWHDhumyyy5zvUMAABHMxngDABAMw4YNKxo8eHDRkUce6ZbDDz+8aMiQIUU9e/YsuvHGG4u2b9/u9svKynLbrrzyyjLfv3bt2qJevXoVvfXWW2W2/+9//3Pbd+7cWZSTk+O+95JLLimzz3/+85+i3r17u+fnzJnj9il/nHPOOcdtt32qctddd7mfYceOHe7x+vXr3XHvvffewD433HBD0fHHH1/m8VFHHeWObT+DeeONN4r69evnfuaPPvrIPbds2bIyr3XppZcWnXjiiW7966+/dvs88cQTZfZ5//333XY7Rml/+MMfig4++OCi3bt3F3377bdun0cffbTMPhdffHHRCSecUOPj/O1vfysaOXJkmeet/cuXLy8qKChwj2uyT2UeeOAB9/orVqwoqs61115b1Ldv3yKfz1f0xRdfuO/58ccfy/wu9O/f3/0O1fTnqurc7on/+8ofe968eW77p59+WubxLbfcEtjHfn9s26mnnlrme08++eTA773/ur344otl9rntttvc76D/ZwQARB4y3gCAoBo8eLDr4uxfLON6wQUXuCrWlm0szTKapTVt2lQff/yxG09tWeT/+7//c4XarCuuZT1LdxUfNGhQme9t1aqVy35aN3bLrpry2VXrDlwdy0y//fbbLvvoL5rVunVrV7naspTWpdlYBe7Fixe7btHm+++/169//WslJCS47sb+zLAVm2vYsKEOPvhgffbZZ0pJSdGXX37pis394Q9/cF2gd+zYUaYNNia+NNvfvq982w8//HBt2bJFs2fPDmwbOHBghXNiXdxrehy7dpapty7y1l4739Z+y0Tbz2Zqsk9lLCtskpOTq70G1kbrHm/Xwgqa2fjq999/P/C89Wyw65qRkVHr81P+3Na10r+T9vtjS//+/cvsY2PXrdu9v+3GsuLl227n1f+7BACIPBRXAwDUa4EqM2TIEK1du9YF1dYl11+YyoKq8uN8LbCycbB2HAtaLNCzQOu5554r05XZ9i3Npi4zFqBbYGjBmH9MuZ9Voa6OdR+377UxtqUDOeumbot1cbau8WPGjHFj1m2csLXRKmFbMG5BuwVLFkjZjQP/jQb7WWzqKev63KZNG9cN+YADDnAV35cuXVqmDc2bNy/z2I5tgagVHyvNxg4bC/7957P8FGlWid1/zmpyHOuebgGvjWm3btOJiYnu5oHdVPBX9a7JPpWxKcPM5s2b3TmoirXTzoE/QLcu+e+8844bu25jqRctWlRmfHdNfi67MVDZua1rlVXKr27aOmu7XSMb/12anV/jv7EDAIg8BN4AgJCwQmUWeFtgW1VFaMug2jjsK6+80o1z9QfTFojXhgXcFnjZmOLSAbqNta3Oa6+95saa29jr8qzitgWbFnhbIGeBsxVYs4yy3WiwwNIy2xZc//TTTy5Qt8ylefDBB932//3vfzrkkEMCx6xJRtMCN8tcW++BytjNi59//rlOjmOs8Jgtli2eNGmSu+Fh2W2rRu7PqNdkn/LsZoWxGxLls8B+luW1c2c9Dvys8J7VCLCbHFOnTnXtHDlyZK1+runTp6s+1HbKOWu7ZcWranuzZs3qqGUAgPpGV3MAQEgsX77cBdJWZK0qs2bNcl+tIJo/6DZz5sxxX0sX2aqOBcWmfMBV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1HP38ol/rBh0O8Xb3rxo747dqG3VwTUVwgHUAoF3mLr//vv15Zdf6oMPPlCbNm0C22+66SZdfPHF+uyzz1zm2wL0H3/8UcuWLXPrgwYN0sCBAwOZ2Lfeeku/+tWv9PHHHysnJ0eHHXaY2rdvr++++05LlizR4MGDNWDAgMD+b7zxhtt/4sSJLst8xBFHqGXLloHXr+71rJtiRkZGma6KU6dO1S+//KI+ffq45+bPn69x48YF2mYZFvtZsrKyNHLkSO233357fc6qa5sFx/azXXDBBYGs9A8//KCtW7fq2GOPrXF7rP32Gg0bNnTnskmTJoHn7Hx9+umnmj59utLS0tzPDAAAgmzFt1L22r37XrLWAOoJgXcY2r59u5577jnddtttZYJuc+2116pdu3YuiE5PT9dZZ53lugZaAL169Wr961//0q233uqyrxZs3nvvvS6Q7Ny5s1atWuUC+iOPPFKbNm1yWXR7jSeeeEIjRoxw+993331699133f6WZb777rv1zDPPqH///tq9e3e1r7dt27ZA5te6mv/jH/9wgagF79YF3QJyC1gt8PZniV955RV37F27dun222/Xk08+6dpSW3tq28aNG93rnX322YHA+5tvvnE3BSzwrkl7Hn744UB3982bN7vnn3/+eXfDwfzmN79Rr1693HP2mtYzYfTo0XXyOwEAAMrJXCXNeln64fGanZpOB0vdDiVrDSAkYjbwtoxoVSxA9AdTdbVvbcbN/fzzzy6QPPjggys816xZM1166aVufcWKFRozZowuv/zywPGvv/56F2hb4GcseLZA94wzznDrNm2MZXMtoPQ//9FHHwWCS8vaWib3mmuucY//9re/ueD8//7v/7R+/fo9vp7frFmz3LhBy9h36dJFubm5Oumkk8rsY691zjnnuMVcddVVbqz03gTetWlbVaprj2XR//Of/+ill15ygb157733tGbNGnXq1Mk9PvPMM11vgZkzZ+qFF15wGXYCbwAA6pBVGJ//nvTzi9KySVV0L6/CYX+RunJDHEBoxGzg7S8mU5m2bdu6cdV+Ns7aX/W1POuGbRlkP8sWW9BcngVlNWXdno1lpKtjWemrr77ardvYaus6XtkYa38ga13Trb2l52y17LllwkvzB57mlFNOcZnkHTt21Pj1jHWTHzJkiAu6jWXATzjhBNfFvTTrzu3Xs2dP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ixfr2muv1RFHHKGnnnpKa9euVVxcnNavX6+33npLxx13nM4880wXlAMAgPDm8/n01VdfucXWw87uHOn7/3rrcQnSyGtC3SIAAIKb8X7sscf0zjvv6IwzztCNN96o1q1bV9gnJydHn3zyiR544AEXiN92221KTk7eu5YBAICgKioq0rp16wLrYWf6015XczNggtSsa6hbBABAcANvC7TffvttJSUlVblPw4YNNX78eLdYAL5r1y4CbwAAUHv5ud4UYk6cNPo6ziIAIPoD75NOOqnK5yzTnZ6e7rqd+x199NH73joAABCbZr4g5Wzw1vucKLXsFeoWAQAQmqrmM2fO1OGHH66hQ4fqoIMO0vvvv78vhwMAAJB8+dLkB0rOxJgbOCsAgNgNvG+//XZdfvnlmjdvnp5++mn94x//UH5+ft21DgAAxJ7Zr0lZK731/Y6S2g4KdYsAAKifwPtPf/qTFixYUGZbbm6uWrRooYSEBLVs2dJVRC0oKNi3FgEAgNhV6JMm3VPymGw3ACCWxniPHTvWVTPv2bOnrrnmGrVr105XXHGFrr/+eqWmprpx3pb9btCgQXBbDAAAote8d6Qti731zqOkTsND3SIAAOov8B41apRGjBjhKpv/+te/1pFHHqnf/e53+uabb7RmzRq1atVKjRs33vcWAQCAepGYmKgzzzwzfM62TWk26d6Sx2OuD2VrAAAIzRjv+Ph4N1XYu+++64LsU045RS+//LI6d+5M0A0AAPbNoo+lDbO99fZDpW6HcUYBALEVeNv47bfeeku33XabC7bPOeccvf7661q7dq1OPPFEvfPOOyqyO9UAAAB7le2+u+Tx6BukUtOUAgAQE4G3VTC3gLtRo0aaPn26LrroIjVr1kx//etf9fjjj+uLL75w2XAb6w0AAMKf3VSfPHmyW2w9pJZ9I62e5q236if1PDa07QEAIBRjvO1N+eGHH1b37t3d4wMOOEDbt293gXinTp30wAMP6Oeff67LtgEAgCCynmqrVq1y68OHh7iIWZls93U2vi2UrQEAIDSB94ABA1w388MOO0yLFi1y04dZ0F3aoEHMswkAAGpp1Q9exts06y71O4VTCACIKjW+nfz3v/9dBx54oOtm3rBhQz3zzDPBbRkAAIgN35TKdo+6VopPCGVrAAAIXcY7PT3dTR8GAABQZ9bNkn752FvP6CAN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lMDPnJgYFR8LqhVr1zgWcY2jH9c4+vki5H+1P/70x6PViSuKgvlDtmzZouXLl4e6GQAAAACAGNOlSxc1b948+gNvu2OflZXl7trHx9N7HgAAAAAQXJbp3r17txo3brzHXmNREXgDAAAAABCuSA8DAAAAABBE0V9FJYxY54LHH39cv/71r5WcnBzq5qAO5efn69NPP9WqVavUpk0bHX300WrQoAHnOIrk5ubqww8/1MaNGzV48GANHz481E1CkFghF/tf/dvf/lbp6emc5yixY8cOPfbYY2W2paam6tJLLw1Zm1D31q5d696Preun/a8eNmwYpzlK/PDDD/r2228rfe7qq69muGkUdd3+7LPPtGzZMrVr107HHHNM1MRNZLzr0Z133ql77rnHBWmIrg/pp556qp599lnl5OTopZde0oknnqitW7eGummoww/sdo0/+ugjbd++XTfccIP+9a9/cX6j1L333qv//ve/7m8b0WPu3Ln6v//7P1cPxr9Ey4c5eKZOnarjjz9es2bN0rZt23Tttdfqvvvu4/RECavjVPrv15b33ntPX3/9teLi4kLdPNRR0P2b3/xG999/v/tM/fzzz2vChAlhP3NVTZHxrgdr1qzRrbfeqmnTptXHy6GevfLKK643gwXc9qZg/zQsSHvmmWd03XXXcT2igF3Lpk2buiyoOeyww3Tuuee6TFmzZs1C3TzUoenTp+v111/nnEahefPmaeDAgbrssstC3RQEgb0P/+Uvf3Ef0u2rGTdunAvGER0OOOAAt/hZVtSKK1twRuAdHX766Sf3Nzt58mT3+cp6rthnrk8++UQnnXSSIh0Z73rw8MMPq2XLlnrooYfq4+VQzzp16uQCbH9Fffvao0cP1yUZ0TNFxDXXXFMmA26VK5OSkkLaLtQte4O3D+wEZtEbeHfr1k1vvvmmnnjiCc2cOTPUTUId92iw4V4XXXRRYFufPn3c8D5EH+uRdMstt+jKK69U+/btQ90c1KHCwsLAHN52Q8WWmsyRHQnIeNeDm2++2XVnmz9/fn28HOrZEUccUeaxdY2xO3WlAzVEtrFjxwY+uFvPBhs/+O9//1uNGjUKddNQhx588EEXmFlXVRsWhOhi78Hr1q1z6zbky26K/+53v2OMd5RYvHix2rZtq7S0NL366qvKzs7WiBEj1Ldv31A3DUFgWe6EhASdc845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", "text/plain": [ "
" ] @@ -350,10 +350,10 @@ "id": "e5db9120", "metadata": { "execution": { - "iopub.execute_input": "2026-08-10T22:40:43.528019Z", - "iopub.status.busy": "2026-08-10T22:40:43.527954Z", - "iopub.status.idle": "2026-08-10T22:40:43.531455Z", - "shell.execute_reply": "2026-08-10T22:40:43.531141Z" + "iopub.execute_input": "2026-11-01T18:47:46.000601Z", + "iopub.status.busy": "2026-11-01T18:47:46.000511Z", + "iopub.status.idle": "2026-11-01T18:47:46.004404Z", + "shell.execute_reply": "2026-11-01T18:47:46.003946Z" } }, "outputs": [ @@ -409,10 +409,10 @@ "id": "efbf20d6", "metadata": { "execution": { - "iopub.execute_input": "2026-08-10T22:40:43.532339Z", - "iopub.status.busy": "2026-08-10T22:40:43.532286Z", - "iopub.status.idle": "2026-08-10T22:40:43.536753Z", - "shell.execute_reply": "2026-08-10T22:40:43.536407Z" + "iopub.execute_input": "2026-11-01T18:47:46.005573Z", + "iopub.status.busy": "2026-11-01T18:47:46.005511Z", + "iopub.status.idle": "2026-11-01T18:47:46.010440Z", + "shell.execute_reply": "2026-11-01T18:47:46.010103Z" } }, "outputs": [ @@ -473,10 +473,10 @@ "id": "387a083f", "metadata": { "execution": { - "iopub.execute_input": "2026-08-10T22:40:43.537613Z", - "iopub.status.busy": "2026-08-10T22:40:43.537558Z", - "iopub.status.idle": "2026-08-10T22:40:43.540024Z", - "shell.execute_reply": "2026-08-10T22:40:43.539672Z" + "iopub.execute_input": "2026-11-01T18:47:46.011471Z", + "iopub.status.busy": "2026-11-01T18:47:46.011415Z", + "iopub.status.idle": "2026-11-01T18:47:46.014086Z", + "shell.execute_reply": "2026-11-01T18:47:46.013790Z" } }, "outputs": [ @@ -554,10 +554,10 @@ "id": "f891e2f1", "metadata": { "execution": { - "iopub.execute_input": "2026-08-10T22:40:43.540848Z", - "iopub.status.busy": "2026-08-10T22:40:43.540788Z", - "iopub.status.idle": "2026-08-10T22:40:43.548122Z", - "shell.execute_reply": "2026-08-10T22:40:43.547797Z" + "iopub.execute_input": "2026-11-01T18:47:46.015209Z", + "iopub.status.busy": "2026-11-01T18:47:46.015154Z", + "iopub.status.idle": "2026-11-01T18:47:46.023135Z", + "shell.execute_reply": "2026-11-01T18:47:46.022687Z" } }, "outputs": [ @@ -614,16 +614,16 @@ "id": "14ff1a06", "metadata": { "execution": { - "iopub.execute_input": "2026-08-10T22:40:43.548986Z", - "iopub.status.busy": "2026-08-10T22:40:43.548927Z", - "iopub.status.idle": "2026-08-10T22:40:43.606727Z", - "shell.execute_reply": "2026-08-10T22:40:43.606371Z" + "iopub.execute_input": "2026-11-01T18:47:46.024138Z", + "iopub.status.busy": "2026-11-01T18:47:46.024069Z", + "iopub.status.idle": "2026-11-01T18:47:46.074409Z", + "shell.execute_reply": "2026-11-01T18:47:46.074003Z" } }, "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAxYAAAHqCAYAAACZcdjsAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjExLjEsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvctoD+AAAAAlwSFlzAAAPYQAAD2EBqD+naQAAVkBJREFUeJzt3QeUU9X69/Fn6Ay9VwsXpSMISBFEunQvFsSLXhDFjgW8IoJix3utyOV/UVTsqIAiiChFpIgoiAgiCCjSlN7bAEPe9duukzczTEkmM4Qk389aWTM5OTlnJzkn2c/Zz947wefz+QwAAAAAwpArnCcDAAAAAIEFAAAAgGxBiwUAAACAsBFYAAAAAAgbgQUAAACAsBFYAAAAAAgbgQUAAACAsBFYAAAAAAgbgQUAAACAsOUJfxMAos0ff/xhEydOtO+//9527dplpUuXtgYNGljv3r2tVKlSFk169OhhHTp0sNtuuy3H9/Xuu+/aG2+8YW+++aZVrFjRosW6devsgw8+sKFDhwa1/ttvv21vvfWWde/e3QYMGGCxZNOmTTZ27Fh77LHH/Mv69OljderUsX/961/Zvr/Bgwfb0qVL/fcTEhKsYMGCds4559hVV11lLVu2tEj68ccf7b777rMXXnjBvQdpGTlypH366afpbkPnX068d+H6xz/+YU2aNLG777470kUB4gaBBRBnVEF46KGH7NJLL7Ubb7zRypUr5ypbL7/8sqs4v/rqq1a3bl2LFi+99JIVKlTotOxr3759tnHjRjt+/LhFE71HGzZsCHr9d955x4oXL+4CjP79+1uBAgUsViio+Oqrr04JtCtUqJAj+9u2bZslJSW591JOnjxp+/fvt6lTp7r39vnnn7cuXbpYpBw9etQd0/qbHl180DrTp0+33Llzn/J44cKF7Uy0efNmO++88yJdDCCuEFgAceSHH35wV1BvueUWu+uuu/zLq1evbs2bN7eePXu65V988YXly5fPosFZZ50V6SLElG+//dZ+//13+/DDD+3aa691gaiurCPr8uTJ41ooAtWrV88WLFjgWsAiGViE4uyzz3avBQDSwzcEEEdGjRrl0p7SShtSeoZSIlSx1JX5MmXKuOW6sqrK5ZYtWyxv3rx2wQUX2O233+5aOrxUpOuuu861esybN89OnDhhzZo1cwHKzJkzbdKkSe4K7YUXXmj33nuvFStWzP88VVx1VVHP07aVFpL6Cnkw+0+dCqV9Tp482Q4cOOCCpiuvvNKlAT377LOuQqfn/POf/7QdO3bYrFmz7MiRI1a/fn278847/dsNVqRfv6iCOn78eHdVuWzZsu71du7c2T2mfc6fP9+1srRv3961VmWUfjNhwgSrWrWqe5/0OrTdtAKLJUuWuJaNX3/91UqWLGndunVz+1Wqj1x//fV28cUX2/r162316tXWuHFjGzZsmD/AVarVb7/95o67Vq1aWd++ff2ve/v27fbKK6+4NB19NqqUX3HFFda2bVv//oNZJ7Xhw4fbtGnT3Pp6L5Qi07VrV/eYPjeV6csvv3RX6GvUqOE+w8DAVeUeN26cK7eu0mtfSqPKahBeokQJS0xM9N/P6D3Tcfrxxx+7zzhXrlxWq1Ytu/XWW/0Bi8qh1+Tz+eyzzz5zx77W0TGtgMCjxxQ07ty502rWrGktWrSwnEw98lKtvHMvmHIG+1qy+/MAkA18AOLCwYMHfTVq1PA9+OCDQT9n1KhRvkaNGvmmTp3q++WXX3xfffWVr2vXrr4ePXr412nYsKGvXr16vocfftj3888/+yZOnOirXr26r127dr4+ffr4vv/+e9+sWbPcdgYOHJjiefXr1/fdcMMNviVLlvg+//xzX/PmzX3XX3+97+TJkyHt/4knnvDff/HFF31169b1vf32274VK1b4nn32WV/t2rV91apV8y1atMj/nMaNG/seeOAB348//uhbuHChr3379r4rrrgiw/dj9OjRbju///77GfP633vvPV/NmjV9I0eO9P3000++CRMm+OrUqeNev2zdutV34403+jp16uTKfejQoXRf3759+9x797///c/d/+STT9zr1XsUaNq0aW6fI0aM8C1fvtz36aef+ho0aOB76aWX/Ou0bt3aHW96DT/88IPviy++cMvfeecdt/yZZ55xz9W2LrnkEl/Pnj19R48e9SUlJfk6duzo69evn3ueXrf3vs+bN89tI5h10rJ9+3b3Geh91ntx4MABt1yflT6z22+/3W3v22+/9XXr1s29hmPHjrl19PnoNQ8dOtS9H19++aV7XGVITk5Od586BrReavosW7Vq5Vu1alVQ75k+Fx1beq1ff/217+qrr/a1adPGd+LECf9r0DE9YMAA39KlS32LFy/2de/e3S33yvfaa6+5c+HV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SgUAAAAgqqRsbgjCzp07rWnTpgQVAAAAALLeYqGgQhPi7d27lw7XABBn1Mdu6tSpbibtMmXKuBECMxrMQ+trePIvv/zSjRqoiVCzc/APAEAUBxbqsJ2QkGBt27a12rVrnxJctGvXzrp3756dZQQAnGYvvfSS61P34IMPplh+77332hdffOG///LLL9u7775rtWrVSnM7gevPmDHDxo4dm+H6AIA4Ciw0ClTLli3TfbxIkSLhlgkAEGEKAnbs2JEisFizZo0LEjT0+PXXX29z5syx7777zt5++20bMWLEKdvw1q9QoYKbr2jt2rUp1teEq0899ZQb8EOtGTNnznSTpV511VV2zjnnuG0Esw4AIEoDi2bNmrkbACC+lCtXzrU4aNjxatWqWbFixVygcPToUVu6dOkp66vFY/Dgwa5lWxOr6vmB6+/fv98mTpxoRYsWtQMHDviHL1eLxqRJk6xKlSqWlJTk1ilVqlSKIc7fe+89t1zrAACiNLAQfbF/8skntnjxYmvYsKFdccUVtnDhQitbtqydd9552V9KAECOW7BggU2fPt39v337dhcMDB061N1v3ry5de7c2d9i/cQTT9gHH3xghw8fdmlTL774YobbVn8MBRAKFLz1NVy5fjMUYKhlQjetI6NGjbLnn3/e//xdu3a5i1qtWrVyvz8///zzKesAAKIwsBgwYICtWrXK/QBoLgtRk/mgQYPcjxKzaANA9Pnll19cK0Ag735iYqILLDyVK1e21q1bu07Zd999t0t1SouCkxYtWtjx48fd3yVLlvjXV0Bxyy23WKFChezNN990KU76LbnrrrtOaQHR782YMWOsQIEC1r59e/f8tFpJAABRFFhoxm3lyWpUEI0OpR8G0UgfGvlDebmaQA8AEF1U8fdaDFJ33j7//PPt119/dYFE/fr1rW/fvu6mFoS33nrLbr/9dhd8BNL633zzjRUsWNCNJKhtanAPb32lRHmtGRpx0JMnTx7bs2dPim1p3iQFFaJRpdJaBwAQZYGFOuOpSTz1D4holKitW7dmV9kAAKdR9erV3U3UgnDs2DG7+uqr/Y+rI/azzz5rl112mV100UWuNULpS0pvUkpU6t+FdevWuWDC62OhQCJwfaVCyZYtW2zz5s2uFWTlypV24sQJ93+gYNYBAERZYKF+FGqZ8DrQefQDpPzca6+9NjvLBwA4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DR482GbNmmWlSpUKah2c+UL57gh5uFlFLM2bN0+z5UCRb0ZNah4FI8rva9SokWuq27Bhg/+mkwIAAADBUwaIWgFuu+02d//DDz9093VTcKA6l0bjzJcvX1Db865QK31Jdb4TJ064C8pqhQhUo0YNW7Bggbvw3LRpUxfQ6OJzqOsgNoTcYqGoVy0T6meRurOQDpprr702021oPUU/mg/jpptuSvHYm2++GfLs3QAAAPGsXLlyrsKeVudttTCESs9XGpSyUDTX2M6dO90cZJ9++qnrwO3p2LGjf5QoTTeg4EFZKYGCWQdxGli0adPGRo8ebf369XPNWgoQFL1OmjTJdQzq1KlTptvQzN26AQAAIHyvvfaav8P1VVdd5QICpRxlROv+8ccfaT6m/rJKVVddb/Xq1Va5cmV/ypUuDGv4WVF6vO57/0uBAin7tHlp83L06NE010GcBhZqQhs/fry98sorNmfOHJf6pCY2DUF75513njJhHgAAAM48usirFCfRcP+6WKwJjHVTp261MqhPhDeak0aHUiCiEUHVCVsdxJXFojqh+mPoOaonarCdQBMnTrTGjRu756gDuaReB3EaWGiEAKVBDRw40N0AAABwZlDFvlKlSmnOYZHaZ5995u9ArYmLlYHy97//3XXS1sTFjzzyiAsGvBFAFXholCcFBUqJevfdd/378yidPXX2ilK0br311hQTJQeT4YI4CCw0XJhy41588cWcKREAAACypHr16q6ulll/Wc2+rT4UanWQzZs3uwBBz9eQtLppZu5evXq5PhqXXnqp6xiuztfyww8/uL8aMUqpTprb7JxzznGjRqXuIK7+tJmtgzgNLNQ5SDNlq3MQBwUAAEB0GTt27CnL1Klat0AKLrw+GKNGjfJ3wA5UqFAhu/feezPcXzDrIE4DCx0c69evt9atW1vVqlVPGYNYw5p17do1O8sIAAAAINYCC80zkdFsiYGzMgIAACD2FClSxKVOZTRoTzDrIM4DC02Op9vu3bvd6AAejUdcunRpAgsAAIAYp+FsdQt3HcSWkGfe1hBkmhjljTfeSLF89uzZbnI89b0AAAAAEF9CDiw0a/bGjRtTDBsmffv2df0vZsyYkZ3lAwAAABCLgYU6bjdp0sQSExNPeUwTpvz+++/ZVTYAAAAAsRpYaEzjH3/80T9To0eT5mm5+lkAAAAAiC8h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AwYM8N8UUOg41sUvBR76zdNvbiCdH6tWrfJfkJg9e7bNmzfPXeRTa4Qo2P/888/dRTj9VdAeSOu9++677pZWkD9t2jT3XD3uBe0efReoH+0HH3xwyveCLiqoFWbs2LGuXJGcCBnZQKlQQGYWLlzoq1Onjm///v2+pKQkX6NGjXwTJ050j61Zs8ZXu3Zt35EjR9z9rl27+po3b+775JNP3P3//ve/vvvuu8938uRJ37hx43xbtmzxb/eLL77wVa9e3X+/devWviFDhvjvJycn+y677DLfBx984F+2du1at7/Nmze7++3atfPdeeed/senTZvmq1u3rntuZs/ftWuXr1q1ainK9P777/vWrVuX4WNApP3yyy/u+Fy5cmWG6+3evdudry+88II7H/bs2ePr2bOn74EHHnCP//nnn247t9xyizuHDx065Lvyyiv95+E333zja9asmTsf5Ouvv/Z16NDBd/DgQd/27dvdczdt2uS226BBA3eOy08//eSrX7++77rrrnP3jx075mvfvr1v9OjR7rtA+7n55pvdd4NoG9rW448/7u4fOHDAt3HjRnd+r1692i3Tc3r37u3KGvid8emnn7r/A8sT7usGzkTTp093x21q3u/VV1995Xv++ed9/fr1S/G4zqNJkya5/1966SX3G61zaejQoe43Tedzjx49fN27d/c99dRT7ly4+uqr3Xmrc1bfAZ06dfI9/fTTvmuuucbXokUL3759+9z2li5d6mvYsKE7t5555hnfVVdd5atXr57/t3PGjBm+xo0bu3P7scce81144YW+jz/+2D129OhRt/4VV1zhtq0y9OrVy9UzEJ3o3Yag6EqIrvoVKVLE3dcVxfHjx9uVV15p559/vuswqquD6kCpKxe6uqiUI10t1NWRq6++2uWC6sqqd7Vw/fr1rolVrSG6QqF5TKR69er+/f7xxx9uPW1bfz1a96effrJKlSq5+xqq2HPOOee4iVx001WVjJ6vFhgNfawrmT179nSv8ZprrnHr6GpNeo8BkaZzQ84+++wM15s5c6abXFTHsVoA1XI4aNAg++c//2nDhg3zr6cBGQoUKOA/v9UCKGrl08h9a9asca0MF198sT/1Uf05PBqIIzEx0W1Xateu7c5/pSnK3LlzXUuklnkpT/peuOuuu+zJJ5/0b0etHaL+Irqp/B5tv1GjRvbdd99l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", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAxYAAAHqCAYAAACZcdjsAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjExLjIsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvgI3uAAAAAAlwSFlzAAAPYQAAD2EBqD+naQAAVkBJREFUeJzt3QeUU9X69/Fn6Ay9VwsXpSMISBFEunQvFsSLXhDFjgW8IoJix3utyOV/UVTsqIAiiChFpIgoiAgiCCjSlN7bAEPe9duukzczTEkmM4Qk389aWTM5OTlnJzkn2c/Zz947wefz+QwAAAAAwpArnCcDAAAAAIEFAAAAgGxBiwUAAACAsBFYAAAAAAgbgQUAAACAsBFYAAAAAAgbgQUAAACAsBFYAAAAAAgbgQUAAACAsOUJfxMAos0ff/xhEydOtO+//9527dplpUuXtgYNGljv3r2tVKlSFk169OhhHTp0sNtuuy3H9/Xuu+/aG2+8YW+++aZVrFjRosW6devsgw8+sKFDhwa1/ttvv21vvfWWde/e3QYMGGCxZNOmTTZ27Fh77LHH/Mv69OljderUsX/961/Zvr/Bgwfb0qVL/fcTEhKsYMGCds4559hVV11lLVu2tEj68ccf7b777rMXXnjBvQdpGTlypH366afpbkPnX068d+H6xz/+YU2aNLG777470kUB4gaBBRBnVEF46KGH7NJLL7Ubb7zRypUr5ypbL7/8sqs4v/rqq1a3bl2LFi+99JIVKlTotOxr3759tnHjRjt+/LhFE71HGzZsCHr9d955x4oXL+4CjP79+1uBAgUsViio+Oqrr04JtCtUqJAj+9u2bZslJSW591JOnjxp+/fvt6lTp7r39vnnn7cuXbpYpBw9etQd0/qbHl180DrTp0+33Llzn/J44cKF7Uy0efNmO++88yJdDCCuEFgAceSHH35wV1BvueUWu+uuu/zLq1evbs2bN7eePXu65V988YXly5fPosFZZ50V6SLElG+//dZ+//13+/DDD+3aa691gaiurCPr8uTJ41ooAtWrV88WLFjgWsAiGViE4uyzz3avBQDSwzcEEEdGjRrl0p7SShtSeoZSIlSx1JX5MmXKuOW6sqrK5ZYtWyxv3rx2wQUX2O233+5aOrxUpOuuu861esybN89OnDhhzZo1cwHKzJkzbdKkSe4K7YUXXmj33nuvFStWzP88VVx1VVHP07aVFpL6Cnkw+0+dCqV9Tp482Q4cOOCCpiuvvNKlAT377LOuQqfn/POf/7QdO3bYrFmz7MiRI1a/fn278847/dsNVqRfv6iCOn78eHdVuWzZsu71du7c2T2mfc6fP9+1srRv3961VmWUfjNhwgSrWrWqe5/0OrTdtAKLJUuWuJaNX3/91UqWLGndunVz+1Wqj1x//fV28cUX2/r162316tXWuHFjGzZsmD/AVarVb7/95o67Vq1aWd++ff2ve/v27fbKK6+4NB19NqqUX3HFFda2bVv//oNZJ7Xhw4fbtGnT3Pp6L5Qi07VrV/eYPjeV6csvv3RX6GvUqOE+w8DAVeUeN26cK7eu0mtfSqPKahBeokQJS0xM9N/P6D3Tcfrxxx+7zzhXrlxWq1Ytu/XWW/0Bi8qh1+Tz+eyzzz5zx77W0TGtgMCjxxQ07ty502rWrGktWrSwnEw98lKtvHMvmHIG+1qy+/MAkA18AOLCwYMHfTVq1PA9+OCDQT9n1KhRvkaNGvmmTp3q++WXX3xfffWVr2vXrr4ePXr412nYsKGvXr16vocfftj3888/+yZOnOirXr26r127dr4+ffr4vv/+e9+sWbPcdgYOHJjiefXr1/fdcMMNviVLlvg+//xzX/PmzX3XX3+97+TJkyHt/4knnvDff/HFF31169b1vf32274VK1b4nn32WV/t2rV91apV8y1atMj/nMaNG/seeOAB348//uhbuHChr3379r4rrrgiw/dj9OjRbju///77GfP633vvPV/NmjV9I0eO9P3000++CRMm+OrUqeNev2zdutV34403+jp16uTKfejQoXRf3759+9x797///c/d/+STT9zr1XsUaNq0aW6fI0aM8C1fvtz36aef+ho0aOB76aWX/Ou0bt3aHW96DT/88IPviy++cMvfeecdt/yZZ55xz9W2LrnkEl/Pnj19R48e9SUlJfk6duzo69evn3ueXrf3vs+bN89tI5h10rJ9+3b3Geh91ntx4MABt1yflT6z22+/3W3v22+/9XXr1s29hmPHjrl19PnoNQ8dOtS9H19++aV7XGVITk5Od586BrReavosW7Vq5Vu1alVQ75k+Fx1beq1ff/217+qrr/a1adPGd+LECf9r0DE9YMAA39KlS32LFy/2de/e3S33yvfaa6+5c+HV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SgUAAAAgqqRsbgjCzp07rWnTpgQVAAAAALLeYqGgQhPi7d27lw7XABBn1Mdu6tSpbibtMmXKuBECMxrMQ+trePIvv/zSjRqoiVCzc/APAEAUBxbqsJ2QkGBt27a12rVrnxJctGvXzrp3756dZQQAnGYvvfSS61P34IMPplh+77332hdffOG///LLL9u7775rtWrVSnM7gevPmDHDxo4dm+H6AIA4Ciw0ClTLli3TfbxIkSLhlgkAEGEKAnbs2JEisFizZo0LEjT0+PXXX29z5syx7777zt5++20bMWLEKdvw1q9QoYKbr2jt2rUp1teEq0899ZQb8EOtGTNnznSTpV511VV2zjnnuG0Esw4AIEoDi2bNmrkbACC+lCtXzrU4aNjxatWqWbFixVygcPToUVu6dOkp66vFY/Dgwa5lWxOr6vmB6+/fv98mTpxoRYsWtQMHDviHL1eLxqRJk6xKlSqWlJTk1ilVqlSKIc7fe+89t1zrAACiNLAQfbF/8skntnjxYmvYsKFdccUVtnDhQitbtqydd9552V9KAECOW7BggU2fPt39v337dhcMDB061N1v3ry5de7c2d9i/cQTT9gHH3xghw8fdmlTL774YobbVn8MBRAKFLz1NVy5fjMUYKhlQjetI6NGjbLnn3/e//xdu3a5i1qtWrVyvz8///zzKesAAKIwsBgwYICtWrXK/QBoLgtRk/mgQYPcjxKzaANA9Pnll19cK0Ag735iYqILLDyVK1e21q1bu07Zd999t0t1SouCkxYtWtjx48fd3yVLlvjXV0Bxyy23WKFChezNN990KU76LbnrrrtOaQHR782YMWOsQIEC1r59e/f8tFpJAABRFFhoxm3lyWpUEI0OpR8G0UgfGvlDebmaQA8AEF1U8fdaDFJ33j7//PPt119/dYFE/fr1rW/fvu6mFoS33nrLbr/9dhd8BNL633zzjRUsWNCNJKhtanAPb32lRHmtGRpx0JMnTx7bs2dPim1p3iQFFaJRpdJaBwAQZYGFOuOpSTz1D4holKitW7dmV9kAAKdR9erV3U3UgnDs2DG7+uqr/Y+rI/azzz5rl112mV100UWuNULpS0pvUkpU6t+FdevWuWDC62OhQCJwfaVCyZYtW2zz5s2uFWTlypV24sQJ93+gYNYBAERZYKF+FGqZ8DrQefQDpPzca6+9NjvLBwA4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DR482GbNmmWlSpUKah2c+UL57gh5uFlFLM2bN0+z5UCRb0ZNah4FI8rva9SokWuq27Bhg/+mkwIAAADBUwaIWgFuu+02d//DDz9093VTcKA6l0bjzJcvX1Db865QK31Jdb4TJ064C8pqhQhUo0YNW7Bggbvw3LRpUxfQ6OJzqOsgNoTcYqGoVy0T6meRurOQDpprr702021oPUU/mg/jpptuSvHYm2++GfLs3QAAAPGsXLlyrsKeVudttTCESs9XGpSyUDTX2M6dO90cZJ9++qnrwO3p2LGjf5QoTTeg4EFZKYGCWQdxGli0adPGRo8ebf369XPNWgoQFL1OmjTJdQzq1KlTptvQzN26AQAAIHyvvfaav8P1VVdd5QICpRxlROv+8ccfaT6m/rJKVVddb/Xq1Va5cmV/ypUuDGv4WVF6vO57/0uBAin7tHlp83L06NE010GcBhZqQhs/fry98sorNmfOHJf6pCY2DUF75513njJhHgAAAM48usirFCfRcP+6WKwJjHVTp261MqhPhDeak0aHUiCiEUHVCVsdxJXFojqh+mPoOaonarCdQBMnTrTGjRu756gDuaReB3EaWGiEAKVBDRw40N0AAABwZlDFvlKlSmnOYZHaZ5995u9ArYmLlYHy97//3XXS1sTFjzzyiAsGvBFAFXholCcFBUqJevfdd/378yidPXX2ilK0br311hQTJQeT4YI4CCw0XJhy41588cWcKREAAACypHr16q6ulll/Wc2+rT4UanWQzZs3uwBBz9eQtLppZu5evXq5PhqXXnqp6xiuztfyww8/uL8aMUqpTprb7JxzznGjRqXuIK7+tJmtgzgNLNQ5SDNlq3MQBwUAAEB0GTt27CnL1Klat0AKLrw+GKNGjfJ3wA5UqFAhu/feezPcXzDrIE4DCx0c69evt9atW1vVqlVPGYNYw5p17do1O8sIAAAAINYCC80zkdFsiYGzMgIAACD2FClSxKVOZTRoTzDrIM4DC02Op9vu3bvd6AAejUdcunRpAgsAAIAYp+FsdQt3HcSWkGfe1hBkmhjljTfeSLF89uzZbnI89b0AAAAAEF9CDiw0a/bGjRtTDBsmffv2df0vZsyYkZ3lAwAAABCLgYU6bjdp0sQSExNPeUwTpvz+++/ZVTYAAAAAsRpYaEzjH3/80T9To0eT5mm5+lkAAAAAiC8hd95u1aqVvfTSS25Wxm7durnhZjXdu1KgNNZxly5dcqakAAAAAGInsNCkeG+//baNGTPGJkyY4IIKzdrYuHFjF3BoaDEAAAAA8SXkwEJKlChhQ4YMyf7SAAAAAIifwCI5OdkWL17s5rJQ34pAmo27Ro0a2VU+AAAAALEYWBw8eNBNdrJhwwbXcpErV8r+37179yawAAAAAOJMyIHFvHnz3CR58+fPTzHzNgAAAID4FfJwszt37rSmTZsSVAAAAADIemChoGLhwoW2d+/eUJ8KAAAAIEaFnAqlDtsJCQnWtm1bq127thUvXjzF4+3atbPu3btnZxkBAAAAxFpgoVGgWrZsme7jzGMBAAAAxJ+QA4tmzZq5GwAAAACEHFjs37/fzbKdmaJFi7phaAEAAADEj6ADi/fff9+ee+65TNfr37+/3XfffeGWCwAAAEAsBhZXXXWVtWrVKtP1mNsCAAAAiD9BBxYKGAgaAADA6Xb9lR1s344NvPGn2Ynkk/7/r+nUwPLmzs1ncJoVK3OOvT1pRux23gYAADidFFRM6b2GN/00O5RkVnjhX/9PvOZXK5Sfj+B06/5ujE+QBwAAAACpEVgAAAAACBuBBQAAAICwEVgAAAAACBuBBQAAAICwEVgAAAAACBuBBQAAAICwEVgAAAAACBuBBQAAAICwEVgAAAAACBuBBQAAAICwEVgAAAAACBuBBQAAAICwEVgAAAAACBuBBQAAAICwEVgAAAAACBuBBQAAAICwEVgAAAAACBuBBQAAAICwEVgAAAAACBuBBQAAAICwEVgAAAAACFue8DcBAACAaPLnvr9uGTly7P//v2yTWcF8mW+3QrG/bohPBBYAAABx5uV5Zo9OC379Fs8Gt97wLmaPdMtysRDlzojAYs2aNVayZEkrXbp0pIsCAAAQ825pada9XvZvl9aK+BbxwOK3336z66+/3p588klr165dpIsDAAAQ80hZQsx13p4zZ4717t3b9u3LJMkPAAAAwBktYoHFZ599Zg8++KANGTLEihWjlw8AAAAQzSIWWDRo0MC+/PJL6969e6SKAAAAACDa+1iUL18+5OckJye7W8T4IrdrIOJ8f52D0crH+Ys45ov285cfYMQpn/kifu6Gsv+Id94OdfSoSDqadDSi+wciffwvW7Ysaj+EJM5fxLGkaD9/jyZFughAxI79ZVF07kZVYFGtWjVLTEyM2P4L5C9ghyO2dyCydPzXr18/aj+G/PkLRLoIQESP/6g+fwvkj3QRgIgd+/UjfO4ePnw46Iv7URVY5M6d290iJiFyuwYiLuGvczBaJXD+Io4lRPv5yw8w4lSCJUT83A1l/xEdbhYAAABAbDgjAosWLVow6zYAAAAQxc6IVKjnnnsu0kUAAAAAEO0tFgAAAACiG4EFAAAAgLARWAAAAAAIG4EFAAAAgLARWAAAAAAIG4EFAAAAgLARWAAAAAAIG4EFAAAAgLARWAAAAAAIG4EFAAAAgLARWAAAAAAIG4EFAAAAgLARWAAAAAAIG4EFAAAAgLARWAAAAAAIG4EFAAAAgLARWAAAAAAIG4EFAAAAgLARWAAAAAAIG4EFAAAAgLARWAAAAAAIG4EFAAAAgLARWAAAAAAIG4EFAAAAgLARWAAAAAAIG4EFAAAAgLARWAAAAAAIG4EFAAAAgLARWAAAAAAIG4EFAAAAgLARWAAAAAAIG4EFAAAAgLARWAAAAAAIG4EFAAAAgLARWAAAAAAIG4EFAAAAgLARWAAAAAAgsAAAAAAQebRYAAAAAAgbgQUAAACAsBFYAAAAAAgbgQUAAACAsBFYAAAAAAgbgQUAAACAsBFYAAAAAAgbgQUAAACAsBFYAAAAAAgbgQUAAACAsBFYAAAAAAgbgQUAAACAsBFYAAAAAAhbHougbdu22bx58yxXrlzWpk0bK1GiRCSLAwAAACDaWiyWLFliHTt2tIULF9r06dOtU6dO9uuvv0aqOAAAAACiMbB45JFH7LbbbrMXXnjBXn31VRdYPPPMM5EqDgAAAIBoCyw2bdpka9euta5du/qXXX755bZgwQI7duxYJIoEAAAAINr6WGzcuNHy5s1rFSpU8C+rXLmyHT9+3LZs2WJVqlRJsf7Jkyfd30OHDllycrJFyjmVKtjRxKMR2z8QSQUqVbADBw5E7YdwVsWzrWze8pEuBhARuStaVJ+/5SufY4cTEyJdDOC0K1/57Iifu0ePHk1RH89Igs/n89lpNmvWLBs8eLB9//33/mUHDx60hg0b2uTJk61mzZop1t+1a5f9/vvvp7uYAAAAAMzs3HPPtVKlSp15LRb58uU7JeUpKSnJ/S1YsOAp6xcrVsy9mPz587sRpAAAAADkPLVUqJ6u+nhmIhJYKO1JgYVaIrzI588//7TcuXNbuXLlTlk/T548mUZIAAAAALJf4cKFg1ovIpf/1YeiYsWK9sUXX/iXzZgxw6VCpdViAQAAAODMFpEWi4SEBNfH4l//+pebu+LIkSNuLos333wzEsUBAAAAEKaIdN72rFy50ubMmeP6XHTu3NmlSOHM9sYbb7iRuW688cZIFwVAAKWX7t+/36WN6uLN6aDvAqWxli9f3qWspqaRTDSaSJkyZXKsDOvWrXMjCmrQj8zKAwCI4cAC0UU/2N26dbMTJ07Yxx9/fMqwwABOv+XLl7uJRjXKnnJgDx8+7OYFuv/++61QoUI5uu/du3fbVVddZe+9956rzKf2f//3f/bNN9/Y22+/nWNlaNu2rQ0cONC6dOmSaXmAaKdAWhkeHl1EUAp5kyZNrE6dOjmyz8WLF7v5x6644ooc2T5iC0MsIWiTJk2ypk2b2iWXXGLvv/++f/knn3ySor/MDz/8YM8//7z/vq4m/vvf/7Y9e/aY4thFixa5lg+tM3HiRDc/iWfMmDGuJUuzsb/11lv+MZM198krr7xiL7/8si1btixFubT8t99+c+VQBUt/FfwEyuj5utI7ZcoUe+mll1x5duzYEdRjQKR9++23dt1119mll15qS5YssYULF9qnn35qK1assNtuu+2U9dWioaG9A+kq/+bNm93/al1I7xjXeaxzOJBGCNF5Wrp0af8ynbM7d+7McM4hBQCpRwbUOat5jGTfvn2njJeu1g8N+BFo+/btrlza3t69e9MsT7ivGzjTAov//ve//vv6Td26dav169fPhg4dmmOBhS4mAsEgsEBQ9OWlL5Y2bdq4GdP1vzdhin7Ux40b519XFXtV4FWZlx9//NEtK168uA0fPtweeeQR/2QvCgTuvPNO/3M//PBDu+WWW1wFRpUTDS+sK55///vfXSVAlYM77rjDbd8zYcIE96Wq+VFUWXj66afdPjyZPV8VMO1XkzZ+/fXX7vWpwpLZY0Ck6Xzq3bu39e3b16WUilJKn3nmmRTz/6hVo3v37taqVSt3cUDnnCrbogq1rvrrOS1atHD/6+r/hg0b/PsZMWKENW7c2LVYNm/e3H9hQee+1lfFRnSOaB8dO3a0Dh062C+//JKivKtXr3atKe3bt3dXWO+55x4XRIi2oeVapueqD54oUNDFjE6dOrlt66bvFHn00Ufd98To0aPdeqnLE+7rBs5UAwYM8N8UUOg41sUvBR76zdNvbiCdH6tWrfJfkJg9e7bNmzfPXeRTa4Qo2P/888/dRTj9VdAeSOu9++677pZWkD9t2jT3XD3uBe0efReoH+0HH3xwyveCLiqoFWbs2LGuXJGcCBnZQKlQQGYWLlzoq1Onjm///v2+pKQkX6NGjXwTJ050j61Zs8ZXu3Zt35EjR9z9rl27+po3b+775JNP3P3//ve/vvvuu8938uRJ37hx43xbtmzxb/eLL77wVa9e3X+/devWviFDhvjvJycn+y677DLfBx984F+2du1at7/Nmze7++3atfPdeeed/senTZvmq1u3rntuZs/ftWuXr1q1ainK9P777/vWrVuX4WNApP3yyy/u+Fy5cmWG6+3evdudry+88II7H/bs2ePr2bOn74EHHnCP//nnn247t9xyizuHDx065Lvyyiv95+E333zja9asmTsf5Ouvv/Z16NDBd/DgQd/27dvdczdt2uS226BBA3eOy08//eSrX7++77rrrnP3jx075mvfvr1v9OjR7rtA+7n55pvdd4NoG9rW448/7u4fOHDAt3HjRnd+r1692i3Tc3r37u3KGvid8emnn7r/A8sT7usGzkTTp093x21q3u/VV1995Xv++ed9/fr1S/G4zqNJkya5/1966SX3G61zaejQoe43Tedzjx49fN27d/c99dRT7ly4+uqr3Xmrc1bfAZ06dfI9/fTTvmuuucbXokUL3759+9z2li5d6mvYsKE7t5555hnfVVdd5atXr57/t3PGjBm+xo0bu3P7scce81144YW+jz/+2D129OhRt/4VV1zhtq0y9OrVy9UzEJ3o3Yag6EqIrvoVKVLE3dcVxfHjx9uVV15p559/vuswqquD6kCpKxe6uqiUI10t1NWRq6++2uWC6sqqd7Vw/fr1rolVrSG6QqF5TKR69er+/f7xxx9uPW1bfz1a96effrJKlSq5+xqq2HPOOee4iVx001WVjJ6vFhgNfawrmT179nSv8ZprrnHr6GpNeo8BkaZzQ84+++wM15s5c6abXFTHsVoA1XI4aNAg++c//2nDhg3zr6cBGQoUKOA/v9UCKGrl08h9a9asca0MF198sT/1Uf05PBqIIzEx0W1Xateu7c5/pSnK3LlzXUuklnkpT/peuOuuu+zJJ5/0b0etHaL+Irqp/B5tv1GjRvbdd99l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", "text/plain": [ "
" ] @@ -696,10 +696,10 @@ "id": "3d718ede", "metadata": { "execution": { - "iopub.execute_input": "2026-08-10T22:40:43.607716Z", - "iopub.status.busy": "2026-08-10T22:40:43.607656Z", - "iopub.status.idle": "2026-08-10T22:40:43.610994Z", - "shell.execute_reply": "2026-08-10T22:40:43.610598Z" + "iopub.execute_input": "2026-11-01T18:47:46.075583Z", + "iopub.status.busy": "2026-11-01T18:47:46.075517Z", + "iopub.status.idle": "2026-11-01T18:47:46.078666Z", + "shell.execute_reply": "2026-11-01T18:47:46.078342Z" } }, "outputs": [ @@ -747,10 +747,10 @@ "id": "ef7db9b1", "metadata": { "execution": { - "iopub.execute_input": "2026-08-10T22:40:43.611801Z", - "iopub.status.busy": "2026-08-10T22:40:43.611746Z", - "iopub.status.idle": "2026-08-10T22:40:43.615984Z", - "shell.execute_reply": "2026-08-10T22:40:43.615693Z" + "iopub.execute_input": "2026-11-01T18:47:46.079772Z", + "iopub.status.busy": "2026-11-01T18:47:46.079714Z", + "iopub.status.idle": "2026-11-01T18:47:46.084112Z", + "shell.execute_reply": "2026-11-01T18:47:46.083668Z" } }, "outputs": [ @@ -828,10 +828,10 @@ "id": "8e82a98a", "metadata": { "execution": { - "iopub.execute_input": "2026-08-10T22:40:43.616815Z", - "iopub.status.busy": "2026-08-10T22:40:43.616753Z", - "iopub.status.idle": "2026-08-10T22:40:43.618955Z", - "shell.execute_reply": "2026-08-10T22:40:43.618725Z" + "iopub.execute_input": "2026-11-01T18:47:46.085055Z", + "iopub.status.busy": "2026-11-01T18:47:46.084972Z", + "iopub.status.idle": "2026-11-01T18:47:46.087073Z", + "shell.execute_reply": "2026-11-01T18:47:46.086761Z" } }, "outputs": [ @@ -864,10 +864,11 @@ " - [MEDIUM] Step 6: Run placebo tests\n", " Why: Falsification tests using fake timing, permutation, and leave-one-out diagnostics to probe assumption validity.\n", " >>> from diff_diff import run_all_placebo_tests\n", - " >>> # Requires binary time indicator (post=0/1), not multi-period:\n", + " >>> # time= is the calendar period column; post_periods lists its\n", + " >>> # post-treatment values (the 0/1 post dummy is derived from them):\n", " >>> placebo = run_all_placebo_tests(\n", - " >>> data, outcome='y', treatment='treated', time='post',\n", - " >>> unit='unit_id', pre_periods=[0], post_periods=[1],\n", + " >>> data, outcome='y', treatment='treated', time='period',\n", + " >>> unit='unit_id', pre_periods=[0, 1], post_periods=[2, 3],\n", " >>> n_permutations=500, seed=42)\n", "\n", " * [HIGH] Step 4: Check if data is actually staggered\n", @@ -903,7 +904,7 @@ " {'baker_step': 6,\n", " 'label': 'Run placebo tests',\n", " 'why': 'Falsification tests using fake timing, permutation, and leave-one-out diagnostics to probe assumption validity.',\n", - " 'code': \"from diff_diff import run_all_placebo_tests\\n# Requires binary time indicator (post=0/1), not multi-period:\\nplacebo = run_all_placebo_tests(\\n data, outcome='y', treatment='treated', time='post',\\n unit='unit_id', pre_periods=[0], post_periods=[1],\\n n_permutations=500, seed=42)\",\n", + " 'code': \"from diff_diff import run_all_placebo_tests\\n# time= is the calendar period column; post_periods lists its\\n# post-treatment values (the 0/1 post dummy is derived from them):\\nplacebo = run_all_placebo_tests(\\n data, outcome='y', treatment='treated', time='period',\\n unit='unit_id', pre_periods=[0, 1], post_periods=[2, 3],\\n n_permutations=500, seed=42)\",\n", " 'priority': 'medium'},\n", " {'baker_step': 4,\n", " 'label': 'Check if data is actually staggered',\n", @@ -943,10 +944,10 @@ "id": "7d1c9510", "metadata": { "execution": { - "iopub.execute_input": "2026-08-10T22:40:43.619775Z", - "iopub.status.busy": "2026-08-10T22:40:43.619719Z", - "iopub.status.idle": "2026-08-10T22:40:43.628094Z", - "shell.execute_reply": "2026-08-10T22:40:43.627755Z" + "iopub.execute_input": "2026-11-01T18:47:46.088100Z", + "iopub.status.busy": "2026-11-01T18:47:46.088030Z", + "iopub.status.idle": "2026-11-01T18:47:46.096993Z", + "shell.execute_reply": "2026-11-01T18:47:46.096655Z" } }, "outputs": [ @@ -1001,10 +1002,10 @@ "id": "dd607a40", "metadata": { "execution": { - "iopub.execute_input": "2026-08-10T22:40:43.628874Z", - "iopub.status.busy": "2026-08-10T22:40:43.628821Z", - "iopub.status.idle": "2026-08-10T22:40:43.646530Z", - "shell.execute_reply": "2026-08-10T22:40:43.646212Z" + "iopub.execute_input": "2026-11-01T18:47:46.098109Z", + "iopub.status.busy": "2026-11-01T18:47:46.098037Z", + "iopub.status.idle": "2026-11-01T18:47:46.116890Z", + "shell.execute_reply": "2026-11-01T18:47:46.116528Z" } }, "outputs": [ @@ -1072,16 +1073,16 @@ "id": "372755dd", "metadata": { "execution": { - "iopub.execute_input": "2026-08-10T22:40:43.647374Z", - "iopub.status.busy": "2026-08-10T22:40:43.647306Z", - "iopub.status.idle": "2026-08-10T22:40:43.695243Z", - "shell.execute_reply": "2026-08-10T22:40:43.694952Z" + "iopub.execute_input": "2026-11-01T18:47:46.117996Z", + "iopub.status.busy": "2026-11-01T18:47:46.117925Z", + "iopub.status.idle": "2026-11-01T18:47:46.167142Z", + "shell.execute_reply": "2026-11-01T18:47:46.166796Z" } }, "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA94AAAJOCAYAAABBfN/cAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjExLjEsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvctoD+AAAAAlwSFlzAAAPYQAAD2EBqD+naQAAc9pJREFUeJzt3QecFOX9x/HfXi/cccBRBEFUFBVpUkR6FUGjJvbYNRoV/9FojIktatQYjSlGY+wl1tgbKE0UUBCQEnvFLu04uF73//o+yZ57xx2zc+5yt3uf94vl2Z2d2515dnZ2fk8NBIPBoAEAAAAAgJhIis3LAgAAAAAAAm8AAAAAAGKMGm8AAAAAAGKIwBsAAAAAgBgi8AYAAAAAIIYIvAEAAAAAiCECbwAAAAAAYojAGwAAAACAGCLwBgAAAAAghlJi+eIAkMjeeOMNe+655+yTTz6xiooK23nnnW3atGl20EEHWUpK/JxelyxZYpdffrndeuuttueee8b0vbZs2WJHHnnkdtcZOnSo/eEPf3D33377bfvrX/9q33zzje277752ww032JNPPmn//ve/raSkxE4++WQ76qijorJtej3lwbHHHmu9evXyXL+6utp9/nPnzrUvv/zS0tPTbdddd7XDDjvMRo8ebS3pz3/+s82aNctzvWOOOcaeeOIJe+mll6wlFRcX289+9jP3+Ybyvra21l544QW3bcrfpKQk99ykSZPskEMOqfuOVVZW2h133GHjx493x0hboc9uzJgxdu655273e91QWlqadezY0fbff3877bTTLCsrK+L3VD7ruHr66acbfRwtjX2m8+fPt5kzZ9qf/vSnqL4XAOwo8XNlCACthC4KL7nkEnv11Vft1FNPdYFfRkaGvfnmm3b11Ve7wPAf//iHZWZmWjwYOHCg3XXXXbbTTjvF/L1qamrsiy++cEHD6aef3ug64fn2m9/8xgUKCiSzs7NdAHbppZfa8ccfb8cdd5wLIKJl4cKFdvfdd9sRRxzhue66devs7LPPts2bN7uAcciQIVZeXm6LFy+2888/30aOHGnXX3+9r6Ammk455ZR6+6H8UwA7e/bseuvl5ubagQceaC3tuuuuc4U+4QUeysdVq1bZeeedZ/vss487dlavXu0KZRTs/fOf/7RAIGArV660v//97zZixAhrS7766isrKCho8vmysjL3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Nn/99deturragsGgW6b7mtN1l112saOOOira2wkAaCUCgYClpKS4FAAAADEIvL/++ms75JBDLDMz01JTU13AnZOTY1988YXr833FFVf4fUkAQBzJy8uzIUOGuBQAAAAxGNV88eLFrh/3woUL7ZprrrG+ffva7Nmz7dFHH7WKigrr37+/35cEAMSR2tpaV+iqFAAAADEIvDWq+dChQ11tt4LuNWvWuObm6vc9ZcoUW7Bggd+XBADEkQ0bNthLL73kUgAAAMQg8N5pp51s/fr17n63bt1c0P3JJ5+4x2p+/u233/p9SQBAHMnNzbXBgwe7FAAAADEIvIcPH+4GVnvmmWfc40mTJtmvf/1ru/nmm+2xxx6zvffe2+9LAgDiiApZNaaHUgAAAMQg8NY83g8//LB17NjRPb7sssts1113tTlz5thJJ51kEydO9PuSAIA4UlZWZl999ZVLAQAAEINRzd944w3btGmTjRo1yj3WqLY33XST35dBCyit+O/Ub+Fqa4JWXhWwsoqgJSXXfz4rnamCAGxr69attmrVKjeYZrt27cgiAACAaAfelZWVdtddd9nFF19s/fr1szFjxtjYsWPdBVhSku8KdOxAIy4saeKZXmZWvs3SNbdwQQ1gW126dLFp06a5FAAAADEIvMeNG+duhYWFtmzZMlu6dKn99re/dbXgo0ePtuOPP972228/vy8LAIgTgUDAkpOTXQoAAIAYBN4hamKu/tw777yz9e7d2x588EF74YUX3GMC79ZpyU3Z9R6refmES0rd/XnXpFt2ZrMPBwBtiApely9f7ma26NSpU0tvDgAAQKvnO9L66KOP7Pnnn7eVK1fa22+/7aYTGzJkiB1xxBE2cuRI22effWKzpfjBttdnOzMtQJ9uABHReb+2ttalAAAAiEHg/eqrr9rtt9/uaryPOeYYO+qoo2z33Xf3+zIAgDil2S00taRSAAAAxCDwPv300238+PH21ltvudtZZ51lFRUVNnToUFfzrec0vysAAAAAAGhG4K3BdPr06eNuRx99tFu2Zs0au/baa+3FF1+0b7/91n71q1+RtwCQoNatW+fG9FCNd/fu3Vt6cwAAABIv8Fafvk8++cRWrFhRd9OcrgcccID9/ve/twkTJsRmSwEArUJOTo4NGDDApQAAAIhB4H333XfbTTfd5Obt1vRhxx57rA0cONBNLSNbtmzx+5IAgDiSlZVlvXr1cikAAABiEHhPmzbNjjzySDe4Wkhpaalrdvjwww+7YJym5gCQuMrLy123Ik0lmZ1df5pCAAAARCHwDh84TVOLPfroo/bMM8+4C7Fx48bZ1KlT/b4kACCOqGWTuhlp+kgCbwAAgBgE3lVVVTZnzhxXu71s2TJLS0uzUaNGuf7dnTt39vtyAIA4o3P9gQceyDkfAAAg2oG3RrFVsP3EE09YdXW1HXTQQfbLX/6ybnA1gm4AaBuSkpJcoatSAAAARDHwfvbZZ13gfcEFF7g+3qmpqW65Am8AQNtRWFhoK1eutG7dulmnTp1aenMAAABavYirK8aOHeumDNN83UcddZTdeeedbnAdAEDbUltba2VlZS4FAABAFGu899prL7v55putoKDAnnvuOXvyySfdtGIdOnSwESNGWElJCYPsAEAb0LFjRxs5cqRLAQAA4M13Bz1daJ1yyin2/PPP27///W+bMmWKLVy40E0jdtFFF9m7777r9yUBAAAAAEhYP2hknAEDBtjVV19tixYtsiuvvNK+++47e+mll6K3dQCAVmf9+vU2a9YslwIAACAG04k1JiMjww477DB3q6ysjMZLAgBaKc3dre5HzOENAAAQmajPBaMpZgAAiUsB96677krgDQAAECEmYQUA+FJRUeGamSsFAACANwJvAIDvebzffPNNlwIAAMAbgTcAwJf8/HybNGmSSwEAAOCNwBsA4EtycrJlZma6FAAAAN4IvAEAvmzdutXWrFnjUgAAAHgj8AYA+FJVVWVbtmxxKQAAALwReAMAfOnUqZONGTPGpQAAAPCWYi1o2bJltmTJEgsEAjZy5Ejbb7/9WnJzAAAAAABInBrv2267zc477zyrra218vJy+9nPfmb33ntvS20OACBCGzZssNmzZ7sUAAAArbTGu6SkxG655Ra77777bNiwYW7ZXnvtZZdeeqmdeOKJlpLSohXxAIDtyMrKst12282lAAAAaKU13hUVFfbb3/7WBg0aVLesd+/erua7rKysJTYJABCh7Oxs69Onj0sBAADgrUWqljt27GgnnHBCvWXPPvus7b333paTk9Pk3wWDQXdrzeJhG0PCtzOetjsehfKWfCafE4EKTwsKClyq+bzjQei7F0/fQc4b5HGi4FgmnxMJxzN5HM7PNUWraNM9Z84ce+KJJ1zT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P+wc0fD4DuWxfgv0vWvN54iGvw2tsqm5MiJc6ETc8MsQ7qmnnqr349a3b1876KCDXIbdf//926x/3nnn1f1dw4uHqVOn2l577WWrV6+2BQsW1HuuV69e9uMf/9idWNUkvqEzzjjDnSRffPFF++yzz+o9N3r0aNt3331d7b0GjgunD/Hoo4929//1r39tc1I+9thj3QH02muv2XvvvVfvucGDB9sBBxxgX3/9tT377LP1ntOBc/LJJ7v7//73v7c54A877DB3sblkyRJbuXJl3fLqWn38P3f3lT/PP/N4vb/TZ3TWWWe5+3rPhl+WAw880Pr06WOrVq2y119/vd5zvXv3tunTp7sD+oEHHtgmD3/2s5+5H6CXX37Zvvzyy3rPjRkzxvr37+9aRsybN2+bL5HGBJDGXvf44493P16vvPKKffTRR/WeU6sKtbL44osv7IUXXtjmC3jCCSe4+48++ug2P7Y/+clP3A+f9lPHTDh93mPHjnUnjMcff9w+Wn+4BYM9LNK+xwVbq23FihW2dOnSes/tvvvu7jjVyaaxff35z3/uvjczZ850J/9w48ePt3322cfefffdbY7v7t272+GHH+6+a4297kknneROcsr7Tz75pN5z+++/vw0ZMsQd97Nmzar3nH6YjjvuOHf/4Ycf3uZEdNRRR7mT1aJFi+ztt9+u99zAgQNt1KhR7jjU97Xhj57GgpAnnnhimwvcQw45xJ75bS+7/enP7LGVu7llPdI/s15Zn9iE/gEbuuskW7duiz300EPb7Os555zjUh0PDS+OJ02a5M4x//nPf2zhwoX1nuvZs6f96Ec/cifyxvLw1FNPdT80c+bMsbVr19Z7buTIkTZo0CD7+OOPW/054v2iAfpGuvu7dtv+iOW7dv3++VcWLrdvV61plecIHd86VpX3+vuGx/eIESPs888/t2eeeabeczqvnHLKKXWv2zCo0eemiwd9NuF5KAMGDHDdqnRh8Mgjj9R7Tvt49tlnu/uPPfbYNhfzOm/pmNc5omEeKm8PPvhgdzFyzz33bLOvM2bMcBcH2hcdFw2Pb5279F1smIc6Fo488kh3AdTYb6C+jwo+dA7Qcdzw+B42bJj7DXz++efrPadj98QTT3T377vvvm0uhHT+UOCi8/eaNWu2Ob51ntVFkI7hcPqunXnmme6+8rdhQYLOd7vssos7vhue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IPXRVA24RuuneW7s6DeP373oUy3sJ5984m4aaTvcK6+8kjDz8rY05aMCbZ03FLCob+wvf/lLN7UmYkfnDF0zI7rOP/98l7dnn32262al7oI6vhPlNzAQZLQcAAAAAABihqbmAAAAAADEEIE3AAAAAAAxROANAAAAAEAMEXgDAAAAABBDBN4AAAAAAMQQgTcAAAAAADFE4A0AAAAAQAwReAMAEGXvv/++lZSUJMz7AACAH4bAGwCwXXfccYdNmDCh3rLNmzfb3nvvbYcffni95R9++KH17dvXPvnkk1aRq++9957bnvBbv379bOzYsXb99ddbTU1NRK/z+OOP20EHHRTRuuvWrbOf/OQnVlpa6h6/+OKLNnHixB+0H5G8T3MtX77czjrrLNt///1t8ODBdvTRR9vs2bOttYpVfsrSpUvdMVJQUGCtzbPPPhuz/QYAxB6BNwBgu0aOHGnffPONffvtt3XLFi9ebLvuuqt98MEHtn79+rrly5Yts5122sl23333VpWrCiS1rbq99dZbdtVVV9mjjz5qDzzwQNTfq6qqql5Af/DBB9v8+fNj/j7N8dRTT9kZZ5zhCiJmzZplCxcutOOOO85+9atfufxpjWKVnwAAxBKBNwBgu/bZZx/Ly8uzFStW1C1TgKYa4L322ssWLVpUr/Z09OjRVltba7fddpv96Ec/soEDB9qwYcPsggsusLKyMquurrYRI0a4GrxwkyZNcjXLodc54ogjbMCAATZ9+nR7+umn69arrKy0yy67zL2Gbj/72c981bCnp6e7Gny97pw5c+pte1PvGW57+1ZRUeH2Q5QPL730Ul0N7Q/d73CNvY+88cYbdtRRR7ntmjx5ct3rNka1uiqA+N3vfmc//elPrWPHjtauXTv78Y9/bOeee67961//coH99vZXtD8nnHCC/eUvf7GhQ4e6fVSBxty5c9027LfffnbppZdaMBisW//YY4+1P/zhD66GXUG/3iuS/G2sxvvll1+2qVOn2pAhQ+wXv/iFXXTRRXbttdfWey/tp15H23fNNdc0u8DCa9saaxlxyimn2N///veIt2fJkiV25JFHumNgypQp9thjj9U9p/Vuvvlml2fa39///vdumwAArR+BNwBg+z8USUmuGbJqikUBlGq8FfCNGjXKXnvttbp1FThq2XPPPecCBgVjqgVXsPLKK6+4GtaU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", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA94AAAJOCAYAAABBfN/cAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjExLjIsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvgI3uAAAAAAlwSFlzAAAPYQAAD2EBqD+naQAAc9pJREFUeJzt3QecFOX9x/HfXi/cccBRBEFUFBVpUkR6FUGjJvbYNRoV/9FojIktatQYjSlGY+wl1tgbKE0UUBCQEnvFLu04uF73//o+yZ57xx2zc+5yt3uf94vl2Z2d2515dnZ2fk8NBIPBoAEAAAAAgJhIis3LAgAAAAAAAm8AAAAAAGKMGm8AAAAAAGKIwBsAAAAAgBgi8AYAAAAAIIYIvAEAAAAAiCECbwAAAAAAYojAGwAAAACAGCLwBgAAAAAghlJi+eIAkMjeeOMNe+655+yTTz6xiooK23nnnW3atGl20EEHWUpK/JxelyxZYpdffrndeuuttueee8b0vbZs2WJHHnnkdtcZOnSo/eEPf3D33377bfvrX/9q33zzje277752ww032JNPPmn//ve/raSkxE4++WQ76qijorJtej3lwbHHHmu9evXyXL+6utp9/nPnzrUvv/zS0tPTbdddd7XDDjvMRo8ebS3pz3/+s82aNctzvWOOOcaeeOIJe+mll6wlFRcX289+9jP3+Ybyvra21l544QW3bcrfpKQk99ykSZPskEMOqfuOVVZW2h133GHjx493x0hboc9uzJgxdu655273e91QWlqadezY0fbff3877bTTLCsrK+L3VD7ruHr66acbfRwtjX2m8+fPt5kzZ9qf/vSnqL4XAOwo8XNlCACthC4KL7nkEnv11Vft1FNPdYFfRkaGvfnmm3b11Ve7wPAf//iHZWZmWjwYOHCg3XXXXbbTTjvF/L1qamrsiy++cEHD6aef3ug64fn2m9/8xgUKCiSzs7NdAHbppZfa8ccfb8cdd5wLIKJl4cKFdvfdd9sRRxzhue66devs7LPPts2bN7uAcciQIVZeXm6LFy+2888/30aOHGnXX3+9r6Ammk455ZR6+6H8UwA7e/bseuvl5ubagQceaC3tuuuuc4U+4QUeysdVq1bZeeedZ/vss487dlavXu0KZRTs/fOf/7RAIGArV660v//97zZixAhrS7766isrKCho8vmysjL3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Nn/99deturragsGgW6b7mtN1l112saOOOira2wkAaCUCgYClpKS4FAAAADEIvL/++ms75JBDLDMz01JTU13AnZOTY1988YXr833FFVf4fUkAQBzJy8uzIUOGuBQAAAAxGNV88eLFrh/3woUL7ZprrrG+ffva7Nmz7dFHH7WKigrr37+/35cEAMSR2tpaV+iqFAAAADEIvDWq+dChQ11tt4LuNWvWuObm6vc9ZcoUW7Bggd+XBADEkQ0bNthLL73kUgAAAMQg8N5pp51s/fr17n63bt1c0P3JJ5+4x2p+/u233/p9SQBAHMnNzbXBgwe7FAAAADEIvIcPH+4GVnvmmWfc40mTJtmvf/1ru/nmm+2xxx6zvffe2+9LAgDiiApZNaaHUgAAAMQg8NY83g8//LB17NjRPb7sssts1113tTlz5thJJ51kEydO9PuSAIA4UlZWZl999ZVLAQAAEINRzd944w3btGmTjRo1yj3WqLY33XST35dBCyit+O/Ub+Fqa4JWXhWwsoqgJSXXfz4rnamCAGxr69attmrVKjeYZrt27cgiAACAaAfelZWVdtddd9nFF19s/fr1szFjxtjYsWPdBVhSku8KdOxAIy4saeKZXmZWvs3SNbdwQQ1gW126dLFp06a5FAAAADEIvMeNG+duhYWFtmzZMlu6dKn99re/dbXgo0ePtuOPP972228/vy8LAIgTgUDAkpOTXQoAAIAYBN4hamKu/tw777yz9e7d2x588EF74YUX3GMC79ZpyU3Z9R6refmES0rd/XnXpFt2ZrMPBwBtiApely9f7ma26NSpU0tvDgAAQKvnO9L66KOP7Pnnn7eVK1fa22+/7aYTGzJkiB1xxBE2cuRI22effWKzpfjBttdnOzMtQJ9uABHReb+2ttalAAAAiEHg/eqrr9rtt9/uaryPOeYYO+qoo2z33Xf3+zIAgDil2S00taRSAAAAxCDwPv300238+PH21ltvudtZZ51lFRUVNnToUFfzrec0vysAAAAAAGhG4K3BdPr06eNuRx99tFu2Zs0au/baa+3FF1+0b7/91n71q1+RtwCQoNatW+fG9FCNd/fu3Vt6cwAAABIv8Fafvk8++cRWrFhRd9OcrgcccID9/ve/twkTJsRmSwEArUJOTo4NGDDApQAAAIhB4H333XfbTTfd5Obt1vRhxx57rA0cONBNLSNbtmzx+5IAgDiSlZVlvXr1cikAAABiEHhPmzbNjjzySDe4Wkhpaalrdvjwww+7YJym5gCQuMrLy123Ik0lmZ1df5pCAAAARCHwDh84TVOLPfroo/bMM8+4C7Fx48bZ1KlT/b4kACCOqGWTuhlp+kgCbwAAgBgE3lVVVTZnzhxXu71s2TJLS0uzUaNGuf7dnTt39vtyAIA4o3P9gQceyDkfAAAg2oG3RrFVsP3EE09YdXW1HXTQQfbLX/6ybnA1gm4AaBuSkpJcoatSAAAARDHwfvbZZ13gfcEFF7g+3qmpqW65Am8AQNtRWFhoK1eutG7dulmnTp1aenMAAABavYirK8aOHeumDNN83UcddZTdeeedbnAdAEDbUltba2VlZS4FAABAFGu899prL7v55putoKDAnnvuOXvyySfdtGIdOnSwESNGWElJCYPsAEAb0LFjRxs5cqRLAQAA4M13Bz1daJ1yyin2/PPP27///W+bMmWKLVy40E0jdtFFF9m7777r9yUBAAAAAEhYP2hknAEDBtjVV19tixYtsiuvvNK+++47e+mll6K3dQCAVmf9+vU2a9YslwIAACAG04k1JiMjww477DB3q6ysjMZLAgBaKc3dre5HzOENAAAQmajPBaMpZgAAiUsB96677krgDQAAECEmYQUA+FJRUeGamSsFAACANwJvAIDvebzffPNNlwIAAMAbgTcAwJf8/HybNGmSSwEAAOCNwBsA4EtycrJlZma6FAAAAN4IvAEAvmzdutXWrFnjUgAAAHgj8AYA+FJVVWVbtmxxKQAAALwReAMAfOnUqZONGTPGpQAAAPCWYi1o2bJltmTJEgsEAjZy5Ejbb7/9WnJzAAAAAABInBrv2267zc477zyrra218vJy+9nPfmb33ntvS20OACBCGzZssNmzZ7sUAAAArbTGu6SkxG655Ra77777bNiwYW7ZXnvtZZdeeqmdeOKJlpLSohXxAIDtyMrKst12282lAAAAaKU13hUVFfbb3/7WBg0aVLesd+/erua7rKysJTYJABCh7Oxs69Onj0sBAADgrUWqljt27GgnnHBCvWXPPvus7b333paTk9Pk3wWDQXdrzeJhG0PCtzOetjsehfKWfCafE4EKTwsKClyq+bzjQei7F0/fQc4b5HGi4FgmnxMJxzN5HM7PNUWraNM9Z84ce+KJJ1zT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P+wc0fD4DuWxfgv0vWvN54iGvw2tsqm5MiJc6ETc8MsQ7qmnnqr349a3b1876KCDXIbdf//926x/3nnn1f1dw4uHqVOn2l577WWrV6+2BQsW1HuuV69e9uMf/9idWNUkvqEzzjjDnSRffPFF++yzz+o9N3r0aNt3331d7b0GjgunD/Hoo4929//1r39tc1I+9thj3QH02muv2XvvvVfvucGDB9sBBxxgX3/9tT377LP1ntOBc/LJJ7v7//73v7c54A877DB3sblkyRJbuXJl3fLqWn38P3f3lT/PP/N4vb/TZ3TWWWe5+3rPhl+WAw880Pr06WOrVq2y119/vd5zvXv3tunTp7sD+oEHHtgmD3/2s5+5H6CXX37Zvvzyy3rPjRkzxvr37+9aRsybN2+bL5HGBJDGXvf44493P16vvPKKffTRR/WeU6sKtbL44osv7IUXXtjmC3jCCSe4+48++ug2P7Y/+clP3A+f9lPHTDh93mPHjnUnjMcff9w+Wn+4BYM9LNK+xwVbq23FihW2dOnSes/tvvvu7jjVyaaxff35z3/uvjczZ850J/9w48ePt3322cfefffdbY7v7t272+GHH+6+a4297kknneROcsr7Tz75pN5z+++/vw0ZMsQd97Nmzar3nH6YjjvuOHf/4Ycf3uZEdNRRR7mT1aJFi+ztt9+u99zAgQNt1KhR7jjU97Xhj57GgpAnnnhimwvcQw45xJ75bS+7/enP7LGVu7llPdI/s15Zn9iE/gEbuuskW7duiz300EPb7Os555zjUh0PDS+OJ02a5M4x//nPf2zhwoX1nuvZs6f96Ec/cifyxvLw1FNPdT80c+bMsbVr19Z7buTIkTZo0CD7+OOPW/054v2iAfpGuvu7dtv+iOW7dv3++VcWLrdvV61plecIHd86VpX3+vuGx/eIESPs888/t2eeeabeczqvnHLKKXWv2zCo0eemiwd9NuF5KAMGDHDdqnRh8Mgjj9R7Tvt49tlnu/uPPfbYNhfzOm/pmNc5omEeKm8PPvhgdzFyzz33bLOvM2bMcBcH2hcdFw2Pb5279F1smIc6Fo488kh3AdTYb6C+jwo+dA7Qcdzw+B42bJj7DXz++efrPadj98QTT3T377vvvm0uhHT+UOCi8/eaNWu2Ob51ntVFkI7hcPqunXnmme6+8rdhQYLOd7vssos7vhue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IPXRVA24RuuneW7s6DeP373oUy3sJ5984m4aaTvcK6+8kjDz8rY05aMCbZ03FLCob+wvf/lLN7UmYkfnDF0zI7rOP/98l7dnn32262al7oI6vhPlNzAQZLQcAAAAAABihqbmAAAAAADEEIE3AAAAAAAxROANAAAAAEAMEXgDAAAAABBDBN4AAAAAAMQQgTcAAAAAADFE4A0AAAAAQAwReAMAEGXvv/++lZSUJMz7AACAH4bAGwCwXXfccYdNmDCh3rLNmzfb3nvvbYcffni95R9++KH17dvXPvnkk1aRq++9957bnvBbv379bOzYsXb99ddbTU1NRK/z+OOP20EHHRTRuuvWrbOf/OQnVlpa6h6/+OKLNnHixB+0H5G8T3MtX77czjrrLNt///1t8ODBdvTRR9vs2bOttYpVfsrSpUvdMVJQUGCtzbPPPhuz/QYAxB6BNwBgu0aOHGnffPONffvtt3XLFi9ebLvuuqt98MEHtn79+rrly5Yts5122sl23333VpWrCiS1rbq99dZbdtVVV9mjjz5qDzzwQNTfq6qqql5Af/DBB9v8+fNj/j7N8dRTT9kZZ5zhCiJmzZplCxcutOOOO85+9atfufxpjWKVnwAAxBKBNwBgu/bZZx/Ly8uzFStW1C1TgKYa4L322ssWLVpUr/Z09OjRVltba7fddpv96Ec/soEDB9qwYcPsggsusLKyMquurrYRI0a4GrxwkyZNcjXLodc54ogjbMCAATZ9+nR7+umn69arrKy0yy67zL2Gbj/72c981bCnp6e7Gny97pw5c+pte1PvGW57+1ZRUeH2Q5QPL730Ul0N7Q/d73CNvY+88cYbdtRRR7ntmjx5ct3rNka1uiqA+N3vfmc//elPrWPHjtauXTv78Y9/bOeee67961//coH99vZXtD8nnHCC/eUvf7GhQ4e6fVSBxty5c9027LfffnbppZdaMBisW//YY4+1P/zhD66GXUG/3iuS/G2sxvvll1+2qVOn2pAhQ+wXv/iFXXTRRXbttdfWey/tp15H23fNNdc0u8DCa9saaxlxyimn2N///veIt2fJkiV25JFHumNgypQp9thjj9U9p/Vuvvlml2fa39///vdumwAArR+BNwBg+z8USUmuGbJqikUBlGq8FfCNGjXKXnvttbp1FThq2XPPPecCBgVjqgVXsPLKK6+4GtaU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", "text/plain": [ "
" ] @@ -1141,10 +1142,10 @@ "id": "0d7c096e", "metadata": { "execution": { - "iopub.execute_input": "2026-08-10T22:40:43.696229Z", - "iopub.status.busy": "2026-08-10T22:40:43.696153Z", - "iopub.status.idle": "2026-08-10T22:40:43.702419Z", - "shell.execute_reply": "2026-08-10T22:40:43.702068Z" + "iopub.execute_input": "2026-11-01T18:47:46.168433Z", + "iopub.status.busy": "2026-11-01T18:47:46.168354Z", + "iopub.status.idle": "2026-11-01T18:47:46.172252Z", + "shell.execute_reply": "2026-11-01T18:47:46.171864Z" } }, "outputs": [ @@ -1218,10 +1219,10 @@ "id": "2fa7cc34", "metadata": { "execution": { - "iopub.execute_input": "2026-08-10T22:40:43.703466Z", - "iopub.status.busy": "2026-08-10T22:40:43.703406Z", - "iopub.status.idle": "2026-08-10T22:40:43.706530Z", - "shell.execute_reply": "2026-08-10T22:40:43.706188Z" + "iopub.execute_input": "2026-11-01T18:47:46.173334Z", + "iopub.status.busy": "2026-11-01T18:47:46.173268Z", + "iopub.status.idle": "2026-11-01T18:47:46.176328Z", + "shell.execute_reply": "2026-11-01T18:47:46.176026Z" } }, "outputs": [ diff --git a/docs/v4-deprecations.yaml b/docs/v4-deprecations.yaml index 974f8b958..f972f16ec 100644 --- a/docs/v4-deprecations.yaml +++ b/docs/v4-deprecations.yaml @@ -1,5 +1,7 @@ # v4.0 deprecation ledger - the single source of truth for every API-lifecycle -# fact in the 4.0 program (3.9 shims -> 4.0 removals -> 5.0 alias retirement). +# fact in the 4.0 program (3.9 shims -> 4.0 removals; the +# 5.0 alias-retirement window was retired at item 9b, 2026-11-01 - every shim +# scheduled for it was removed AT 4.0). # # NORMATIVE SCHEMA: docs/v4-design.md section 11 (field meanings, kind and # status vocabularies, locator grammar, lifecycle rules). Enforced by @@ -226,14 +228,13 @@ rows: group: merge-mpd old: "diff_diff:EventStudyResults.period_effects" new: null - deprecated_in: "4.0" - removed_in: "5.0" - status: shimmed + deprecated_in: null + removed_in: "4.0" + status: removed phase: 5 - warning: FutureWarning test_ref: tests/test_v4_merge_mpd.py code_refs: [diff_diff/results_base.py, diff_diff/guides/llms-full.txt] - notes: "SHIMMED at the 4.0 cut (PR 4c-ii, 2026-10-25): locator migrated from MultiPeriodDiDResults.period_effects with [M-011]'s flip (cross-row rule, spec section 11). @property on EventStudyResults (results_base.py; a plain descriptor, never a dataclass field) emitting FutureWarning(stacklevel=2) naming 5.0 and to_dataframe(); returns {calendar label: results_base._PeriodEffectView} over the non-reference rows (reference omitted like the old dict; duplicate labels raise); is_significant = p_value < alpha (NaN -> False; the removed record hard-coded 0.05 - user decision 2026-10-25); stars via a leaf-local helper (results_base imports no diff_diff module, so the view is a leaf-local computed property, not a deprecated_field_property rename-forwarder). GATED to time_scale='calendar' surfaces: on a relative-time surface the property raises AttributeError before warning, so hasattr is False and nothing warns. build_event_study_surface opens with an isinstance(EventStudyResults) passthrough so no duck sniff reaches the property. Reader inventory after the deletions: results_base.py (the view) and guides/llms-full.txt (its documentation). Original: Period-keyed dict becomes a FutureWarning property VIEW over the unified event-study surface on the successor container at 4.0; removed 5.0. Migrate this locator to the successor class when [M-011] flips (cross-row rule, spec section 11). Reader inventory completed at the 3.9 bump, when the rule-11 Duty C window armed for 4.0-scheduled rows." + notes: "REMOVED at the 4.0 cut (item 9b, 2026-11-01): UNWARNED BREAK of a released 3.x name (user decision 2026-11-01 - no 4.x debt): the name was native/documented through v3.13.0 and no release carried the FutureWarning shim (it landed on main after the v3.13.0 tag), so the 4.0->5.0 window was retired and the property deleted outright; the EventStudyResults.period_effects view, _PeriodEffectView and the leaf-local stars helper are gone from results_base.py; to_dataframe() (one row per event_time, reference row included) and the columnar arrays are the successor. Pre-release main (2026-10-25/26): SHIMMED at the 4.0 cut (PR 4c-ii, 2026-10-25): locator migrated from MultiPeriodDiDResults.period_effects with [M-011]'s flip (cross-row rule, spec section 11). @property on EventStudyResults (results_base.py; a plain descriptor, never a dataclass field) emitting FutureWarning(stacklevel=2) naming 5.0 and to_dataframe(); returns {calendar label: results_base._PeriodEffectView} over the non-reference rows (reference omitted like the old dict; duplicate labels raise); is_significant = p_value < alpha (NaN -> False; the removed record hard-coded 0.05 - user decision 2026-10-25); stars via a leaf-local helper (results_base imports no diff_diff module, so the view is a leaf-local computed property, not a deprecated_field_property rename-forwarder). GATED to time_scale='calendar' surfaces: on a relative-time surface the property raises AttributeError before warning, so hasattr is False and nothing warns. build_event_study_surface opens with an isinstance(EventStudyResults) passthrough so no duck sniff reaches the property. Reader inventory after the deletions: results_base.py (the view) and guides/llms-full.txt (its documentation). Original: Period-keyed dict becomes a FutureWarning property VIEW over the unified event-study surface on the successor container at 4.0; removed 5.0. Migrate this locator to the successor class when [M-011] flips (cross-row rule, spec section 11). Reader inventory completed at the 3.9 bump, when the rule-11 Duty C window armed for 4.0-scheduled rows." # ---- fit(aggregate=) -> results.aggregate(type=) ------------------------- - id: M-020 @@ -640,126 +641,117 @@ rows: old: "diff_diff:CallawaySantAnnaResults.overall_att" new: "diff_diff:CallawaySantAnnaResults.att" introduced_in: "4.0" - deprecated_in: "4.0" - removed_in: "5.0" - status: shimmed + deprecated_in: null + removed_in: "4.0" + status: removed phase: 5 - warning: FutureWarning test_ref: tests/test_v4_storage_flip.py code_refs: [diff_diff/staggered_results.py, diff_diff/staggered.py, diff_diff/staggered_aggregation.py, diff_diff/power.py, diff_diff/practitioner.py, diff_diff/efficient_did.py, diff_diff/wooldridge.py, diff_diff/_reporting_helpers.py, diff_diff/diagnostic_report.py, diff_diff/prep_dgp.py, diff_diff/_staggered_triple_diff_engine.py, diff_diff/triple_diff_results.py, diff_diff/dml_did.py, diff_diff/dml_did_results.py, diff_diff/_deprecation.py, diff_diff/guides/llms-autonomous.txt, diff_diff/guides/llms-full.txt, diff_diff/guides/llms-practitioner.txt, docs/methodology/REGISTRY.md, docs/methodology/REPORTING.md] - notes: "SHIMMED at the 4.0 cut (PR 5b, 2026-10-26): the canonical quintet att/se/t_stat/p_value/conf_int are the native dataclass fields, renamed IN SLOT (the generated __init__ positional layout is public API and unchanged; constructors take canonical keywords and overall_att= raises TypeError). The five overall_* names are read-only deprecated_field_property shims (FutureWarning, stacklevel 2, 'Will be removed in 5.0'; plain class attributes, never dataclass fields, absent from fields()/asdict()/to_dict()). DMLDiDResults inherits the storage and RE-DECLARES the five shims so the warning names the subclass (tests/test_naming_guard.py SURFACE_ALLOWLIST['DMLDiDResults.overall_att'] records it - no independent row). Pickle migration: __setstate__ on every flipped container rewrites the legacy keys through _deprecation.migrate_renamed_state_keys(state, LEGACY_OVERALL_QUINTET) (an existing canonical key wins; the legacy key never lingers in __dict__; spec section 5). Report surface: _reporting_helpers headline_attribute is 'att' for the nine containers and diagnostic_report._extract_scalar_headline's legacy overall_*/overall_att_* alias row was deleted (DR schema 2.0 -> 3.0, BR 3.0 -> 4.0; REPORTING.md Note). StaggeredTripleDiffResults is UNFLIPPED (storage, to_dict keys and headline_attribute stay overall_*; it dies with the item-6 container unification, whose successor is born canonical). The bootstrap draw containers (CSBootstrapResults.overall_att_se etc., DCDHBootstrapResults.overall_se etc.) keep their payload names (user decision 2026-10-26; REGISTRY.md Result-class field naming Note). Reader inventory after PR 5a/5a-ii/5b: no in-repo reader of the overall_* names remains on the nine containers (source, tests, docs, notebooks, guides, benchmarks all read the canonical quintet); the only quoted literals are the shim declarations, the LEGACY_OVERALL_QUINTET state map and the staggered-DDD branch of _reporting_helpers. diff_diff/_deprecation.py is 5.0-WINDOW INFRASTRUCTURE (added 2026-10-18): these rows' FutureWarning properties ride its deprecated_field_property helper and their __setstate__ bodies ride migrate_renamed_state_keys through the 4.0 -> 5.0 window ([M-016]'s view is a leaf-local computed property in results_base.py instead), so the 5.0 removal PR deletes these shims and the LEGACY_OVERALL_QUINTET state map but never the module - and must add CONSUMER_ALLOWLIST[('overall_att', 'diff_diff/_deprecation.py')] if it keeps the migration literal for still-older pickles. This row carries the token family's CROSS-CUTTING consumer inventory (reports, guides, methodology docs, DGP helpers, the un-rowed staggered-DDD engine/container) alongside its own producers; per-class producer files live on their own rows. Inventory completed at the 3.9 bump, when the rule-11 Duty C window armed for 4.0-scheduled rows." + notes: "REMOVED at the 4.0 cut (item 9b, 2026-11-01): UNWARNED BREAK of a released 3.x name (user decision 2026-11-01 - no 4.x debt): the name was native/documented through v3.13.0 and no release carried the FutureWarning shim (it landed on main after the v3.13.0 tag), so the 4.0->5.0 window was retired and the property deleted outright; the five overall_* FutureWarning properties are gone (AttributeError); the canonical quintet att/se/t_stat/p_value/conf_int is the only vocabulary; __setstate__ legacy-key migration via _deprecation.migrate_renamed_state_keys(LEGACY_OVERALL_QUINTET) kept so 3.x pickles still load. DMLDiDResults' re-declared shims gone with the parent's (the SURFACE_ALLOWLIST entry was deleted). deprecated_field_property itself was deleted from _deprecation.py (no consumer left); the module survives for the pickle migration. Pre-release main (2026-10-25/26): SHIMMED at the 4.0 cut (PR 5b, 2026-10-26): the canonical quintet att/se/t_stat/p_value/conf_int are the native dataclass fields, renamed IN SLOT (the generated __init__ positional layout is public API and unchanged; constructors take canonical keywords and overall_att= raises TypeError). The five overall_* names are read-only deprecated_field_property shims (FutureWarning, stacklevel 2, 'Will be removed in 5.0'; plain class attributes, never dataclass fields, absent from fields()/asdict()/to_dict()). DMLDiDResults inherits the storage and RE-DECLARES the five shims so the warning names the subclass (tests/test_naming_guard.py SURFACE_ALLOWLIST['DMLDiDResults.overall_att'] records it - no independent row). Pickle migration: __setstate__ on every flipped container rewrites the legacy keys through _deprecation.migrate_renamed_state_keys(state, LEGACY_OVERALL_QUINTET) (an existing canonical key wins; the legacy key never lingers in __dict__; spec section 5). Report surface: _reporting_helpers headline_attribute is 'att' for the nine containers and diagnostic_report._extract_scalar_headline's legacy overall_*/overall_att_* alias row was deleted (DR schema 2.0 -> 3.0, BR 3.0 -> 4.0; REPORTING.md Note). StaggeredTripleDiffResults is UNFLIPPED (storage, to_dict keys and headline_attribute stay overall_*; it dies with the item-6 container unification, whose successor is born canonical). The bootstrap draw containers (CSBootstrapResults.overall_att_se etc., DCDHBootstrapResults.overall_se etc.) keep their payload names (user decision 2026-10-26; REGISTRY.md Result-class field naming Note). Reader inventory after PR 5a/5a-ii/5b: no in-repo reader of the overall_* names remains on the nine containers (source, tests, docs, notebooks, guides, benchmarks all read the canonical quintet); the only quoted literals are the shim declarations, the LEGACY_OVERALL_QUINTET state map and the staggered-DDD branch of _reporting_helpers. diff_diff/_deprecation.py is 5.0-WINDOW INFRASTRUCTURE (added 2026-10-18): these rows' FutureWarning properties ride its deprecated_field_property helper and their __setstate__ bodies ride migrate_renamed_state_keys through the 4.0 -> 5.0 window ([M-016]'s view is a leaf-local computed property in results_base.py instead), so the 5.0 removal PR deletes these shims and the LEGACY_OVERALL_QUINTET state map but never the module - and must add CONSUMER_ALLOWLIST[('overall_att', 'diff_diff/_deprecation.py')] if it keeps the migration literal for still-older pickles. This row carries the token family's CROSS-CUTTING consumer inventory (reports, guides, methodology docs, DGP helpers, the un-rowed staggered-DDD engine/container) alongside its own producers; per-class producer files live on their own rows. Inventory completed at the 3.9 bump, when the rule-11 Duty C window armed for 4.0-scheduled rows." - id: M-051 kind: field group: field-flip old: "diff_diff:SunAbrahamResults.overall_att" new: "diff_diff:SunAbrahamResults.att" introduced_in: "4.0" - deprecated_in: "4.0" - removed_in: "5.0" - status: shimmed + deprecated_in: null + removed_in: "4.0" + status: removed phase: 5 - warning: FutureWarning test_ref: tests/test_v4_storage_flip.py code_refs: [diff_diff/sun_abraham.py] - notes: "SHIMMED at the 4.0 cut (PR 5b, 2026-10-26): SunAbrahamResults stores the canonical quintet natively (fields renamed in slot); overall_att/overall_se/overall_t_stat/overall_p_value/overall_conf_int are read-only FutureWarning deprecated_field_property shims (removed 5.0); __setstate__ migrates 3.x pickles via _deprecation.migrate_renamed_state_keys. Mechanics and the cross-cutting inventory on [M-050]." + notes: "REMOVED at the 4.0 cut (item 9b, 2026-11-01): UNWARNED BREAK of a released 3.x name (user decision 2026-11-01 - no 4.x debt): the name was native/documented through v3.13.0 and no release carried the FutureWarning shim (it landed on main after the v3.13.0 tag), so the 4.0->5.0 window was retired and the property deleted outright; the five overall_* FutureWarning properties are gone (AttributeError); the canonical quintet att/se/t_stat/p_value/conf_int is the only vocabulary; __setstate__ legacy-key migration via _deprecation.migrate_renamed_state_keys(LEGACY_OVERALL_QUINTET) kept so 3.x pickles still load. Pre-release main (2026-10-25/26): SHIMMED at the 4.0 cut (PR 5b, 2026-10-26): SunAbrahamResults stores the canonical quintet natively (fields renamed in slot); overall_att/overall_se/overall_t_stat/overall_p_value/overall_conf_int are read-only FutureWarning deprecated_field_property shims (removed 5.0); __setstate__ migrates 3.x pickles via _deprecation.migrate_renamed_state_keys. Mechanics and the cross-cutting inventory on [M-050]." - id: M-052 kind: field group: field-flip old: "diff_diff:ImputationDiDResults.overall_att" new: "diff_diff:ImputationDiDResults.att" introduced_in: "4.0" - deprecated_in: "4.0" - removed_in: "5.0" - status: shimmed + deprecated_in: null + removed_in: "4.0" + status: removed phase: 5 - warning: FutureWarning test_ref: tests/test_v4_storage_flip.py code_refs: [diff_diff/imputation_results.py, diff_diff/imputation.py] - notes: "SHIMMED at the 4.0 cut (PR 5b, 2026-10-26): ImputationDiDResults stores the canonical quintet natively (fields renamed in slot); overall_att/overall_se/overall_t_stat/overall_p_value/overall_conf_int are read-only FutureWarning deprecated_field_property shims (removed 5.0); __setstate__ migrates 3.x pickles via _deprecation.migrate_renamed_state_keys. Mechanics and the cross-cutting inventory on [M-050]." + notes: "REMOVED at the 4.0 cut (item 9b, 2026-11-01): UNWARNED BREAK of a released 3.x name (user decision 2026-11-01 - no 4.x debt): the name was native/documented through v3.13.0 and no release carried the FutureWarning shim (it landed on main after the v3.13.0 tag), so the 4.0->5.0 window was retired and the property deleted outright; the five overall_* FutureWarning properties are gone (AttributeError); the canonical quintet att/se/t_stat/p_value/conf_int is the only vocabulary; __setstate__ legacy-key migration via _deprecation.migrate_renamed_state_keys(LEGACY_OVERALL_QUINTET) kept so 3.x pickles still load. Pre-release main (2026-10-25/26): SHIMMED at the 4.0 cut (PR 5b, 2026-10-26): ImputationDiDResults stores the canonical quintet natively (fields renamed in slot); overall_att/overall_se/overall_t_stat/overall_p_value/overall_conf_int are read-only FutureWarning deprecated_field_property shims (removed 5.0); __setstate__ migrates 3.x pickles via _deprecation.migrate_renamed_state_keys. Mechanics and the cross-cutting inventory on [M-050]." - id: M-053 kind: field group: field-flip old: "diff_diff:TwoStageDiDResults.overall_att" new: "diff_diff:TwoStageDiDResults.att" introduced_in: "4.0" - deprecated_in: "4.0" - removed_in: "5.0" - status: shimmed + deprecated_in: null + removed_in: "4.0" + status: removed phase: 5 - warning: FutureWarning test_ref: tests/test_v4_storage_flip.py code_refs: [diff_diff/two_stage_results.py, diff_diff/two_stage.py] - notes: "SHIMMED at the 4.0 cut (PR 5b, 2026-10-26): TwoStageDiDResults stores the canonical quintet natively (fields renamed in slot); overall_att/overall_se/overall_t_stat/overall_p_value/overall_conf_int are read-only FutureWarning deprecated_field_property shims (removed 5.0); __setstate__ migrates 3.x pickles via _deprecation.migrate_renamed_state_keys. Mechanics and the cross-cutting inventory on [M-050]." + notes: "REMOVED at the 4.0 cut (item 9b, 2026-11-01): UNWARNED BREAK of a released 3.x name (user decision 2026-11-01 - no 4.x debt): the name was native/documented through v3.13.0 and no release carried the FutureWarning shim (it landed on main after the v3.13.0 tag), so the 4.0->5.0 window was retired and the property deleted outright; the five overall_* FutureWarning properties are gone (AttributeError); the canonical quintet att/se/t_stat/p_value/conf_int is the only vocabulary; __setstate__ legacy-key migration via _deprecation.migrate_renamed_state_keys(LEGACY_OVERALL_QUINTET) kept so 3.x pickles still load. Pre-release main (2026-10-25/26): SHIMMED at the 4.0 cut (PR 5b, 2026-10-26): TwoStageDiDResults stores the canonical quintet natively (fields renamed in slot); overall_att/overall_se/overall_t_stat/overall_p_value/overall_conf_int are read-only FutureWarning deprecated_field_property shims (removed 5.0); __setstate__ migrates 3.x pickles via _deprecation.migrate_renamed_state_keys. Mechanics and the cross-cutting inventory on [M-050]." - id: M-054 kind: field group: field-flip old: "diff_diff:StackedDiDResults.overall_att" new: "diff_diff:StackedDiDResults.att" introduced_in: "4.0" - deprecated_in: "4.0" - removed_in: "5.0" - status: shimmed + deprecated_in: null + removed_in: "4.0" + status: removed phase: 5 - warning: FutureWarning test_ref: tests/test_v4_storage_flip.py code_refs: [diff_diff/stacked_did_results.py, diff_diff/stacked_did.py] - notes: "SHIMMED at the 4.0 cut (PR 5b, 2026-10-26): StackedDiDResults stores the canonical quintet natively (fields renamed in slot); overall_att/overall_se/overall_t_stat/overall_p_value/overall_conf_int are read-only FutureWarning deprecated_field_property shims (removed 5.0); __setstate__ migrates 3.x pickles via _deprecation.migrate_renamed_state_keys (extends the existing clean_control -> control_group migration). Mechanics and the cross-cutting inventory on [M-050]." + notes: "REMOVED at the 4.0 cut (item 9b, 2026-11-01): UNWARNED BREAK of a released 3.x name (user decision 2026-11-01 - no 4.x debt): the name was native/documented through v3.13.0 and no release carried the FutureWarning shim (it landed on main after the v3.13.0 tag), so the 4.0->5.0 window was retired and the property deleted outright; the five overall_* FutureWarning properties are gone (AttributeError); the canonical quintet att/se/t_stat/p_value/conf_int is the only vocabulary; __setstate__ legacy-key migration via _deprecation.migrate_renamed_state_keys(LEGACY_OVERALL_QUINTET) kept so 3.x pickles still load. Pre-release main (2026-10-25/26): SHIMMED at the 4.0 cut (PR 5b, 2026-10-26): StackedDiDResults stores the canonical quintet natively (fields renamed in slot); overall_att/overall_se/overall_t_stat/overall_p_value/overall_conf_int are read-only FutureWarning deprecated_field_property shims (removed 5.0); __setstate__ migrates 3.x pickles via _deprecation.migrate_renamed_state_keys (extends the existing clean_control -> control_group migration). Mechanics and the cross-cutting inventory on [M-050]." - id: M-055 kind: field group: field-flip old: "diff_diff:EfficientDiDResults.overall_att" new: "diff_diff:EfficientDiDResults.att" introduced_in: "4.0" - deprecated_in: "4.0" - removed_in: "5.0" - status: shimmed + deprecated_in: null + removed_in: "4.0" + status: removed phase: 5 - warning: FutureWarning test_ref: tests/test_v4_storage_flip.py code_refs: [diff_diff/efficient_did_results.py, diff_diff/efficient_did_aggregation.py, diff_diff/efficient_did.py] - notes: "SHIMMED at the 4.0 cut (PR 5b, 2026-10-26): EfficientDiDResults stores the canonical quintet natively (fields renamed in slot); overall_att/overall_se/overall_t_stat/overall_p_value/overall_conf_int are read-only FutureWarning deprecated_field_property shims (removed 5.0); __setstate__ migrates 3.x pickles via _deprecation.migrate_renamed_state_keys. Mechanics and the cross-cutting inventory on [M-050]." + notes: "REMOVED at the 4.0 cut (item 9b, 2026-11-01): UNWARNED BREAK of a released 3.x name (user decision 2026-11-01 - no 4.x debt): the name was native/documented through v3.13.0 and no release carried the FutureWarning shim (it landed on main after the v3.13.0 tag), so the 4.0->5.0 window was retired and the property deleted outright; the five overall_* FutureWarning properties are gone (AttributeError); the canonical quintet att/se/t_stat/p_value/conf_int is the only vocabulary; __setstate__ legacy-key migration via _deprecation.migrate_renamed_state_keys(LEGACY_OVERALL_QUINTET) kept so 3.x pickles still load. Pre-release main (2026-10-25/26): SHIMMED at the 4.0 cut (PR 5b, 2026-10-26): EfficientDiDResults stores the canonical quintet natively (fields renamed in slot); overall_att/overall_se/overall_t_stat/overall_p_value/overall_conf_int are read-only FutureWarning deprecated_field_property shims (removed 5.0); __setstate__ migrates 3.x pickles via _deprecation.migrate_renamed_state_keys. Mechanics and the cross-cutting inventory on [M-050]." - id: M-056 kind: field group: field-flip old: "diff_diff:WooldridgeDiDResults.overall_att" new: "diff_diff:WooldridgeDiDResults.att" introduced_in: "4.0" - deprecated_in: "4.0" - removed_in: "5.0" - status: shimmed + deprecated_in: null + removed_in: "4.0" + status: removed phase: 5 - warning: FutureWarning test_ref: tests/test_v4_storage_flip.py code_refs: [diff_diff/wooldridge_results.py, diff_diff/wooldridge.py] - notes: "SHIMMED at the 4.0 cut (PR 5b, 2026-10-26): WooldridgeDiDResults stores the canonical quintet natively (fields renamed in slot); overall_att/overall_se/overall_t_stat/overall_p_value/overall_conf_int are read-only FutureWarning deprecated_field_property shims (removed 5.0); __setstate__ migrates 3.x pickles via _deprecation.migrate_renamed_state_keys (key renames run BEFORE the existing default backfills); the two self-WRITE sites - aggregate('simple') in wooldridge_results.py and the bootstrap override in wooldridge.py - assign the canonical fields. Mechanics and the cross-cutting inventory on [M-050]." + notes: "REMOVED at the 4.0 cut (item 9b, 2026-11-01): UNWARNED BREAK of a released 3.x name (user decision 2026-11-01 - no 4.x debt): the name was native/documented through v3.13.0 and no release carried the FutureWarning shim (it landed on main after the v3.13.0 tag), so the 4.0->5.0 window was retired and the property deleted outright; the five overall_* FutureWarning properties are gone (AttributeError); the canonical quintet att/se/t_stat/p_value/conf_int is the only vocabulary; __setstate__ legacy-key migration via _deprecation.migrate_renamed_state_keys(LEGACY_OVERALL_QUINTET) kept so 3.x pickles still load. Pre-release main (2026-10-25/26): SHIMMED at the 4.0 cut (PR 5b, 2026-10-26): WooldridgeDiDResults stores the canonical quintet natively (fields renamed in slot); overall_att/overall_se/overall_t_stat/overall_p_value/overall_conf_int are read-only FutureWarning deprecated_field_property shims (removed 5.0); __setstate__ migrates 3.x pickles via _deprecation.migrate_renamed_state_keys (key renames run BEFORE the existing default backfills); the two self-WRITE sites - aggregate('simple') in wooldridge_results.py and the bootstrap override in wooldridge.py - assign the canonical fields. Mechanics and the cross-cutting inventory on [M-050]." - id: M-057 kind: field group: field-flip old: "diff_diff:ChaisemartinDHaultfoeuilleResults.overall_att" new: "diff_diff:ChaisemartinDHaultfoeuilleResults.att" introduced_in: "4.0" - deprecated_in: "4.0" - removed_in: "5.0" - status: shimmed + deprecated_in: null + removed_in: "4.0" + status: removed phase: 5 - warning: FutureWarning test_ref: tests/test_v4_storage_flip.py code_refs: [diff_diff/chaisemartin_dhaultfoeuille_results.py, diff_diff/chaisemartin_dhaultfoeuille.py] - notes: "SHIMMED at the 4.0 cut (PR 5b, 2026-10-26): ChaisemartinDHaultfoeuilleResults stores the canonical quintet natively (fields renamed in slot); overall_att/overall_se/overall_t_stat/overall_p_value/overall_conf_int are read-only FutureWarning deprecated_field_property shims (removed 5.0); __setstate__ migrates 3.x pickles via _deprecation.migrate_renamed_state_keys (extends the existing groups -> units migration). Mechanics and the cross-cutting inventory on [M-050]." + notes: "REMOVED at the 4.0 cut (item 9b, 2026-11-01): UNWARNED BREAK of a released 3.x name (user decision 2026-11-01 - no 4.x debt): the name was native/documented through v3.13.0 and no release carried the FutureWarning shim (it landed on main after the v3.13.0 tag), so the 4.0->5.0 window was retired and the property deleted outright; the five overall_* FutureWarning properties are gone (AttributeError); the canonical quintet att/se/t_stat/p_value/conf_int is the only vocabulary; __setstate__ legacy-key migration via _deprecation.migrate_renamed_state_keys(LEGACY_OVERALL_QUINTET) kept so 3.x pickles still load. Pre-release main (2026-10-25/26): SHIMMED at the 4.0 cut (PR 5b, 2026-10-26): ChaisemartinDHaultfoeuilleResults stores the canonical quintet natively (fields renamed in slot); overall_att/overall_se/overall_t_stat/overall_p_value/overall_conf_int are read-only FutureWarning deprecated_field_property shims (removed 5.0); __setstate__ migrates 3.x pickles via _deprecation.migrate_renamed_state_keys (extends the existing groups -> units migration). Mechanics and the cross-cutting inventory on [M-050]." - id: M-058 kind: field group: field-flip old: "diff_diff:ContinuousDiDResults.overall_att" new: "diff_diff:ContinuousDiDResults.att" introduced_in: "4.0" - deprecated_in: "4.0" - removed_in: "5.0" - status: shimmed + deprecated_in: null + removed_in: "4.0" + status: removed phase: 5 - warning: FutureWarning test_ref: tests/test_v4_storage_flip.py code_refs: [diff_diff/continuous_did_results.py, diff_diff/continuous_did.py, docs/methodology/continuous-did.md] - notes: "SHIMMED at the 4.0 cut (PR 5b, 2026-10-26): ContinuousDiDResults stores the ATT-side headline under the canonical quintet (fields renamed in slot; overall_acrt* untouched - a separate estimand). NINE read-only FutureWarning deprecated_field_property shims onto the five canonical fields: the five former fields overall_att/overall_att_se/overall_att_t_stat/overall_att_p_value/overall_att_conf_int AND the four former silent aliases overall_se/overall_t_stat/overall_p_value/overall_conf_int (removed 5.0). __setstate__ migrates 3.x pickles via a module-local overall_att_* -> canonical map (the silent aliases were never stored). Mechanics on [M-050]. Original: Also flips the deviant overall_att_se/overall_att_p_value/overall_att_conf_int/overall_att_t_stat sibling fields (the trap-catalog outlier)." + notes: "REMOVED at the 4.0 cut (item 9b, 2026-11-01): UNWARNED BREAK of a released 3.x name (user decision 2026-11-01 - no 4.x debt): the name was native/documented through v3.13.0 and no release carried the FutureWarning shim (it landed on main after the v3.13.0 tag), so the 4.0->5.0 window was retired and the property deleted outright; all NINE overall_att_* / overall_* FutureWarning properties are gone (AttributeError); the canonical quintet is the only vocabulary (overall_acrt* untouched - a separate estimand); __setstate__ migration via the module-local _LEGACY_OVERALL_ATT_QUINTET kept so 3.x pickles still load. Pre-release main (2026-10-25/26): SHIMMED at the 4.0 cut (PR 5b, 2026-10-26): ContinuousDiDResults stores the ATT-side headline under the canonical quintet (fields renamed in slot; overall_acrt* untouched - a separate estimand). NINE read-only FutureWarning deprecated_field_property shims onto the five canonical fields: the five former fields overall_att/overall_att_se/overall_att_t_stat/overall_att_p_value/overall_att_conf_int AND the four former silent aliases overall_se/overall_t_stat/overall_p_value/overall_conf_int (removed 5.0). __setstate__ migrates 3.x pickles via a module-local overall_att_* -> canonical map (the silent aliases were never stored). Mechanics on [M-050]. Original: Also flips the deviant overall_att_se/overall_att_p_value/overall_att_conf_int/overall_att_t_stat sibling fields (the trap-catalog outlier)." # ---- Alias table --------------------------------------------------------- - id: M-060 @@ -1835,27 +1827,27 @@ rows: group: obligation-sc-inner-budget old: "diff_diff:SyntheticControl[inner_max_iter]" new: null - deprecated_in: "4.0" - removed_in: "5.0" - status: shimmed + deprecated_in: "3.13.0" + removed_in: "4.0" + status: removed phase: 5 warning: DeprecationWarning test_ref: tests/test_methodology_synthetic_control.py code_refs: [diff_diff/synthetic_control.py, docs/methodology/REGISTRY.md] - notes: "Inert since the exact NNLS inner solve (#838 / #842): the donor-weight problem is solved by Lawson-Hanson's active set, which has no iteration budget. Warn-and-discard shim (stored as None, the [M-001] shape); deprecated_in 4.0 because 4.0 is the locked ladder's next release (tests/test_naming_guard.py _NEXT_RELEASE), so the shim ships there and the removal lands at 5.0." + notes: "REMOVED at the 4.0 cut (item 9b, 2026-11-01): the parameter is gone from SyntheticControl.__init__ (positional slots after it - standardize, alpha, seed, v_cv_t0 - shift up by two); passing it is a TypeError. The DeprecationWarning shipped in the released 3.13.0 (deprecated_in corrected from the ladder's 4.0 to 3.13.0). Through 3.13: Inert since the exact NNLS inner solve (#838 / #842): the donor-weight problem is solved by Lawson-Hanson's active set, which has no iteration budget. Warn-and-discard shim (stored as None, the [M-001] shape); deprecated_in 4.0 because 4.0 is the locked ladder's next release (tests/test_naming_guard.py _NEXT_RELEASE), so the shim ships there and the removal lands at 5.0." - id: M-149 kind: param group: obligation-sc-inner-budget old: "diff_diff:SyntheticControl[inner_min_decrease]" new: null - deprecated_in: "4.0" - removed_in: "5.0" - status: shimmed + deprecated_in: "3.13.0" + removed_in: "4.0" + status: removed phase: 5 warning: DeprecationWarning test_ref: tests/test_methodology_synthetic_control.py code_refs: [diff_diff/synthetic_control.py, docs/methodology/REGISTRY.md] - notes: "Inert since the exact NNLS inner solve (#838 / #842): an exact solve has no stopping tolerance. Same warn-and-discard shim and ladder reasoning as [M-148]." + notes: "REMOVED at the 4.0 cut (item 9b, 2026-11-01): the parameter is gone from SyntheticControl.__init__ (positional slots after it - standardize, alpha, seed, v_cv_t0 - shift up by two); passing it is a TypeError. The DeprecationWarning shipped in the released 3.13.0 (deprecated_in corrected from the ladder's 4.0 to 3.13.0). Through 3.13: Inert since the exact NNLS inner solve (#838 / #842): an exact solve has no stopping tolerance. Same warn-and-discard shim and ladder reasoning as [M-148]." # ---- 4.0-cut framing amendment (2026-10-18) ------------------------------ - id: M-150 kind: param-value @@ -1878,28 +1870,26 @@ rows: old: "diff_diff:LWDiDResults.pvalue" new: "diff_diff:LWDiDResults.p_value" introduced_in: null - deprecated_in: "4.0" - removed_in: "5.0" - status: shimmed + deprecated_in: null + removed_in: "4.0" + status: removed phase: 5 - warning: FutureWarning test_ref: tests/test_lwdid.py code_refs: [diff_diff/lwdid_results.py, diff_diff/_deprecation.py, docs/methodology/REGISTRY.md] - notes: "Born 3.10 as an undocumented duplicate of the canonical quintet name p_value (the alias-diet class, spec section 3.2: one name per concept) - no reader anywhere in the repo (every .pvalue hit is RandomizationResult / SpecificationResult / the lwdid-py package, independent surfaces). Deprecated AT 4.0 rather than removed, because the name shipped in released minors 3.10-3.13; removed 5.0. Shim = deprecated_field_property(..., removed_in='5.0') so the warning and the generated docstring name 5.0 (the helper gained the removed_in keyword in this PR). Row added in the same PR as the shim (the [M-148]/[M-149] precedent). ENFORCEMENT NOTE: 'pvalue' joins test_naming_guard's _PATTERN_TOKENS and _AMBIGUOUS_TOKENS (the two kwarg-lane hits are the independent same-named fields on RandomizationResult / SpecificationResult, allowlisted)." + notes: "REMOVED at the 4.0 cut (item 9b, 2026-11-01): UNWARNED BREAK of a released 3.x name (user decision 2026-11-01 - no 4.x debt): the name was native/documented through v3.13.0 and no release carried the FutureWarning shim (it landed on main after the v3.13.0 tag), so the 4.0->5.0 window was retired and the property deleted outright; LWDiDResults.pvalue is gone; read .p_value. Pre-release main (2026-10-25/26): Born 3.10 as an undocumented duplicate of the canonical quintet name p_value (the alias-diet class, spec section 3.2: one name per concept) - no reader anywhere in the repo (every .pvalue hit is RandomizationResult / SpecificationResult / the lwdid-py package, independent surfaces). Deprecated AT 4.0 rather than removed, because the name shipped in released minors 3.10-3.13; removed 5.0. Shim = deprecated_field_property(..., removed_in='5.0') so the warning and the generated docstring name 5.0 (the helper gained the removed_in keyword in this PR). Row added in the same PR as the shim (the [M-148]/[M-149] precedent). ENFORCEMENT NOTE: 'pvalue' joins test_naming_guard's _PATTERN_TOKENS and _AMBIGUOUS_TOKENS (the two kwarg-lane hits are the independent same-named fields on RandomizationResult / SpecificationResult, allowlisted)." - id: M-152 kind: field group: lwdid-results-contract old: "diff_diff:LWDiDResults.ci" new: "diff_diff:LWDiDResults.conf_int" introduced_in: null - deprecated_in: "4.0" - removed_in: "5.0" - status: shimmed + deprecated_in: null + removed_in: "4.0" + status: removed phase: 5 - warning: FutureWarning test_ref: tests/test_lwdid.py code_refs: [diff_diff/lwdid_results.py, diff_diff/_deprecation.py, docs/methodology/REGISTRY.md] - notes: "Twin of [M-151] for the conf_int alias; same window, same shim. ENFORCEMENT NOTE: 'ci' is library-wide local vocabulary (`ci = ...` assignments in ~21 source modules) and RDPlot's own confidence-level parameter, so it joins _AMBIGUOUS_TOKENS (precise lanes only) with a SURFACE_ALLOWLIST entry for RDPlot.__init__[ci] and CONSUMER_ALLOWLIST entries for rdplot.py and the CCFT-2017 paper review; REGISTRY.md's rdplot `ci` backtick is covered by this row's code_refs." + notes: "REMOVED at the 4.0 cut (item 9b, 2026-11-01): UNWARNED BREAK of a released 3.x name (user decision 2026-11-01 - no 4.x debt): the name was native/documented through v3.13.0 and no release carried the FutureWarning shim (it landed on main after the v3.13.0 tag), so the 4.0->5.0 window was retired and the property deleted outright; LWDiDResults.ci is gone; read .conf_int. Pre-release main (2026-10-25/26): Twin of [M-151] for the conf_int alias; same window, same shim. ENFORCEMENT NOTE: 'ci' is library-wide local vocabulary (`ci = ...` assignments in ~21 source modules) and RDPlot's own confidence-level parameter, so it joins _AMBIGUOUS_TOKENS (precise lanes only) with a SURFACE_ALLOWLIST entry for RDPlot.__init__[ci] and CONSUMER_ALLOWLIST entries for rdplot.py and the CCFT-2017 paper review; REGISTRY.md's rdplot `ci` backtick is covered by this row's code_refs." - id: M-153 kind: behavior group: lwdid-results-contract @@ -1939,3 +1929,27 @@ rows: test_ref: tests/test_v4_cluster_policy.py code_refs: [diff_diff/utils.py] notes: "Shared cluster-argument contract (item 7b, 2026-10-29; the [M-144]/[M-145] validation-tightening precedent): cluster is str | None | False on every estimator exposing it (StackedDiD keeps its 'unit'/'unit_subexp' vocabulary); non-str column labels, True and other types raise the shared ValueError at __init__, through set_params and again at fit (direct-mutation defense). Previously 13 classes accepted any hashable label and only DMLDiD type-checked. Also covers EfficientDiD.hausman_pretest and the LWDiD sensitivity helpers' own cluster arguments." + - id: M-156 + kind: behavior + group: renames-post + old: "diff_diff:run_placebo_test[time]" + new: null + deprecated_in: "4.0" + removed_in: null + status: done + phase: 5 + test_ref: tests/test_diagnostics.py + code_refs: [diff_diff/diagnostics.py, diff_diff/practitioner.py, diff_diff/guides/llms-full.txt, diff_diff/guides/llms-practitioner.txt, docs/api/diagnostics.rst, docs/methodology/REGISTRY.md, docs/tutorials/04_parallel_trends.ipynb, docs/tutorials/17_brand_awareness_survey.ipynb] + notes: "DONE at the 4.0 cut (item 9b, 2026-11-01): time= is the calendar period column ONLY (spec section 8 rule 1); post_periods is required for every test_type and the 0/1 post dummy handed to permutation_test/leave_one_out_test [M-137]/[M-138] is derived as data[time].isin(post_periods) (the placebo_timing_test/placebo_group_test idiom) via diagnostics._resolve_placebo_post; a missing time column, missing/empty post_periods, NaN in the time column, post_periods values absent from data[time], or a pre-existing _placebo_post column raise ValueError. Through 3.x: the wrapper forwarded time= as the dummy, which raised (validate_binary) on any multi-period calendar column - the TODO.md 2(c)-ii redesign row is closed." + - id: M-157 + kind: behavior + group: renames-post + old: "diff_diff:run_all_placebo_tests[time]" + new: null + deprecated_in: "4.0" + removed_in: null + status: done + phase: 5 + test_ref: tests/test_diagnostics.py + code_refs: [diff_diff/diagnostics.py, diff_diff/practitioner.py, diff_diff/guides/llms-full.txt, diff_diff/guides/llms-practitioner.txt, docs/api/diagnostics.rst, docs/methodology/REGISTRY.md, docs/tutorials/04_parallel_trends.ipynb, docs/tutorials/17_brand_awareness_survey.ipynb] + notes: "DONE at the 4.0 cut (item 9b, 2026-11-01): twin of [M-156] for the suite wrapper - time= is the calendar column only, post_periods (already required) feeds the derived dummy, and the validation runs BEFORE the three try/except blocks so a malformed post_periods / time column raises instead of being swallowed into error dicts. Through 3.x: with a multi-period calendar column the permutation and leave-one-out entries were ALWAYS error dicts (validate_binary rejected the forwarded calendar column) and nothing warned." diff --git a/docs/v4-design.md b/docs/v4-design.md index bf90dc30b..862a6dfd8 100644 --- a/docs/v4-design.md +++ b/docs/v4-design.md @@ -60,9 +60,13 @@ sketched in section 9 but not specified here. **Ladder:** 3.9 ships the new surface additively plus FutureWarnings on old surface (sklearn playbook) -> 4.0 removes deprecated surface, flips results -storage (section 5) and the two default policies -> 4.1 adds the front door -> -5.0 removes the 4.0-era compatibility properties. Last fully-old-API release: -3.8.0 (tag `v3.8.0`). +storage (section 5) and the two default policies -> 4.1 adds the front door. +(Amended 2026-11-01, item 9b, user decision: the 4.0-era compatibility +properties scheduled for a 5.0 removal were retired AT 4.0 - no release ever +carried them - so the released 3.x names `overall_*`, `period_effects` and +LWDiD's `pvalue`/`ci` break at 4.0 WITHOUT a warned release; the migration +guide is the only notice, and 5.0 has no scheduled removals.) Last +fully-old-API release: 3.8.0 (tag `v3.8.0`). **maint/3.8 branch rule:** 3.8.x patches are cut from main until the first Phase 2 PR merges; after that, main carries minor-worthy content, so further @@ -378,7 +382,8 @@ settings and unconditionally in the no-unit repeated-cross-sections form. **Results.** 3.9's `TWFE(event_study=True)` returns the unified event-study surface (section 5) from day one - no intermediate container churn. `period_effects` becomes a FutureWarning property view over that surface at -4.0, removed 5.0 [M-016] (the view's values are a private frozen record +4.0, removed 5.0 [M-016] (Amended 2026-11-01, item 9b: the view was retired AT +4.0 before any release carried it - `period_effects` is simply gone; the view's values were a private frozen record mirroring the former `PeriodEffect` fields - `period, effect, se, t_stat, p_value, conf_int, is_significant, significance_stars` - keyed by calendar period with the reference omitted; user decision 2026-10-24; shipped PR 4c-ii @@ -508,7 +513,13 @@ minors.) At 4.0 the STORAGE flips [M-050]..[M-058]: the canonical names become the native dataclass fields; `overall_att` (and sibling `overall_*` inference fields, plus ContinuousDiD's deviant `overall_att_*` family [M-058]) become FutureWarning properties, removed 5.0. MultiPeriodDiD's -`avg_*` family dies with its class [M-011] instead of flipping. +`avg_*` family dies with its class [M-011] instead of flipping. (Amended +2026-11-01, item 9b, user decision: the 4.0 -> 5.0 windows of [M-016], +[M-050]..[M-058], [M-151], [M-152] were retired AT 4.0 - the shims had landed +on main after the v3.13.0 tag and no release carried them, so the courtesy +window protected nobody yet; the released 3.x names therefore break at 4.0 +without a warned release, stated plainly in `docs/migration-4.0.md`. The +pickle migration of section 5 stays.) **Serialization policy.** `to_dict()` / `to_dataframe()` emit canonical names only from 4.0 (deprecated names never leak into serialized output; the @@ -990,11 +1001,14 @@ missed.** guard (`tests/test_naming_guard.py`) and removal PRs consult the family's `code_refs` UNION, and the pre-terminal repo-wide grep above remains the per-row safety net (decided 2026-08-01 over per-row duplication). - Corollary (2026-10-18): the shim helper module `diff_diff/_deprecation.py` - SURVIVES 4.0 - the 4.0->5.0 FutureWarning property shims of [M-016] and - [M-050]..[M-058] are its consumers through the 5.0 window - so the 4.0 - removal PRs delete the shims that use it, never the module (recorded in - [M-050]'s `code_refs`). + Corollary (2026-10-18; amended 2026-11-01, item 9b): the helper module + `diff_diff/_deprecation.py` SURVIVES 4.0 as the pickle-migration + (`LEGACY_OVERALL_QUINTET`, `migrate_renamed_state_keys`, + `migrate_event_study_reference_sentinel`) and required-arg (`NOT_SUPPLIED`, + `require_arg`) helper module (recorded in [M-050]'s `code_refs`); the + kwarg-rename and property-shim helpers were deleted at item 9b together + with their last consumers (the [M-016] / [M-050]..[M-058] / [M-151] / + [M-152] shims, retired AT 4.0). **Missed-rename amendments (2026-08-02, the 2(c)-ii PR-B sweep):** [M-136] (LPDiD `to_dataframe(level="event")` - the drifted value spelling every @@ -1006,7 +1020,11 @@ phase-1 estimator-roster inventory missed). The `run_placebo_test` / `run_all_placebo_tests` WRAPPERS keep their single overloaded `time` (calendar for the timing/group tests, post dummy for the two renamed callees): a rename cannot express dual semantics - the signature redesign is -tracked in TODO.md and the guard carries honest allowlist reasons. +tracked in TODO.md and the guard carries honest allowlist reasons. (RESOLVED +2026-11-01, item 9b: `time` is the calendar column on both wrappers, +`post_periods` is required, and the post dummy is derived as +`data[time].isin(post_periods)` [M-156] [M-157]; the wrappers joined the +guard's rule-1 calendar inventory.) **Domain vocabulary that is NOT a violation** (recorded so the sweep is not re-litigated): the staggered family's `group`/`groups` on results containers @@ -1026,7 +1044,9 @@ candidates:** altered-meaning trap - and over a convenience subclass); underscored control_group values (over R-compact spellings, over accept-both); unified event-study representation with `period_effects` as a 4.0->5.0 -property (over keeping the dict canonical, over hard removal); n_bootstrap +property (over keeping the dict canonical, over hard removal; superseded +2026-11-01, item 9b: hard removal at 4.0 once it was clear no release would +carry the property); n_bootstrap semantic-only unification (over uniform 999, over uniform 200); panel auto-cluster-at-unit (over never-auto-cluster, over status quo). 2026-07-31 addendum (consolidation scope): staggered-family mega-merge @@ -1052,7 +1072,7 @@ above; anything only one PR cares about stays in that PR's plan.** | 2: contract foundations | 3.9 | (a) results base + unified event-study representation [M-092] + to_dict completion + the Diagnostic marker base on the diagnostic result roster [M-091] (section 3.5); (b) `aggregate()` + fit(aggregate=) shims [M-020..M-027] [M-139] (M-020's shim already shipped; M-139 is the HAD workflow twin, a pre-cut amendment); (c) param renames [M-030..M-047] [M-084] [M-086..M-089] + their results-field mirrors [M-094] [M-095] (section 8 rule 9) + the public-function completeness sweep [M-097..M-113] (section 8 rule 10) + the dCDH results mirror [M-114] + the fourth `robust` site [M-115] + the 2(c)-ii missed-rename amendments [M-136..M-138] (LPDiD `level` value; the two post-dummy diagnostics params) + BaseEstimator mixin + ContinuousDiD covariates move; (d) alias introduction [M-062] (the Spillover introduction is cancelled [M-063]) + the alias-diet `__getattr__` warning shim [M-135] + wrapper deprecations [M-070..M-077] + the two inference-surface policies: `n_bootstrap` semantic unification [M-081] and the wild-cluster-bootstrap roster guard [M-096]; shipped insertions (all done): the aggregate contract [M-122], the ETWFE reference-period family [M-123] [M-124] [M-125], and the variance-consolidation program [M-126] [M-127] | | 3: merges | 3.9 | (a) TWFE event-study mode [M-010] + EventStudy warn [M-060] + the fit `time`->`post` rename [M-082] (gates: section 4.1's equivalence/divergence/pooled-parity test triple) (shipped: tests/test_v4_merge_mpd.py; consumer ports incl. HonestDiD/PreTrendsPower calendar routes); (b) TripleDifference facade [M-013] + the SDDD alias [M-064] (shipped: tests/test_v4_merge_ddd.py; the engine relocation into the private shared mixin, the keyword-only staggered fit params, and the pscore_trim tightening [M-142]. The fit-time aggregate=/balance_e= carve-out rows are scheduled REMOVALS and so are cited in the phase-5 cell, not here - their only lifecycle version is removed_in 4.0); (c) CiC method= [M-015] + its results-field mirror [M-143] (shipped: tests/test_v4_merge_cic.py; method= is keyword-only and lowercase-only, the QDiD CLASS is deprecated while the METHOD is not, and ChangesInChangesResults.estimator -> .method carries a dual-key to_dict() window through 3.9) | | 4: release + soak | 3.9 cut | Migration guide written (skeleton: section 10); maintainer cuts 3.9; maint/3.8 rule active | -| 5: enforcement | 4.0 | Removals [M-010..M-015, M-020..M-027, M-139, M-030, M-032..M-047 old names, M-060, M-061, M-064, M-070..M-077, M-084, M-086..M-089, M-001..M-003, M-117, M-118, M-119, M-120, M-140, M-141, M-143] + the alias diet [M-132]..[M-134] + the amendment's old names [M-094] [M-095] [M-097..M-115] [M-136..M-138] [M-150] (incl. their consumer migrations and the `clean_control` serialized reporting key); M-031's old `time` name persists as the merged class's calendar column, so it is deliberately absent from the removal roster (its 4.0 enforcement is the M-085 behavior entry below); property window: [M-016] property-flips at 4.0 (removal at 5.0); storage flips [M-050..M-058]; default policies [M-004..M-006, M-128..M-131, M-080] + the shared cluster contract [M-155] (7b) and the SpilloverDiD df_convention knob [M-154] (7c); merged-class behavior enforcements [M-083] [M-085]; warning retirement [M-007]; fastpath go/no-go [M-008]; diagnostic-family docs/roster reorganization [M-090]; sentinel retirement [M-093]; the family-wide anticipation validation [M-144], the family-wide pscore_trim validation [M-145], and the family-wide summary-alpha guard [M-146] (staggered family at the initial cut; extended to the non-staggered summaries post-cut), the LWDiD to_dataframe reshape [M-153] (behavior tightenings landing at 4.0 - shipped post-3.9-cut, terminal `done`, no removal/deprecation fields); the additive Wooldridge comparison-support policy [M-147] (`unsupported_period_action="drop"` preserves filtering, `"error"` refuses before period removal; same post-cut lifecycle); the inert SyntheticControl inner-budget pair [M-148] [M-149] (warn-and-discard shims at 4.0 once the donor weights moved to an exact NNLS, removal at 5.0); the LWDiD result-alias pair [M-151] [M-152] (FutureWarning shims at 4.0, removal at 5.0); docs/llms.txt/README refresh | +| 5: enforcement | 4.0 | Removals [M-010..M-015, M-020..M-027, M-139, M-030, M-032..M-047 old names, M-060, M-061, M-064, M-070..M-077, M-084, M-086..M-089, M-001..M-003, M-117, M-118, M-119, M-120, M-140, M-141, M-143] + the alias diet [M-132]..[M-134] + the amendment's old names [M-094] [M-095] [M-097..M-115] [M-136..M-138] [M-150] (incl. their consumer migrations and the `clean_control` serialized reporting key); M-031's old `time` name persists as the merged class's calendar column, so it is deliberately absent from the removal roster (its 4.0 enforcement is the M-085 behavior entry below); property window: [M-016] removed at 4.0 (item 9b; the planned 5.0 window was retired); storage flips [M-050..M-058] (their 4.0 -> 5.0 shims retired at 4.0, item 9b); default policies [M-004..M-006, M-128..M-131, M-080] + the shared cluster contract [M-155] (7b) and the SpilloverDiD df_convention knob [M-154] (7c); merged-class behavior enforcements [M-083] [M-085]; placebo-wrapper calendar contract [M-156] [M-157] (item 9b); warning retirement [M-007]; fastpath go/no-go [M-008]; diagnostic-family docs/roster reorganization [M-090]; sentinel retirement [M-093]; the family-wide anticipation validation [M-144], the family-wide pscore_trim validation [M-145], and the family-wide summary-alpha guard [M-146] (staggered family at the initial cut; extended to the non-staggered summaries post-cut), the LWDiD to_dataframe reshape [M-153] (behavior tightenings landing at 4.0 - shipped post-3.9-cut, terminal `done`, no removal/deprecation fields); the additive Wooldridge comparison-support policy [M-147] (`unsupported_period_action="drop"` preserves filtering, `"error"` refuses before period removal; same post-cut lifecycle); the inert SyntheticControl inner-budget pair [M-148] [M-149] (DeprecationWarning in 3.13.0, removed at 4.0 - item 9b); the LWDiD result-alias pair [M-151] [M-152] (removed at 4.0, item 9b); docs/llms.txt/README refresh | | 6: front door | 4.1 | `event_study(data, outcome, unit, time, first_treat, estimator=...)` comparison entry point over the staggered family (sketch only; specified in its own plan) | Citation semantic for the table: a cell may cite a row whose current `phase` @@ -1220,7 +1240,8 @@ tests-first PR that flips no rows). the canonical quintet ahead of the flip; 5b, 2026-10-26, flipped the storage: the quintet are the native fields, renamed in slot, with the `overall_*` names as read-only `deprecated_field_property` FutureWarning - shims (removed 5.0), `DMLDiDResults` re-declaring the five shims so the + shims (removed 5.0; AMENDED 2026-11-01, item 9b: the shims were retired AT + 4.0 before any release carried them), `DMLDiDResults` re-declaring the five shims so the warning names the subclass, `__setstate__` migration through the new `_deprecation.migrate_renamed_state_keys` / `LEGACY_OVERALL_QUINTET`, ContinuousDiD's nine spellings shimmed, the report surface reading the @@ -1266,6 +1287,22 @@ tests-first PR that flips no rows). `/docs-check` classification; pinned by `tests/test_v4_diagnostic_family_docs.py`, which runs in the docs-tests workflow; row flipped to `done`.) +9b. Shim retirement + placebo redesign (SHIPPED 2026-11-01, user decision: no + 4.x debt): the 14 rows scheduled for a 5.0 removal - [M-016] [M-050..M-058] + [M-148] [M-149] [M-151] [M-152] - flipped to `removed` at 4.0 (none of + the FutureWarning shims had reached a release; M-148/M-149's + DeprecationWarning shipped in 3.13.0), so the released 3.x names + `overall_*` / `overall_att_*`, `period_effects` and LWDiD's `pvalue`/`ci` + break at 4.0 without a warned release (stated plainly in the migration + guide); `_deprecation.py` reduced to the pickle-migration and + required-arg helpers; `run_placebo_test` / `run_all_placebo_tests` take + `time=` as the calendar column only, require `post_periods`, and derive + the post dummy as `data[time].isin(post_periods)` [M-156] [M-157] + (TODO.md's 2(c)-ii redesign row closed - the 3.x wrappers returned error + dicts on any multi-period calendar column). Pinned by + `tests/test_v4_storage_flip.py`, `tests/test_v4_merge_mpd.py`, + `tests/test_methodology_synthetic_control.py`, `tests/test_lwdid.py` and + `tests/test_diagnostics.py`. 10. Release: bump to 4.0.0 (closes the cut window), changelog compile, migration-guide retitle, due gate green; re-execute (Rust backend) the tutorials whose clustered-default outputs moved at item 7a and were @@ -1281,7 +1318,7 @@ tests-first PR that flips no rows). Ordering constraints: 4a before 4b before 4c-i before 4c-ii; 4 before 5 and 6; 5 before 6 (the unified DDD container is born canonical); 7a before 7b before 7c; 7 and 8 -independent; 9 after 4/6; 10 last. +independent; 9 after 4/6; 9b after 5 and 4c-ii; 10 last. Waves 2 and 3 are independent of each other and of 4. **3.9-cut checklist (un-rowed obligations).** `test_due_rows_are_terminal` @@ -1440,6 +1477,13 @@ or `evaluate` -> `done`. Terminal rows (`removed`/`done`) keep asserting forever - a removed symbol resurrecting is a test failure. **Assertion semantics (enforced by `tests/test_v4_matrix.py`).** +- Item 9b (2026-11-01): a row may move from `shimmed` to `removed` inside the + cut window with `deprecated_in: null` and no `warning` when no RELEASE + carried its shim (the shim landed on main after `cut_base`); the guard's + cut-window exemption is what permits the early terminal flip, and the + migration guide carries the unwarned-break notice. `deprecated_in` is + corrected to the release that actually warned when one did (M-148/M-149: + 3.13.0). - Cut-PR-2 probe clarifications (2026-10-18): at `removed`, a `function` row whose top-level `diff_diff:Name` resolves to a MODULE object counts as absent (four 3.9 wrappers - `trop`, `synthetic_control`, `stacked_did`, diff --git a/tests/_capture_v4_aggregate_oracles.py b/tests/_capture_v4_aggregate_oracles.py index 38949b197..7ba5dcc72 100644 --- a/tests/_capture_v4_aggregate_oracles.py +++ b/tests/_capture_v4_aggregate_oracles.py @@ -107,11 +107,11 @@ def _dict_surface(surface: Optional[Dict[Any, Dict[str, Any]]]) -> Optional[Dict def _overall(r: Any) -> Dict[str, Optional[float]]: - ci = getattr(r, "overall_conf_int", None) or (np.nan, np.nan) + ci = r.conf_int if r.conf_int is not None else (np.nan, np.nan) return { "att": _f(r.att), "se": _f(r.se), - "t_stat": _f(getattr(r, "overall_t_stat", np.nan)), + "t_stat": _f(r.t_stat), "p_value": _f(r.p_value), "ci_lo": _f(ci[0]), "ci_hi": _f(ci[1]), diff --git a/tests/test_balance_interop_contract.py b/tests/test_balance_interop_contract.py index c7e11258a..c01240501 100644 --- a/tests/test_balance_interop_contract.py +++ b/tests/test_balance_interop_contract.py @@ -278,11 +278,10 @@ def test_result_accepts_provenance_attribute(fitted_cs): fitted_cs._balance_adjustment = sentinel assert fitted_cs._balance_adjustment is sentinel del fitted_cs._balance_adjustment - # ...while the legacy ``overall_*`` names are read-only FutureWarning - # properties since the 4.0 storage flip (the canonical ``att`` is the - # stored field; full coverage lives in tests/test_result_aliases.py). - with pytest.raises(AttributeError): - fitted_cs.overall_att = 0.0 + # ...while the legacy ``overall_*`` names are gone since the 4.0 storage + # flip (the canonical ``att`` is the stored field; the shims were retired + # at 4.0 by item 9b; full coverage lives in tests/test_result_aliases.py). + assert not hasattr(fitted_cs, "overall_att") # --------------------------------------------------------------------------- diff --git a/tests/test_diagnostics.py b/tests/test_diagnostics.py index f22a68d05..5f124a857 100644 --- a/tests/test_diagnostics.py +++ b/tests/test_diagnostics.py @@ -634,9 +634,10 @@ def test_dispatch_permutation(self, simple_panel_data): simple_panel_data, outcome="outcome", treatment="treated", - time="post", + time="period", unit="unit", test_type="permutation", + post_periods=[2, 3], n_permutations=50, seed=42, ) @@ -649,9 +650,10 @@ def test_dispatch_leave_one_out(self, simple_panel_data): simple_panel_data, outcome="outcome", treatment="treated", - time="post", + time="period", unit="unit", test_type="leave_one_out", + post_periods=[2, 3], ) assert results.test_type == "leave_one_out" @@ -701,13 +703,16 @@ def test_runs_all_tests(self, panel_data_parallel_trends): seed=42, ) - # Should have fake timing tests for periods 1 and 2 - assert "fake_timing_1" in results - assert "fake_timing_2" in results - - # Should have permutation and LOO tests - assert "permutation" in results - assert "leave_one_out" in results + # Fake timing tests for periods 1 and 2, plus permutation and LOO - + # every one a real result on a six-period calendar column (through + # 3.x the two dummy-consuming tests came back as error dicts here). + assert set(results) == {"fake_timing_1", "fake_timing_2", "permutation", "leave_one_out"} + for key, value in results.items(): + assert isinstance(value, PlaceboTestResults), (key, value) + assert results["permutation"].test_type == "permutation" + assert results["leave_one_out"].test_type == "leave_one_out" + assert np.isfinite(results["permutation"].p_value) + assert np.isfinite(results["leave_one_out"].placebo_effect) def test_returns_dict_structure(self, simple_panel_data): """Should return properly structured dict.""" @@ -724,10 +729,241 @@ def test_returns_dict_structure(self, simple_panel_data): ) assert isinstance(results, dict) - - # Check that each result is either PlaceboTestResults or error dict + assert set(results) == {"fake_timing_1", "permutation", "leave_one_out"} for key, value in results.items(): - assert isinstance(value, (PlaceboTestResults, dict)) + assert isinstance(value, PlaceboTestResults), (key, value) + + +# ============================================================================= +# Wrapper calendar contract (ledger rows M-156 / M-157, 4.0 item 9b) +# ============================================================================= + + +class TestPlaceboWrapperCalendarContract: + """``time`` is the calendar column; the post dummy is derived from ``post_periods``.""" + + @pytest.mark.parametrize( + "test_type", ["fake_timing", "fake_group", "permutation", "leave_one_out"] + ) + @pytest.mark.parametrize("post_periods", [None, []]) + def test_run_placebo_test_requires_post_periods( + self, simple_panel_data, test_type, post_periods + ): + with pytest.raises(ValueError, match="post_periods is required"): + run_placebo_test( + simple_panel_data, + outcome="outcome", + treatment="treated", + time="period", + unit="unit", + test_type=test_type, + fake_treatment_period=1, + fake_treatment_group=[15, 16], + post_periods=post_periods, + n_permutations=10, + seed=0, + ) + + @pytest.mark.parametrize("post_periods", [None, []]) + def test_run_all_requires_post_periods_and_raises_not_error_dict( + self, simple_panel_data, post_periods + ): + # post_periods is a required positional on run_all_placebo_tests, so + # omitting it is Python's own TypeError; None / [] reach the helper and + # raise BEFORE the try blocks (never recorded as an error dict). + with pytest.raises(ValueError, match="post_periods is required"): + run_all_placebo_tests( + simple_panel_data, + outcome="outcome", + treatment="treated", + time="period", + unit="unit", + pre_periods=[0, 1], + post_periods=post_periods, + n_permutations=10, + seed=0, + ) + + def test_misspelled_time_column_raises_value_error(self, simple_panel_data): + with pytest.raises(ValueError, match="time column 'perido' not found"): + run_all_placebo_tests( + simple_panel_data, + outcome="outcome", + treatment="treated", + time="perido", + unit="unit", + pre_periods=[0, 1], + post_periods=[2, 3], + ) + + def test_post_periods_absent_from_time_column_raises(self, simple_panel_data): + with pytest.raises(ValueError, match=r"post_periods values \[7\] do not occur"): + run_placebo_test( + simple_panel_data, + outcome="outcome", + treatment="treated", + time="period", + unit="unit", + test_type="permutation", + post_periods=[3, 7], + n_permutations=10, + seed=0, + ) + + def test_nan_in_time_column_raises(self, simple_panel_data): + data = simple_panel_data.copy() + data.loc[data.index[:3], "period"] = np.nan + with pytest.raises(ValueError, match="has missing values"): + run_all_placebo_tests( + data, + outcome="outcome", + treatment="treated", + time="period", + unit="unit", + pre_periods=[0, 1], + post_periods=[2, 3], + ) + + def test_preexisting_placebo_post_column_raises(self, simple_panel_data): + data = simple_panel_data.copy() + data["_placebo_post"] = 0 + with pytest.raises(ValueError, match="already has a '_placebo_post' column"): + run_placebo_test( + data, + outcome="outcome", + treatment="treated", + time="period", + unit="unit", + test_type="leave_one_out", + post_periods=[2, 3], + ) + + def test_wrapper_does_not_mutate_input(self, simple_panel_data): + before = simple_panel_data.copy() + run_placebo_test( + simple_panel_data, + outcome="outcome", + treatment="treated", + time="period", + unit="unit", + test_type="permutation", + post_periods=[2, 3], + n_permutations=10, + seed=0, + ) + pd.testing.assert_frame_equal(simple_panel_data, before) + assert "_placebo_post" not in simple_panel_data.columns + + def test_permutation_parity_with_direct_callee(self, simple_panel_data): + """The derived dummy reproduces ``post`` exactly, so the wrapper equals the callee bitwise.""" + assert ( + simple_panel_data["post"] == simple_panel_data["period"].isin([2, 3]).astype(int) + ).all() + via_wrapper = run_placebo_test( + simple_panel_data, + outcome="outcome", + treatment="treated", + time="period", + unit="unit", + test_type="permutation", + post_periods=[2, 3], + n_permutations=40, + seed=7, + ) + direct = permutation_test( + simple_panel_data, + outcome="outcome", + treatment="treated", + post="post", + unit="unit", + n_permutations=40, + seed=7, + ) + assert via_wrapper.placebo_effect == direct.placebo_effect + assert via_wrapper.p_value == direct.p_value + np.testing.assert_array_equal( + via_wrapper.permutation_distribution, direct.permutation_distribution + ) + + @pytest.mark.parametrize("label_kind", ["pandas_timestamp", "numpy_datetime64"]) + def test_datetime_calendar_column_with_either_scalar_kind(self, simple_panel_data, label_kind): + """Datetime periods: ``post_periods`` may be pandas Timestamps or NumPy datetime64 + scalars (``data[time].values[...]``) - membership is dtype-aware, never a Python-set + hash comparison, so both spellings validate and reproduce the direct callees.""" + data = simple_panel_data.copy() + data["date"] = pd.Timestamp("2020-01-01") + pd.to_timedelta(data["period"] * 30, unit="D") + dates = np.sort(data["date"].unique().to_numpy(dtype="datetime64[ns]")) + if label_kind == "numpy_datetime64": + post_dates = list(dates[2:]) + assert all(isinstance(d, np.datetime64) for d in post_dates) + else: + post_dates = [pd.Timestamp(d) for d in dates[2:]] + assert all(isinstance(d, pd.Timestamp) for d in post_dates) + assert (data["post"] == data["date"].isin(post_dates).astype(int)).all() + for test_type in ("permutation", "leave_one_out"): + via_wrapper = run_placebo_test( + data, + outcome="outcome", + treatment="treated", + time="date", + unit="unit", + test_type=test_type, + post_periods=post_dates, + n_permutations=20, + seed=3, + ) + if test_type == "permutation": + direct = permutation_test( + data, + outcome="outcome", + treatment="treated", + post="post", + unit="unit", + n_permutations=20, + seed=3, + ) + else: + direct = leave_one_out_test( + data, outcome="outcome", treatment="treated", post="post", unit="unit" + ) + assert via_wrapper.placebo_effect == direct.placebo_effect + assert via_wrapper.p_value == direct.p_value + suite = run_all_placebo_tests( + data, + outcome="outcome", + treatment="treated", + time="date", + unit="unit", + pre_periods=list(dates[:2]), + post_periods=post_dates, + n_permutations=20, + seed=3, + ) + for key, value in suite.items(): + assert isinstance(value, PlaceboTestResults), (key, value) + + def test_leave_one_out_parity_with_direct_callee(self, simple_panel_data): + via_wrapper = run_placebo_test( + simple_panel_data, + outcome="outcome", + treatment="treated", + time="period", + unit="unit", + test_type="leave_one_out", + post_periods=[2, 3], + ) + direct = leave_one_out_test( + simple_panel_data, + outcome="outcome", + treatment="treated", + post="post", + unit="unit", + ) + assert via_wrapper.placebo_effect == direct.placebo_effect + assert via_wrapper.p_value == direct.p_value + np.testing.assert_array_equal( + via_wrapper.leave_one_out_effects, direct.leave_one_out_effects + ) class TestDiagnosticsTStatNaN: diff --git a/tests/test_lwdid.py b/tests/test_lwdid.py index 8b27412fe..6448d1b5d 100644 --- a/tests/test_lwdid.py +++ b/tests/test_lwdid.py @@ -4639,7 +4639,7 @@ def test_cohort_effect_cell_mass_oracle_unbalanced(self): class TestV4ResultsContract: - """4.0 results-contract conformance (ledger rows M-146, M-151, M-152, M-153).""" + """4.0 results-contract conformance (ledger rows M-146, M-151, M-152 - aliases removed at 4.0 - and M-153).""" @staticmethod def _common_df(seed=23, n=40, T=6): @@ -4757,18 +4757,16 @@ def test_to_dataframe_relays_nan_inference_jointly(self): for col in ("se", "t_stat", "p_value", "conf_int_lower", "conf_int_upper", "df"): assert np.isnan(frame.loc[0, col]), col - # -- M-151 / M-152: pvalue / ci alias shims ------------------------------- + # -- M-151 / M-152: pvalue / ci aliases removed at 4.0 (item 9b) ---------- @pytest.mark.parametrize("old, new", [("pvalue", "p_value"), ("ci", "conf_int")]) - def test_alias_properties_warn_and_relay(self, common, old, new): + def test_alias_properties_are_removed(self, common, old, new): + import inspect + from diff_diff.lwdid_results import LWDiDResults assert old not in LWDiDResults.__dataclass_fields__ assert new in LWDiDResults.__dataclass_fields__ - with pytest.warns( - FutureWarning, match=rf"LWDiDResults\.{old} is deprecated.*removed in 5\.0" - ): - value = getattr(common, old) - assert value == getattr(common, new) - with pytest.raises(AttributeError): - setattr(common, old, value) - assert "removed in 5.0" in getattr(LWDiDResults, old).__doc__ + assert inspect.getattr_static(LWDiDResults, old, None) is None + assert not hasattr(common, old) + canonical = getattr(common, new) + assert canonical is not None diff --git a/tests/test_methodology_placebo.py b/tests/test_methodology_placebo.py index 7a3afac11..78f66a512 100644 --- a/tests/test_methodology_placebo.py +++ b/tests/test_methodology_placebo.py @@ -391,6 +391,7 @@ def test_dispatcher_threads_treatment_into_fake_group(self): unit="unit", test_type="fake_group", fake_treatment_group=[3, 4], + post_periods=[1], ) # dispatcher passes treatment -> ever-treated dropped -> 5 controls x 2 = 10 rows assert res.n_obs == 10 diff --git a/tests/test_methodology_synthetic_control.py b/tests/test_methodology_synthetic_control.py index 5a43b278e..561ff4b6c 100644 --- a/tests/test_methodology_synthetic_control.py +++ b/tests/test_methodology_synthetic_control.py @@ -76,7 +76,7 @@ # Behavior tests only need a VALID, cleanly-converged fit, not data-driven V quality. # A single start + a small outer cap gives a clean ~0.1s fit without changing what these # tests assert. The inner weight solve is exact (one NNLS), with no budget or tolerance -# to set (``inner_max_iter`` / ``inner_min_decrease`` are deprecated no-ops). Pure-Python +# to set (the former ``inner_max_iter`` / ``inner_min_decrease`` params were removed at 4.0). Pure-Python # coverage of the # production-default nested path (n_starts=4 with the _v_starts heuristic candidates) is # kept by the dedicated non-slow ``test_nested_production_defaults_smoke``; the @slow @@ -1070,31 +1070,19 @@ def exhausted(Y, zeta=0.0, return_convergence=False): assert np.isnan(res.placebo_p_value) -def test_inner_budget_params_are_deprecated_no_ops(): - # inner_max_iter / inner_min_decrease (ledger M-148 / M-149): DeprecationWarning when - # passed, discarded to None (get_params / set_params see None), and the fit is the - # same as the default's. A default construction never warns. - df, _, _ = _make_panel(n_donors=4) - with warnings.catch_warnings(): - warnings.simplefilter("error") - base = SyntheticControl(seed=0, **_FAST) - assert base.get_params()["inner_max_iter"] is None - assert base.get_params()["inner_min_decrease"] is None - with pytest.warns(DeprecationWarning, match="inner_max_iter is deprecated and has no effect"): - est = SyntheticControl(seed=0, inner_max_iter=5, **_FAST) - with pytest.warns( - DeprecationWarning, match="inner_min_decrease is deprecated and has no effect" - ): +def test_inner_budget_params_are_removed(): + # inner_max_iter / inner_min_decrease (ledger M-148 / M-149, removed at 4.0 - item 9b): + # the constructor rejects them, get_params no longer reports them, and the + # transactional set_params rejects them through its probe re-init. + with pytest.raises(TypeError, match="inner_max_iter"): + SyntheticControl(seed=0, inner_max_iter=5, **_FAST) + with pytest.raises(TypeError, match="inner_min_decrease"): SyntheticControl(seed=0, inner_min_decrease=1e-3, **_FAST) - assert est.inner_max_iter is None and est.get_params()["inner_max_iter"] is None - with pytest.warns(DeprecationWarning, match="inner_min_decrease is deprecated"): + est = SyntheticControl(seed=0, **_FAST) + assert "inner_max_iter" not in est.get_params() + assert "inner_min_decrease" not in est.get_params() + with pytest.raises((TypeError, ValueError), match="inner_min_decrease"): est.set_params(inner_min_decrease=1e-2) - assert est.get_params() == base.get_params() - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - a = base.fit(df, "y", "treated", "unit", "year") - b = est.fit(df, "y", "treated", "unit", "year") - assert a.att == b.att and a.donor_weights == b.donor_weights def test_inner_solve_is_exact_where_a_budgeted_solve_stopped(): @@ -1251,8 +1239,6 @@ def test_get_set_params_roundtrip(): "custom_v", "optimizer_options", "n_starts", - "inner_max_iter", - "inner_min_decrease", "standardize", "alpha", "seed", diff --git a/tests/test_naming_guard.py b/tests/test_naming_guard.py index 4543da706..5b2880780 100644 --- a/tests/test_naming_guard.py +++ b/tests/test_naming_guard.py @@ -545,6 +545,10 @@ def _build_rowed_index(): "placebo_timing_test[time]", "plot_staircase[time]", "profile_panel[time]", + # The two placebo wrappers joined the calendar-only contract at 4.0 (rows + # M-156 / M-157, item 9b): the post dummy is derived from post_periods. + "run_all_placebo_tests[time]", + "run_placebo_test[time]", "summarize_did_data[time]", "twowayfeweights[time]", "validate_did_data[time]", @@ -583,12 +587,6 @@ def _build_rowed_index(): "overall/event_study modes survive - only the routing param died" ), "plot_group_effects[groups]": _CS_COHORT + " - cohort selector on the plotting surface", - "DMLDiDResults.overall_att": ( - "re-declared deprecated_field_property shim so the FutureWarning " - "names the subclass; the storage is INHERITED from " - "CallawaySantAnnaResults and rides M-050 (dml_did_results.py / " - "dml_did.py are in its code_refs) - no independent row" - ), "TripleDifference.fit[group]": ( "rule-3 reserved treated-group 0/1 indicator (v4-design section 8 rule 3)" ), @@ -607,16 +605,6 @@ def _build_rowed_index(): ) for fn in ("attgt_weights", "decompose_twfe_weights") }, - "run_placebo_test[time]": ( - "OVERLOADED pass-through, redesign pending (TODO.md): forwarded as " - "the calendar column to placebo_timing_test/placebo_group_test AND " - "as the 0/1 post dummy to permutation_test/leave_one_out_test " - "(M-137/M-138) - workable only because a two-period 0/1 calendar " - "column is both; a rename cannot fix the dual semantics" - ), - "run_all_placebo_tests[time]": ( - "OVERLOADED pass-through, redesign pending (TODO.md) - see " "run_placebo_test[time]" - ), **{key: _RULE1_TIME for key in _RULE1_TIME_SURFACES}, "rank_control_units[lambda_reg]": ( "prep helper's own independent regularization param (prep.py) - a " @@ -1027,6 +1015,69 @@ def _token_family_code_refs(tok): ("clean_control", "diff_diff/stacked_did_results.py"): ( "__setstate__ legacy-key migration (M-095), kept" ), + # (a-ii) item 9b (2026-11-01): the M-016 / M-050..M-058 / M-148 / M-149 / + # M-151 / M-152 shims were removed AT 4.0, so their token families have no + # live row left and every remaining lane hit must be accounted for here. + ("overall_att", "diff_diff/_deprecation.py"): ( + "LEGACY_OVERALL_QUINTET __setstate__ key map (M-050..M-058), kept so 3.x pickles load" + ), + ("overall_att", "diff_diff/continuous_did_results.py"): ( + "_LEGACY_OVERALL_ATT_QUINTET __setstate__ key map (M-058), kept so 3.x pickles load" + ), + ("overall_att", "diff_diff/chaisemartin_dhaultfoeuille.py"): ( + "body-local `overall_att` name for the overall aggregate (the estimator's own arithmetic), not a reader of the removed M-050..M-058 property" + ), + ("overall_att", "diff_diff/continuous_did.py"): ( + "body-local `overall_att` name for the overall aggregate (the estimator's own arithmetic), not a reader of the removed M-050..M-058 property" + ), + ("overall_att", "diff_diff/efficient_did_aggregation.py"): ( + "body-local `overall_att` name for the overall aggregate (the estimator's own arithmetic), not a reader of the removed M-050..M-058 property" + ), + ("overall_att", "diff_diff/imputation.py"): ( + "body-local `overall_att` name for the overall aggregate (the estimator's own arithmetic), not a reader of the removed M-050..M-058 property" + ), + ("overall_att", "diff_diff/imputation_results.py"): ( + "bootstrap-kit replay keyword (`overall_att=bk[...]`) of the bootstrap draw containers, which keep their payload names (REGISTRY field-naming Note); not a results-property reader" + ), + ("overall_att", "diff_diff/stacked_did.py"): ( + "body-local `overall_att` name for the overall aggregate (the estimator's own arithmetic), not a reader of the removed M-050..M-058 property" + ), + ("overall_att", "diff_diff/staggered_aggregation.py"): ( + "body-local `overall_att` name for the overall aggregate (the estimator's own arithmetic), not a reader of the removed M-050..M-058 property" + ), + ("overall_att", "diff_diff/sun_abraham.py"): ( + "body-local `overall_att` name for the overall aggregate (the estimator's own arithmetic), not a reader of the removed M-050..M-058 property" + ), + ("overall_att", "diff_diff/two_stage.py"): ( + "body-local `overall_att` name for the overall aggregate (the estimator's own arithmetic), not a reader of the removed M-050..M-058 property" + ), + ("overall_att", "diff_diff/two_stage_results.py"): ( + "bootstrap-kit replay keyword (`overall_att=bk[...]`) of the bootstrap draw containers, which keep their payload names; not a results-property reader" + ), + ("overall_att", "diff_diff/wooldridge.py"): ( + "body-local `overall_att` name for the overall aggregate (the estimator's own arithmetic), not a reader of the removed M-050..M-058 property" + ), + ("overall_att", "docs/methodology/REGISTRY.md"): ( + "methodology prose: 3.x storage history of the M-050..M-058 flip and the retired LWDiD pre-release surface; not a reader" + ), + ("overall_att", "docs/methodology/REPORTING.md"): ( + "schema-version history (DR 3.0 / BR 4.0 renamed the headline attribute string from overall_att to att); not a reader" + ), + ("inner_max_iter", "docs/methodology/REGISTRY.md"): ( + "SyntheticControl inner-solve prose: the removed param's history (M-148; the pre-#838 Frank-Wolfe budget); not a reader" + ), + ("inner_min_decrease", "docs/methodology/REGISTRY.md"): ( + "SyntheticControl inner-solve prose: the removed param's history (M-149); not a reader" + ), + ("pvalue", "diff_diff/lwdid_results.py"): ( + "`self._ri_result.pvalue` relays RandomizationResult.pvalue (an independent field) through the ri_pvalue property; not the removed M-151 alias" + ), + ("pvalue", "docs/methodology/REGISTRY.md"): ( + "LWDiD results-contract prose naming the removed M-151 alias as history; not a reader" + ), + ("ci", "docs/methodology/REGISTRY.md"): ( + "LWDiD results-contract prose naming the removed M-152 alias as history, plus RDPlot's own `ci` confidence-level parameter; not a reader" + ), ("groups", "diff_diff/chaisemartin_dhaultfoeuille_results.py"): ( "__setstate__ legacy-key migration (M-114), kept" ), @@ -1173,9 +1224,6 @@ def _token_family_code_refs(tok): "fallback label string literal for a non-str control_group value, not a reader" ), ("clean_control", "diff_diff/power.py"): "historical comment on the 3.x default, not a reader", - ("placebo_effects", "diff_diff/_deprecation.py"): ( - "docstring names the M-003 precedent the helper's wording follows; not a reader" - ), ("trop", "diff_diff/trop.py"): ( "docstring example variable `trop = TROP()` (kwarg-lane noise), not the removed wrapper" ), diff --git a/tests/test_result_aliases.py b/tests/test_result_aliases.py index 594c948eb..ee93787c0 100644 --- a/tests/test_result_aliases.py +++ b/tests/test_result_aliases.py @@ -3,9 +3,10 @@ Since the 4.0 storage flip (ledger rows M-050..M-058, PR 5b) the canonical quintet ``att`` / ``se`` / ``t_stat`` / ``p_value`` / ``conf_int`` IS the native storage on every estimator container in this file, and the 3.x -``overall_*`` / ``overall_att_*`` names are read-only ``FutureWarning`` -properties (removed at 5.0). This file pins that direction: canonical names -are constructor keywords and dataclass fields, legacy names warn and relay. +``overall_*`` / ``overall_att_*`` names are GONE (their 4.0 -> 5.0 +FutureWarning shims were retired at 4.0 by item 9b of the cut). This file +pins that direction: canonical names are constructor keywords and dataclass +fields, legacy names are absent (``hasattr`` False). The last ``overall_*`` container, ``StaggeredTripleDiffResults``, was retired by the item-6 DDD container unification (row M-014): ``TripleDifferenceResults`` now serves both DDD designs with native canonical storage. @@ -19,6 +20,7 @@ from __future__ import annotations +import inspect import math from dataclasses import asdict, fields @@ -144,22 +146,6 @@ class _Probe: assert inst.x == 1.5 -def _assert_legacy_aliases_relay(res, *, att, se, t_stat, p_value, conf_int): - """Pattern B post-flip: the five legacy ``overall_*`` properties warn and relay - the canonical fields.""" - cls = type(res).__name__ - for old, expected in ( - ("overall_att", att), - ("overall_se", se), - ("overall_t_stat", t_stat), - ("overall_p_value", p_value), - ("overall_conf_int", conf_int), - ): - with pytest.warns(FutureWarning, match=rf"{cls}\.{old} is deprecated"): - got = getattr(res, old) - assert _alias_equal(got, expected), f"{cls}.{old} relays {got!r}, expected {expected!r}" - - # Sentinel inference values exercised across direct-construction tests. _ATT = 1.5 _SE = 0.3 @@ -198,34 +184,23 @@ def _assert_legacy_aliases_relay(res, *, att, se, t_stat, p_value, conf_int): @pytest.mark.parametrize("cls", FLIPPED_PATTERN_B, ids=lambda c: c.__name__) -def test_pattern_b_legacy_aliases_relay_canonical(cls): +def test_pattern_b_canonical_keywords_and_no_legacy_names(cls): """Each flipped class constructs with canonical keywords; the legacy - ``overall_*`` properties warn (FutureWarning) and relay the stored value.""" + ``overall_*`` names are gone (removed at 4.0, item 9b).""" res = cls(**_required_init_kwargs(cls, dict(_CANONICAL))) assert res.att == _ATT assert res.se == _SE assert res.t_stat == _T assert res.p_value == _P assert res.conf_int == _CI - _assert_legacy_aliases_relay(res, att=_ATT, se=_SE, t_stat=_T, p_value=_P, conf_int=_CI) - - -@pytest.mark.parametrize("cls", FLIPPED_PATTERN_B, ids=lambda c: c.__name__) -def test_pattern_b_legacy_aliases_propagate_nan(cls): - """When the canonical fields are NaN (degenerate fit), the legacy aliases are NaN. - - Pins the safe_inference() joint-NaN contract (per CLAUDE.md: ALL inference - fields are computed together and stay NaN-consistent). Aliases are pure - read-throughs, so the contract holds without re-computation. - """ - res = cls(**_required_init_kwargs(cls, dict(_CANONICAL_NAN))) - with pytest.warns(FutureWarning): - assert math.isnan(res.overall_att) - assert math.isnan(res.overall_se) - assert math.isnan(res.overall_t_stat) - assert math.isnan(res.overall_p_value) - assert math.isnan(res.overall_conf_int[0]) - assert math.isnan(res.overall_conf_int[1]) + for old in ( + "overall_att", + "overall_se", + "overall_t_stat", + "overall_p_value", + "overall_conf_int", + ): + assert not hasattr(res, old), f"{cls.__name__}.{old} resurrected" @pytest.mark.parametrize("cls", FLIPPED_PATTERN_B, ids=lambda c: c.__name__) @@ -270,31 +245,6 @@ def test_continuous_did_canonical_fields_native(): assert res.overall_acrt == 0.0 # the separate ACRT estimand is untouched -@pytest.mark.parametrize( - ("old", "expected"), - [ - ("overall_att", _ATT), - ("overall_att_se", _SE), - ("overall_att_t_stat", _T), - ("overall_att_p_value", _P), - ("overall_att_conf_int", _CI), - ("overall_se", _SE), - ("overall_t_stat", _T), - ("overall_p_value", _P), - ("overall_conf_int", _CI), - ], -) -def test_continuous_did_legacy_spellings_warn_and_relay(old, expected): - """All nine 3.x spellings - the five former ``overall_att_*`` fields and the - four former silent ``overall_*`` aliases - warn and relay the canonical - field (the M-058 family).""" - res = ContinuousDiDResults( - **_required_init_kwargs(ContinuousDiDResults, _continuous_did_overrides()) - ) - with pytest.warns(FutureWarning, match=rf"ContinuousDiDResults\.{old} is deprecated"): - assert getattr(res, old) == expected - - # ============================================================================ # Read-only semantics # ============================================================================ @@ -326,21 +276,21 @@ def test_continuous_did_legacy_spellings_warn_and_relay(old, expected): ], ids=["CallawaySantAnnaResults", "ContinuousDiDResults"], ) -def test_legacy_aliases_are_read_only_canonical_fields_writable(cls, ovr, legacy_names): - """Post-flip read-only semantics are INVERTED from 3.x: assigning to a legacy - ``overall_*`` property raises AttributeError (no setter installed), while the - canonical quintet are plain dataclass fields and accept assignment (the +def test_legacy_names_absent_canonical_fields_writable(cls, ovr, legacy_names): + """The legacy ``overall_*`` / ``overall_att_*`` names are absent (neither + field nor descriptor; ``hasattr`` False), while the canonical quintet are + plain dataclass fields and accept assignment (the ``WooldridgeDiDResults.aggregate("simple")`` write path relies on this). - Regression lineage: a downstream test in tests/test_practitioner.py once - used `r.overall_se = X` on a `ContinuousDiDResults.__new__()` mock. Locking - the direction here means fixtures that assign the wrong name fail loudly - via this test rather than deep in another suite. + Note: with the descriptors gone, assigning a legacy name creates a shadow + instance attribute on these non-slotted dataclasses instead of raising - + the former "wrong name fails loudly" guard was retired with the shims at + item 9b; the naming guard's consumer sweep is the in-repo defense. """ res = cls(**_required_init_kwargs(cls, ovr)) for name in legacy_names: - with pytest.raises(AttributeError): - setattr(res, name, object()) + assert inspect.getattr_static(cls, name, None) is None, f"{cls.__name__}.{name}" + assert not hasattr(res, name), f"{cls.__name__}.{name}" for name in ("att", "se", "conf_int", "p_value", "t_stat"): setattr(res, name, 7.0) assert getattr(res, name) == 7.0 @@ -430,8 +380,7 @@ def test_balance_adapter_pattern_returns_populated_se(): conf_int is not None and len(conf_int) == 2 and all(np.isfinite(x) for x in conf_int) ), f"balance adapter would see conf_int={conf_int!r}; pre-alias bug returned None" assert p_value is not None and np.isfinite(p_value) - # The legacy overall_* properties relay the same stored values (and warn). - with pytest.warns(FutureWarning): - assert se == res.overall_se - assert conf_int == res.overall_conf_int - assert p_value == res.overall_p_value + # The legacy overall_* names are gone (removed at 4.0, item 9b). + assert not hasattr(res, "overall_se") + assert not hasattr(res, "overall_conf_int") + assert not hasattr(res, "overall_p_value") diff --git a/tests/test_v4_matrix.py b/tests/test_v4_matrix.py index ecb77d101..4780e8bdd 100644 --- a/tests/test_v4_matrix.py +++ b/tests/test_v4_matrix.py @@ -143,11 +143,12 @@ # to_dataframe level twin (M-150, 4.0-cut framing) = 132; + the LWDiD # results-contract rows (M-151/M-152 alias deprecations, M-153 to_dataframe # reshape; 4.0-cut PR 1) = 135; + the 7b cluster-policy pair (M-154 SpilloverDiD -# df_convention, shipped 7c; M-155 the shared cluster contract) = 137. +# df_convention, shipped 7c; M-155 the shared cluster contract) = 137; + the +# placebo-wrapper calendar-contract pair (M-156/M-157, 4.0-cut item 9b) = 139. # Ids are never reused and terminal rows are never deleted, so the ledger # only grows - raise the floor when rows are added; a lower parse count # means scanner/format drift or an illegal row deletion. -ROW_COUNT_FLOOR = 137 +ROW_COUNT_FLOOR = 139 # Committed snapshot of the shipped id set ("ids are never deleted or reused" # contract - a delete-one-add-one edit keeps the count above the floor but trips @@ -207,6 +208,7 @@ (148, 149), (150, 150), (151, 155), + (156, 157), ] EXPECTED_INITIAL_IDS = frozenset( f"M-{n:03d}" for lo, hi in _INITIAL_ID_RANGES for n in range(lo, hi + 1) @@ -714,7 +716,7 @@ def test_initial_ids_never_deleted(): 4.0-cut PR 1) + M-154/M-155 (the 7b cluster-policy pair)).""" missing = sorted(EXPECTED_INITIAL_IDS - set(_ROW_IDS)) assert not missing, f"ledger rows deleted (ids are permanent): {missing}" - assert len(EXPECTED_INITIAL_IDS) == 137 + assert len(EXPECTED_INITIAL_IDS) == 139 def test_version_tuple_pads_to_three_components(): @@ -1042,8 +1044,9 @@ def test_ledger_header_declares_cut_window(): """The 4.0 cut window is declared (spec section 11): it names a version some row is scheduled at, and while the window is OPEN (``__version__`` below it) ``cut_base`` must equal the shipped ``__version__`` - the off-base guard's reference point. Once ``__version__`` - reaches the window the keys are a historical record (rows scheduled past the window, e.g. the - 5.0 field-alias removals, legitimately stay non-terminal, so nothing is asserted then).""" + reaches the window the keys are a historical record (a row scheduled past the window would + legitimately stay non-terminal, so nothing is asserted then; since item 9b retired the 5.0 + field-alias window no such row exists).""" import diff_diff assert CUT_WINDOW == "4.0" @@ -1367,8 +1370,9 @@ def _changes_at_4_0(row): """Does this row require reader action at the 4.0 cut? Keyed on the two LIFECYCLE version fields only - a symbol removed at 4.0, or one - whose deprecation warning starts firing at 4.0 (the ``field-flip`` family, removed - at 5.0). 113 of the 137 rows qualify. + whose deprecation warning starts firing at 4.0 (behavior rows such as the placebo-wrapper + calendar contract M-156/M-157; the ``field-flip`` family was removed at 4.0 by item 9b and + qualifies through ``removed_in``). 115 of the 139 rows qualify. The 24 that do not, and why (this enumeration is the contract - a reader of the guide must be able to trust that nothing 4.0-relevant was dropped): diff --git a/tests/test_v4_merge_mpd.py b/tests/test_v4_merge_mpd.py index 8b3b1f6f7..b2e2ef404 100644 --- a/tests/test_v4_merge_mpd.py +++ b/tests/test_v4_merge_mpd.py @@ -6,8 +6,8 @@ v4-design section 4.1 gate triple, the mode/rename validation surfaces, the day-one auto-cluster carve-outs (user decision 2026-08-07), the 4.0 removal pins (M-010/M-011/M-012/M-060: the names are gone; M-007: the legacy -reference-period warning is retired; M-016: ``period_effects`` is a -FutureWarning view on the surface), the unified surface contract, warning +reference-period warning is retired; M-016: ``period_effects`` is gone - +its 4.0 -> 5.0 view was retired at 4.0 by item 9b), the unified surface contract, warning attribution, and the consumer ports. Through 3.x the MultiPeriodDiD class was the live twin of every lane here; those parity pins moved to the committed pre-refactor oracle in ``tests/test_twfe_event_study_decoupling.py``. @@ -42,21 +42,14 @@ from diff_diff.pretrends import compute_pretrends_power from diff_diff.results_base import ( EventStudyResults, - _PeriodEffectView, build_event_study_surface, ) -from diff_diff.visualization import plot_event_study, plot_honest_event_study +from diff_diff.visualization import plot_event_study # --------------------------------------------------------------------------- # Pinned messages (the deprecation/validation contract) # --------------------------------------------------------------------------- -PERIOD_EFFECTS_MSG = ( - "EventStudyResults.period_effects is deprecated and will be removed in " - "5.0; use to_dataframe() (one row per event_time, reference row included) " - "or the columnar att/se/t_stat/p_value/conf_int_lower/conf_int_upper " - "arrays instead." -) REMOVED_NAMES = ("MultiPeriodDiD", "MultiPeriodDiDResults", "PeriodEffect", "EventStudy") STATIC_TIME_MSG = ( "TwoWayFixedEffects.fit: in static mode (event_study=False) time= is " @@ -646,8 +639,8 @@ def spy(X, cluster_ids, bread, contrasts, **kw): assert np.allclose(anchor[np.nonzero(anchor)], 1.0 / n_post) -class TestPeriodEffectsView: - """M-016: ``period_effects`` is a FutureWarning view on calendar surfaces.""" +class TestPeriodEffectsRemoved: + """M-016: ``period_effects`` is gone (the 4.0 -> 5.0 view was retired at 4.0, item 9b).""" def _fit(self, panel, alpha=0.05): return TwoWayFixedEffects(alpha=alpha).fit( @@ -660,187 +653,36 @@ def _fit(self, panel, alpha=0.05): post_periods=_post(), ) - def test_is_a_property_not_a_field(self): + def test_period_effects_is_gone_on_calendar_surfaces(self, panel): import dataclasses import inspect assert "period_effects" not in {f.name for f in dataclasses.fields(EventStudyResults)} - assert isinstance(inspect.getattr_static(EventStudyResults, "period_effects"), property) - - def test_warns_once_and_matches_the_surface_rowwise(self, panel): + assert inspect.getattr_static(EventStudyResults, "period_effects", None) is None s = self._fit(panel) + assert s.time_scale == "calendar" with warnings.catch_warnings(record=True) as rec: warnings.simplefilter("always") - pe = s.period_effects - fw = [w for w in rec if issubclass(w.category, FutureWarning)] - assert len(fw) == 1 and str(fw[0].message) == PERIOD_EFFECTS_MSG - labels = [lab for lab, ref in zip(s.event_time.tolist(), s.is_reference) if not ref] - assert list(pe) == labels - for i, lab in enumerate(s.event_time.tolist()): - if s.is_reference[i]: - assert lab not in pe - continue - rec_ = pe[lab] - assert isinstance(rec_, _PeriodEffectView) - assert rec_.period == lab - assert rec_.effect == float(s.att[i]) and rec_.se == float(s.se[i]) - assert rec_.t_stat == float(s.t_stat[i]) and rec_.p_value == float(s.p_value[i]) - assert rec_.conf_int == (float(s.conf_int_lower[i]), float(s.conf_int_upper[i])) - assert rec_.is_significant == bool(s.p_value[i] < s.alpha) - # the reference row is omitted, as the pre-4.0 dict did - assert len(pe) == int((~s.is_reference).sum()) - - def test_is_significant_uses_the_surface_alpha(self, panel): - s10 = self._fit(panel, alpha=0.10) - with warnings.catch_warnings(): - warnings.simplefilter("ignore", FutureWarning) - pe = s10.period_effects - ps = {lab: r.p_value for lab, r in pe.items()} - for lab, r in pe.items(): - assert r.is_significant == (ps[lab] < 0.10) - # a row between 0.05 and 0.10 is significant at the surface's alpha - # but would not have been at the removed record's hard-coded 0.05 - - # the pin is on the rule, so just assert the rule held for every row - assert all(r.is_significant == (r.p_value < 0.10) for r in pe.values()) - - def test_significance_stars_thresholds(self): - rows = {1: (1.0, 0.1), 2: (1.0, 0.2), 4: (1.0, 0.5), 5: (0.001, 1.0), 6: (0.05, 1.0)} - s = calendar_surface(rows, reference=3, post_periods=[4, 5, 6]) - with warnings.catch_warnings(): - warnings.simplefilter("ignore", FutureWarning) - pe = s.period_effects - from diff_diff.results_base import _significance_stars - - for lab, r in pe.items(): - assert r.significance_stars == _significance_stars(r.p_value) - assert _significance_stars(np.nan) == "" - assert _significance_stars(0.0005) == "***" - assert _significance_stars(0.005) == "**" - assert _significance_stars(0.03) == "*" - assert _significance_stars(0.07) == "." - assert _significance_stars(0.5) == "" - - def test_nan_rows_stay_nan_records(self): - rows = {1: (0.1, 0.2), 2: (np.nan, np.nan), 4: (0.5, 0.1)} - s = calendar_surface(rows, reference=3, post_periods=[4]) - with warnings.catch_warnings(): - warnings.simplefilter("ignore", FutureWarning) - r = s.period_effects[2] - assert np.isnan(r.effect) and np.isnan(r.p_value) - assert r.is_significant is False and r.significance_stars == "" - - def test_datetime_labels_survive_as_keys(self): - import dataclasses - - rows = {1: (0.1, 0.2), 2: (0.2, 0.2), 4: (0.5, 0.1)} - base = calendar_surface(rows, reference=3, post_periods=[4]) - # object-dtype Timestamps are what a datetime time column produces - # (the surface keeps the native dtype; a datetime64[ns] array would - # be flattened to integer nanoseconds by .tolist(), which the view - # avoids by iterating the array itself) - stamps = np.array([pd.Timestamp(f"2020-0{m}-01") for m in (1, 2, 3, 4)], dtype=object) - s = dataclasses.replace( - base, - event_time=stamps, - vcov=None, - vcov_index=None, - reference_period=stamps[2], - post_periods=(stamps[3],), - ) - with warnings.catch_warnings(): - warnings.simplefilter("ignore", FutureWarning) - pe = s.period_effects - assert set(pe) == {stamps[0], stamps[1], stamps[3]} - assert all(isinstance(k, pd.Timestamp) for k in pe) - assert pe[pd.Timestamp("2020-04-01")].effect == 0.5 - - def test_duplicate_labels_fail_closed(self): - import dataclasses - - rows = {1: (0.1, 0.2), 2: (0.2, 0.2), 4: (0.5, 0.1)} - base = calendar_surface(rows, reference=3, post_periods=[4]) - dup = dataclasses.replace( - base, event_time=np.array([1, 1, 3, 4]), vcov=None, vcov_index=None - ) - with pytest.raises(ValueError, match="duplicate event_time labels"): - dup.period_effects - # a label shared by the reference row and an estimated row is rejected - # too (it would surface the estimate under the reference's key) - ref_dup = dataclasses.replace( - base, event_time=np.array([1, 3, 3, 4]), vcov=None, vcov_index=None - ) - with pytest.raises(ValueError, match="duplicate event_time labels"): - ref_dup.period_effects + assert not hasattr(s, "period_effects") + with pytest.raises(AttributeError): + s.period_effects + assert not [w for w in rec if issubclass(w.category, FutureWarning)] + # the successor is the surface itself + frame = s.to_dataframe() + assert len(frame) == len(s.event_time) - def test_relative_surfaces_do_not_carry_the_view(self): - """Gated to calendar surfaces: a relative-time aggregate('event_study') - container never had period_effects, so hasattr is False and nothing - warns there.""" + def test_period_effects_is_gone_on_relative_surfaces(self): data = generate_staggered_data(n_units=40, n_periods=6, seed=3) cs = CallawaySantAnna().fit( data, outcome="outcome", unit="unit", time="period", first_treat="first_treat" ) es = cs.aggregate("event_study") assert isinstance(es, EventStudyResults) and es.time_scale == "relative" - with warnings.catch_warnings(record=True) as rec: - warnings.simplefilter("always") - assert not hasattr(es, "period_effects") - with pytest.raises(AttributeError, match="time_scale='calendar'"): - es.period_effects - assert not [w for w in rec if issubclass(w.category, FutureWarning)] + assert not hasattr(es, "period_effects") - def test_hasattr_fires_the_view_on_calendar_surfaces(self, panel): - """Documents the hazard: any new duck sniff must guard with isinstance - first, because hasattr runs the getter (and warns).""" + def test_builder_passthrough(self, panel): s = self._fit(panel) - with warnings.catch_warnings(record=True) as rec: - warnings.simplefilter("always") - assert hasattr(s, "period_effects") - assert [w for w in rec if "period_effects" in str(w.message)] - - def test_builder_passthrough_never_fires_the_view(self, panel): - s = self._fit(panel) - with warnings.catch_warnings(record=True) as rec: - warnings.simplefilter("always") - assert build_event_study_surface(s) is s - assert not [w for w in rec if "period_effects" in str(w.message)] - - def test_no_consumer_path_fires_the_view(self, panel): - """The view-never-fires sweep: every surface-consuming path stays - duck-sniff free (scoped to the view's own FutureWarning, so legitimate - UserWarnings or matplotlib/scipy warnings do not fail it).""" - # matplotlib is an optional dependency (absent on the CI test legs): - # the plotting half of the sweep runs only when it is importable; - # the rest of the sweep always runs. - try: - import matplotlib - - matplotlib.use("Agg") - import matplotlib.pyplot as plt - except ImportError: # pragma: no cover - depends on the environment - plt = None - - s = self._fit(panel) - with warnings.catch_warnings(record=True) as rec: - warnings.simplefilter("always") - s.to_dataframe() - s.to_dict() - s.summary() - build_event_study_surface(s) - h = compute_honest_did(s, M=1.0) - compute_pretrends_power(s, M=0.3, pretest_form="wald") - if plt is not None: - ax = plot_event_study(s) - plt.close("all") - plot_honest_event_study(h) # original_results rides on h - plt.close("all") - assert ax is not None - offenders = [ - w - for w in rec - if issubclass(w.category, FutureWarning) and "period_effects" in str(w.message) - ] - assert offenders == [] + assert build_event_study_surface(s) is s # --------------------------------------------------------------------------- @@ -1501,7 +1343,6 @@ def test_plot_partition_aware_shading_both_renderers(self, panel): matplotlib.use("Agg") import matplotlib.pyplot as plt - from diff_diff.visualization import plot_event_study from diff_diff.visualization._event_study import _pre_shading_runs assert _pre_shading_runs([0, 1, 3]) == [(0, 1), (3, 3)] diff --git a/tests/test_v4_rename_mechanical.py b/tests/test_v4_rename_mechanical.py index 04c9cd0c6..0f56687cf 100644 --- a/tests/test_v4_rename_mechanical.py +++ b/tests/test_v4_rename_mechanical.py @@ -15,28 +15,21 @@ and pickles from the old field name still migrate via ``__setstate__`` (spec section 5: 3.x pickles load under 4.0). -``diff_diff._deprecation`` itself survives (it still serves M-031/M-082 and -the PR-3 rows), so ``TestDeprecationHelpers`` stays. +``diff_diff._deprecation`` itself survives as the pickle-migration and +required-arg helper module (the kwarg-rename and field-property helpers were +deleted at item 9b with their last consumers), so ``TestDeprecationHelpers`` +covers the sentinel and ``require_arg`` only. """ import copy import pickle import warnings -from dataclasses import dataclass, fields import numpy as np import pandas as pd import pytest -from diff_diff._deprecation import ( - NOT_SUPPLIED, - _NotSupplied, - deprecated_field_property, - deprecated_kwarg_message, - require_arg, - resolve_renamed_kwarg, - warn_deprecated_kwarg, -) +from diff_diff._deprecation import NOT_SUPPLIED, _NotSupplied, require_arg def _assert_no_future_warning(record): @@ -71,90 +64,11 @@ def test_deepcopy_params_preserves_sentinel_and_isolates_mutable_values(self): cloned["nested"][1].append("changed") assert params["nested"][1] == [] - def test_message_template(self): - msg = deprecated_kwarg_message("Cls.fit", "old", "use new= instead") - assert msg == ( - "Cls.fit(old=) is deprecated and will be removed in 4.0; " "use new= instead." - ) - - def test_warn_deprecated_kwarg_emits_future_warning(self): - # Retained 5.0-window helper (spec section 8 rule 11): its last library - # caller (dCDH fit(aggregate=), M-026) was removed at 4.0, so this pin - # keeps the helper itself under test. - with pytest.warns(FutureWarning, match=r"Cls\.fit\(old=\) is deprecated") as rec: - warn_deprecated_kwarg("Cls.fit", "old", "use new= instead", stacklevel=2) - assert str(rec[0].message) == deprecated_kwarg_message("Cls.fit", "old", "use new= instead") - - def test_resolve_neither_returns_default(self): - with warnings.catch_warnings(): - warnings.simplefilter("error") - out = resolve_renamed_kwarg( - "Cls.fit", "old", NOT_SUPPLIED, "new", NOT_SUPPLIED, default="d" - ) - assert out == "d" - - def test_resolve_new_silent(self): - with warnings.catch_warnings(): - warnings.simplefilter("error") - out = resolve_renamed_kwarg("Cls.fit", "old", NOT_SUPPLIED, "new", "value") - assert out == "value" - - def test_resolve_old_warns_and_maps(self): - with pytest.warns(FutureWarning, match=r"Cls\.fit\(old=\) is deprecated"): - out = resolve_renamed_kwarg("Cls.fit", "old", "value", "new", NOT_SUPPLIED) - assert out == "value" - - def test_resolve_old_extra_appended(self): - with pytest.warns(FutureWarning, match="calendar column"): - resolve_renamed_kwarg( - "Cls.fit", - "old", - "value", - "new", - NOT_SUPPLIED, - extra="From 4.0, old= means the calendar column only.", - ) - - def test_resolve_both_raises(self): - with pytest.raises(ValueError, match=r"pass only new="): - resolve_renamed_kwarg("Cls.fit", "old", "a", "new", "b") - def test_require_arg(self): require_arg("Cls.fit", "new", "value") # no raise with pytest.raises(TypeError, match=r"missing required argument: 'new'"): require_arg("Cls.fit", "new", NOT_SUPPLIED) - def test_deprecated_field_property_shape(self): - @dataclass - class Toy: - new_name: int = 3 - old_name = deprecated_field_property("Toy", "old_name", "new_name") - - toy = Toy() - assert "new_name" in {f.name for f in fields(toy)} - assert "old_name" not in {f.name for f in fields(toy)} - with pytest.warns(FutureWarning, match=r"Toy\.old_name is deprecated"): - assert toy.old_name == 3 - with pytest.raises(AttributeError): - toy.old_name = 5 - # default removal version is the 3.9 sweep's 4.0, in both the warning and the docstring - with pytest.warns(FutureWarning, match=r"Will be removed in 4\.0\."): - toy.old_name - assert "(removed in 4.0)" in Toy.old_name.__doc__ - - def test_deprecated_field_property_removed_in_override(self): - """Aliases deprecated AT 4.0 (M-151/M-152) name their 5.0 removal in both surfaces.""" - - @dataclass - class Toy: - conf_int: int = 7 - ci = deprecated_field_property("Toy", "ci", "conf_int", removed_in="5.0") - - toy = Toy() - with pytest.warns(FutureWarning, match=r"Toy\.ci is deprecated.*Will be removed in 5\.0\."): - assert toy.ci == 7 - assert Toy.ci.__doc__ == "Deprecated alias for :attr:`conf_int` (removed in 5.0)." - # --------------------------------------------------------------------------- # M-032: WooldridgeDiD.fit cohort= -> first_treat= diff --git a/tests/test_v4_rename_semantic.py b/tests/test_v4_rename_semantic.py index c29cb6b3d..cf92bcb4a 100644 --- a/tests/test_v4_rename_semantic.py +++ b/tests/test_v4_rename_semantic.py @@ -211,9 +211,10 @@ def test_wrapper_paths_silent_and_no_error_dicts(self, placebo_panel): placebo_panel, "y", "d", - "p", + "p", # the two-period calendar column (M-156: never a dummy) unit="u", test_type="permutation", + post_periods=[1], n_permutations=30, seed=2, ) diff --git a/tests/test_v4_storage_flip.py b/tests/test_v4_storage_flip.py index fbd94d6d9..fceab8b3d 100644 --- a/tests/test_v4_storage_flip.py +++ b/tests/test_v4_storage_flip.py @@ -9,11 +9,14 @@ Contract pinned here, per class: 1. the canonical quintet ``att`` / ``se`` / ``t_stat`` / ``p_value`` / - ``conf_int`` are dataclass fields; every legacy name is a plain - descriptor (``deprecated_field_property``), not a field; -2. reading a legacy name emits the pinned ``FutureWarning`` text naming the - class (the subclass for ``DMLDiDResults``) and relays the canonical value; - assigning to it raises ``AttributeError``; + ``conf_int`` are dataclass fields; +2. every legacy ``overall_*`` name is GONE (item 9b of the cut, 2026-11-01, + retired the 4.0 -> 5.0 FutureWarning shims at 4.0): it is neither a field + nor a descriptor and ``hasattr`` is False. Assigning a legacy name to an + instance now creates a plain shadow attribute (the dataclasses are not + slotted) - the former "wrong name fails loudly" guard was retired with + the descriptors; the naming guard's consumer sweep is the remaining + defense against legacy-name writers in the repo; 3. a 3.x pickle state (legacy keys) loads through ``__setstate__`` into the canonical fields and no ``overall_*`` key lingers in ``__dict__``; 4. a 4.0 pickle round-trips with every field present and ``to_dict()`` emits @@ -78,8 +81,8 @@ class (the subclass for ``DMLDiDResults``) and relays the canonical value; CANONICAL = ("att", "se", "t_stat", "p_value", "conf_int") -# Legacy property name -> canonical field, per class (what WARNS). -PROPERTY_MAP = { +# Legacy name -> canonical field, per class (what is GONE at 4.0). +LEGACY_NAMES = { CallawaySantAnnaResults: dict(LEGACY_OVERALL_QUINTET), DMLDiDResults: dict(LEGACY_OVERALL_QUINTET), SunAbrahamResults: dict(LEGACY_OVERALL_QUINTET), @@ -98,9 +101,9 @@ class (the subclass for ``DMLDiDResults``) and relays the canonical value; if cls is ContinuousDiDResults else dict(LEGACY_OVERALL_QUINTET) ) - for cls in PROPERTY_MAP + for cls in LEGACY_NAMES } -CLASSES = list(PROPERTY_MAP) +CLASSES = list(LEGACY_NAMES) IDS = [c.__name__ for c in CLASSES] # ``to_dataframe(level=...)`` vocabulary per class (read from each container). @@ -257,51 +260,36 @@ def fits(): @pytest.mark.parametrize("cls", CLASSES, ids=IDS) -def test_canonical_quintet_are_fields_legacy_names_are_descriptors(cls): +def test_canonical_quintet_are_fields_legacy_names_are_gone(fits, cls): field_names = set(cls.__dataclass_fields__) for name in CANONICAL: assert name in field_names, f"{cls.__name__}.{name} is not a dataclass field" - for old in PROPERTY_MAP[cls]: + res = fits[cls] + for old in LEGACY_NAMES[cls]: assert old not in field_names, f"{cls.__name__}.{old} is still a dataclass field" - assert isinstance( - inspect.getattr_static(cls, old), property - ), f"{cls.__name__}.{old} does not resolve as a property descriptor" + assert ( + inspect.getattr_static(cls, old, None) is None + ), f"{cls.__name__}.{old} still resolves on the class (resurrected shim?)" + assert not hasattr(res, old), f"{cls.__name__}.{old} still readable on an instance" # The ACRT family on ContinuousDiD is a separate estimand and stays stored. if cls is ContinuousDiDResults: assert {"overall_acrt", "overall_acrt_se", "overall_acrt_conf_int"} <= field_names # --------------------------------------------------------------------------- -# 2. Warning + relay + read-only +# 2. DMLDiDResults inherits the storage (no DML-specific field, no shim) # --------------------------------------------------------------------------- -@pytest.mark.parametrize("cls", CLASSES, ids=IDS) -def test_legacy_properties_warn_relay_and_reject_assignment(fits, cls): - res = fits[cls] - assert type(res) is cls - for old, new in PROPERTY_MAP[cls].items(): - pattern = ( - rf"^{cls.__name__}\.{old} is deprecated; use {new} instead\. Will be removed in 5\.0\.$" - ) - with pytest.warns(FutureWarning, match=pattern): - got = getattr(res, old) - assert _nan_eq(got, getattr(res, new)), (cls.__name__, old) - with pytest.raises(AttributeError): - setattr(res, old, 1.0) - doc = inspect.getattr_static(cls, old).__doc__ or "" - assert "removed in 5.0" in doc, (cls.__name__, old, doc) - - -def test_dml_warning_names_the_subclass(fits): +def test_dml_inherits_canonical_storage_without_legacy_names(fits): res = fits[DMLDiDResults] - with pytest.warns(FutureWarning, match=r"^DMLDiDResults\.overall_att is deprecated"): - res.overall_att - # The storage is the inherited field set - no DML-specific field appeared. assert set(DMLDiDResults.__dataclass_fields__) >= set( CallawaySantAnnaResults.__dataclass_fields__ ) - assert "overall_att" not in DMLDiDResults.__dataclass_fields__ + for old in LEGACY_OVERALL_QUINTET: + assert old not in DMLDiDResults.__dataclass_fields__ + assert inspect.getattr_static(DMLDiDResults, old, None) is None + assert not hasattr(res, old) # --------------------------------------------------------------------------- @@ -335,8 +323,7 @@ def test_legacy_state_loads_into_canonical_fields(fits, cls): _assert_no_legacy_keys(restored, cls) if cls is StackedDiDResults: assert restored.control_group == res.control_group - with pytest.warns(FutureWarning): - assert _nan_eq(restored.overall_att, res.att) + assert not hasattr(restored, "overall_att") # ---------------------------------------------------------------------------