Skip to content

Commit 89da093

Browse files
committed
docs(lwdid): withdraw tutorial 27 from this PR (fresh notebook = tracked follow-up)
CI round 6 showed the tutorial's Walmart narrative needs redesign, not per-sentence repair: the 'common-timing' example is built on a fabricated shared 1986 onset (cohorts first treated as late as 1999), its executed outputs still compared the pooled 0.0373 contrast with the paper's staggered ATT(1) and translated it into 200-250 jobs, the Basker 150-300-hires range does not match the staggered estimate (0.0109 x 6,589 ~ 72 jobs, CI including zero), and the cohort-effects cell read the wrong result object. Five consecutive review rounds' only actionable findings were this notebook's prose. Withdrawn surfaces: docs/tutorials/27_lwdid.ipynb, its toctree entry + card in docs/tutorials/index.rst, and the api-rst seealso link. A replacement notebook is a tracked TODO.md follow-up (numbers-locked authoring workflow; staggered fit as the causal specification; any pooled contrast explicitly diagnostic). REGISTRY + CHANGELOG record the withdrawal; the estimator, docs, and test surfaces are unchanged.
1 parent 7b3328a commit 89da093

6 files changed

Lines changed: 6 additions & 2372 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
140140
`significant_05` for failed specs, full-frame pre-validation, unknown
141141
kwargs raise); staggered sample metadata counts contributing units;
142142
rank-deficient designs rebuild the influence function on kept columns.
143+
- Tutorial: the contribution's `27_lwdid.ipynb` is WITHDRAWN (its
144+
Walmart narrative was built on a fabricated common onset with jobs
145+
figures inconsistent with the staggered estimate; a fresh notebook
146+
is a tracked follow-up).
143147
- Shared surfaces: `hc3` escapes closed across siblings (DiD/MP-DiD
144148
`absorb=` now full-dummy-routes hc3 like hc2; TWFE no longer crashes
145149
misleadingly; SpilloverDiD rejects hc3 at construction with its own

TODO.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ generic sparse-FE, QR+SVD rank-detection redundancy, `check_finite` bypass — m
6868

6969
| Issue | Location | Origin | Effort | Priority |
7070
|-------|----------|--------|--------|----------|
71+
| Author a fresh LWDiD tutorial notebook (the contribution's tutorial 27 was WITHDRAWN from PR #588 during CI review: its Walmart empirical narrative was built on a fabricated common onset with jobs translations that did not match the staggered estimate, and per-sentence repair failed across 5 review rounds). Build via the numbers-locked workflow: prototype in scripts, lock the numbers, then assemble and execute once; teach the staggered fit as the causal specification and keep any pooled contrast explicitly diagnostic | `docs/tutorials/` | #588 | Medium |
7172
| LWDiD staggered fit recomputes cohort-wide work across surfaces: each cohort's rolling transformation is built once per `fit_staggered` cohort loop, but unit-level post summaries, control-eligibility sets, and the composite path's `ydot_by_cohort` are re-derived independently by the cell, aggregate, and `_composite_regression_aggregation` paths — runtime grows ~cohorts × panel size. Cache the per-cohort transformation + post summaries in one pass and reuse; add a many-cohort unbalanced-panel benchmark | `diff_diff/lwdid_staggered.py`, `diff_diff/lwdid.py` | #588 | Mid | Low |
7273
| Reuse the demeaner's factorized codes in `absorbed_fe_rank`/`absorbed_fe_cr1_k_increment` instead of re-factorizing: at 186k rows the rank helper adds ~1.9 ms per absorbed fit (7.7% of the fastest Rust-served TWFE fit) and the K_reference increment ~3.2 ms per clustered-hc1 absorbed fit (~13%; see `docs/performance-plan.md` "Component-aware absorbed-FE rank"), and the helpers and `demean_by_groups` factorize the same group columns. Threading the codes through the call sites halves the factorize work; the `connected_components` call itself is ~1.1 ms. Deliberately not done in the correctness PRs. | `diff_diff/utils.py` | #variance-inventory | Quick | Low |
7374
| `EfficientDiD` conditional path: the largest remaining O(n) stage is the sieve/nuisance construction outside the tiled pass (~9s at 10k). (The `_ridge_solve_weights` Python-prep shave landed 2026-07-07 — the `omega_stack[rest]` fancy-index copy and tail scatter are skipped when no row is zero-masked, byte-identical outputs; the `zero_mask` abs scan itself remains, needed for correctness.) | `efficient_did_covariates.py` | CS-scaling | Mid | Low |

docs/api/lwdid.rst

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -587,8 +587,6 @@ Restrictions
587587

588588
.. seealso::
589589

590-
:doc:`../tutorials/27_lwdid`
591-
Tutorial demonstrating the full LWDiD workflow on simulated and real data.
592590
:class:`~diff_diff.CallawaySantAnna`
593591
Propensity-score reweighting using long differences (Equation 4.13, LW 2025).
594592
:class:`~diff_diff.WooldridgeDiD`

docs/methodology/REGISTRY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2582,6 +2582,7 @@ Event-study/placebo transformations over ALL periods (Appendix D): demeaning (D.
25822582
- **Note (review round 13: scale-equilibrated influence bread, effective-rank guard):** the RA influence reconstruction inverts the COLUMN-EQUILIBRATED Gram and unscales (`(X'X)^{-1} = D^{-1}(Xs'Xs)^{-1}D^{-1}`) — the pre-fix raw-Gram pinv silently dropped low-scale directions at large covariate units, so cell ATT/SE (from the equilibrated `solve_ols`) were unit-invariant while every AGGREGATE SE/p/CI and the multiplier-bootstrap inputs were not (execution class: rescaling one covariate by 1e7 moved the overall SE from 0.128 to 0.028 with no warning). Aggregate-inference unit-invariance is pinned across the overall and event-study surfaces. The exact-inference small-sample guard uses the EFFECTIVE (equilibrated) design rank, so a redundant-column design with positive effective residual df fits while a genuinely saturated design still raises. docs/index.rst and the practitioner tree scope the heterogeneous-trends claim to `rolling='detrend'` and describe PSM as point-estimation-only.
25832583
- **Note (review round 14):** numeric TIME columns must be finite at the front door (`+/-Inf` previously passed the NaN check and raised a raw OverflowError in event-time arithmetic); datetime/Period/ordered-label time columns are unaffected. The Prop-99 api-docs passage no longer claims the exact-inference p-value "validates the normality assumption" (it tests the treatment-effect null under those assumptions; RI is the assumption-free robustness check).
25842584
- **Note (review round 16):** a staggered event row whose accepted SE is non-finite contributes NO column to the analytical event-study covariance (its influence is not stored and `compute_event_study_bands` filters defensively) — previously a NaN-inference row could expose a 0.0 covariance diagonal, presenting it as known without uncertainty (the common-timing path already guarded on finite SE). `robustness_pre_periods` honors `k_min=1` for demeaning (the former unconditional `max(k_min, 2)` silently dropped a valid one-pre-period spec) and rejects sub-minimum `k_min` for detrending explicitly. The degenerate all-NaN-transform early returns carry full fit provenance (`cluster_name`, `psm_config`).
2585+
- **Note (CI review rounds, tutorial withdrawal):** the contribution's tutorial (`27_lwdid.ipynb`) was WITHDRAWN from the PR after five CI review rounds whose only remaining findings were its empirical narrative — the Walmart "common-timing" example fabricated a shared 1986 onset for cohorts first treated as late as 1999 and translated the pooled contrast into hires figures inconsistent with the staggered estimate (0.0109 × 6,589 ≈ 72 jobs, CI including zero, vs a claimed 150–300). Earlier tutorial-related clauses in the round notes above are historical. A replacement notebook is a tracked follow-up (TODO.md) using the numbers-locked authoring workflow.
25852586
- **Note (review round 17):** the RA interaction gate, the small-sample effective-rank guard, and the post-fit replay mirror all use the SHARED solver's pivoted-QR rank detector (`_detect_rank_deficiency`, scale-invariant 1e-7 convention) — `np.linalg.matrix_rank`'s looser default tolerance previously disagreed with the solver on NEAR-collinear controls (`x2 = x + 1e-10`), so the gate could count a direction the solver drops, turn the interacted design off, and change the ATT relative to the identified single-control fit. Near-collinear invariance + replay coherence pinned.
25862587
- **Note (review round 18):** the common-timing time-scale contract (Period rejected for detrend/detrendq; trend/seasonal transforms require numeric/datetime/Period time) lives in one shared validator called by BOTH `fit()` and `get_transformation_diagnostics()` (diagnostics previously reached the transforms' raw float-conversion errors). `randomization_inference` validates array shapes/lengths BEFORE the non-finite-outcome filter (a mismatched length combined with a non-finite y previously raised a raw boolean-index IndexError). RI citations point at the LW 2026 small-sample paper (the 2025 Section-5 reference concerned detrending, not RI), and the api-docs no longer call RI "assumption-free" (it does not require normality, conditional on the complete-randomization assignment mechanism).
25872588
- **Note (review round 19: family-consistent multiplier contributions — deliberate, externally validated):** the influence contributions feeding the event-study multiplier bootstrap are NORMALIZED TO THE REQUESTED ANALYTICAL VARIANCE FAMILY (classical: per-cell scalar rescale to the classical magnitude; hc1/CR1: the small-sample factor; hc2/hc3: leverage adjustment), not the raw Appendix E.2 contributions. Consequences: per-cell SCALAR adjustments (classical/hc1/CR1) leave the sup-t critical value INVARIANT (draws and SEs scale together and the normalized statistic cancels the factor) while the per-event bootstrap SEs report magnitudes consistent with the requested family rather than the raw asymptotic form — a deliberate coherence choice, so a fit's analytical and bootstrap surfaces answer in the same family. External validation: the RA/hc1 configuration's multiplier-bootstrap SEs are gated against the AUTHORS' Stata package's high-B multiplier bootstrap within the Monte-Carlo bound (acceptance suite, `test_walmart_eventstudy_se_vs_stata`). PSM continues under a rank-deficient (finite-probability) propensity fit exactly like ipw/dr — matching needs only the probabilities — with the regression-point fail-closed fallback reserved for genuinely non-finite propensity fits.

0 commit comments

Comments
 (0)