feat: add covariate support to ChangesInChanges/QDiD (qte xformla parity)#688
Conversation
…ity) Port qte 1.3.1's xformla covariate branch exactly - the Melly-Santangelo (2015) quantile-regression conditional CiC in qte's simplified form: - covariates= fit kwarg (library convention) + DiD-style trailing formula terms; numeric covariates only (dummy-encode categoricals); formula plus any explicit column argument now raises (uniform strictness) - per-cell Koenker-Bassett quantile regressions via scipy linprog/HiGHS on qte's fixed internal seq(0.01, 0.99, 0.01) tau grid (hardcoded R doubles, pinned against the fixture); quantreg predict.rqs Fhat/Qhat step-function conventions ported verbatim; per-observation conditional-rank imputation (CiC: control-cell QRs; QDiD: three cells, own-cell ranks, additive form with qte's asymmetric type-7/type-1 quantile pair) - covariates travel through both bootstrap schemes with QR refits per replicate (qte bootiter parity); LP failure NaN-rows through the gate - conditional-envelope support diagnostic (MS Assumption 4) replaces the unconditional support warning; the eq. 17 interior-range guard is scoped to unconditional fits (q_lower/q_upper NaN under covariates); QDiD monotonicity check skipped (moot by construction); paired cell sort keeps (y, X) aligned while preserving all sorted-y invariants - fixtures: covariate scenarios + atol=0 qr_cases convention micro-fixtures (R coefficients, prediction matrices, Fhat/Qhat outputs), quantreg 6.1 pinned alongside qte 1.3.1; seeded SE blocks now generated with cores=1 (qte's default forked bootstrap is not reproducible even under set.seed) - parity is layered: conventions bit-exact conditioned on R inputs, the LP solver proven exactly optimal per tau (equal coefficients or equal check loss), end-to-end covariate results gated at documented exact-tie selection bounds (REGISTRY Deviation note; measured worst 9.3e-3 ATT / 8.1e-2 QTE, gates 2e-2 / 0.15) - docs sweep: REGISTRY covariates block with labeled Notes, api rst example (file added to doc-snippets allowlist), llms/llms-full, references.rst (Melly-Santangelo 2015 + Koenker-Bassett 1978 entries), choosing/r_ comparison, review-doc corrections (the earlier "no R parity target for covariate CiC" claim was wrong - qte's xformla branch is one), TODO row rescoped to the full MS estimator, stale ROADMAP row removed, CHANGELOG Unreleased bullet amended Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011hismLBFcUbUzvDRq8ruWb
The regenerated benchmarks/data/qte_golden.json is ~700KB of single-line generated JSON; with both diff sides it pushed the review prompt past the model's input limit and the review job died in 23s on PR #688. Extend the existing large-asset exclusion list (the file stays visible in --name-status; the reviewable artifact is benchmarks/R/generate_qte_golden.R). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011hismLBFcUbUzvDRq8ruWb
|
🔁 AI review rerun (requested by @igerber) Head SHA: Overall Assessment✅ Looks good — no unmitigated P0/P1 findings. Executive Summary
MethodologyFinding 1Severity: P3 Code QualityNo findings. PerformanceFinding 1Severity: P3 informational, tracked in MaintainabilityNo findings. Tech DebtFinding 1Severity: P3 informational, tracked in SecurityNo findings. Documentation/TestsFinding 1Severity: P2 Finding 2Severity: P3 |
…iers, micro consistency test - REGISTRY covariate Deviation note now states the audited committed gates (ATT 0.04 / QTE 0.25) with the randomized-audit evidence and sharpness floors, replacing the stale pre-audit numbers (P3) - choosing_estimator.rst + llms.txt qualify the monotone-transform invariance claim as unconditional-fits-only (the covariate QR branch is not equivariant) (P2) - new qr_cases test asserts the stored micro end-to-end qte outputs are consistent with composing R's stored imputations (R-vs-R, atol 1e-12) - the guard the generator comment promised (P3) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011hismLBFcUbUzvDRq8ruWb
|
🔁 AI review rerun (requested by @igerber) Head SHA: Overall Assessment✅ Looks good — no unmitigated P0/P1 findings. Executive Summary
MethodologyFinding 1Severity: P3 informational, documented deviation Code QualityNo findings. PerformanceFinding 1Severity: P3 informational, tracked in MaintainabilityNo findings. Tech DebtFinding 1Severity: P3 informational, tracked in SecurityNo findings. Documentation/TestsFinding 1Severity: P2 |
…ms.txt The compact QDiD one-liner still attached the footnote-21 warning to all fits; qualified as unconditional-only (covariate-path counterfactual quantile curve is monotone by construction) and mention covariates= support, matching the registry / API docs / llms-full. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011hismLBFcUbUzvDRq8ruWb
|
🔁 AI review rerun (requested by @igerber) Head SHA: Overall Assessment✅ Looks good — no unmitigated P0/P1 findings. Executive Summary
MethodologyFinding 1Severity: P3 informational, documented deviation Code QualityNo findings. PerformanceFinding 1Severity: P3 informational, tracked in MaintainabilityNo findings. Tech DebtFinding 1Severity: P3 informational, tracked in SecurityNo findings. Documentation/TestsFinding 1Severity: P3 informational |
…claims api rst intro, llms-full narrative, and the CHANGELOG bullet now carry the unconditional-fits-only qualifier on monotone-transform invariance, completing the claim-set sweep. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011hismLBFcUbUzvDRq8ruWb
|
🔁 AI review rerun (requested by @igerber) Head SHA: Overall Assessment ✅ Looks good — no unmitigated P0/P1 findings. Executive Summary
Methodology Finding 1 Code Quality No findings. Performance Finding 1 Maintainability No findings. Tech Debt Finding 1 Security No findings. Documentation/Tests Finding 1 |
…MS review doc The Implementation Notes relevance block, the version-gaps bullet, and the requirements-checklist header still described covariates as a future PR / claimed no R implementation exists; all now distinguish the shipped qte simplified branch from the full Melly-Santangelo estimator (which indeed still has no R reference). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011hismLBFcUbUzvDRq8ruWb
|
🔁 AI review rerun (requested by @igerber) Head SHA: Overall Assessment ✅ Looks good — no unmitigated P0/P1 findings. Executive Summary
Methodology Finding 1 Code Quality No findings. Performance Finding 1 Maintainability No findings. Tech Debt Finding 1 Security No findings. A secret-pattern scan only hit the expected GitHub Actions secret reference, not a committed secret. Documentation/Tests Finding 1 Finding 2 |
Summary
ChangesInChangesandQDiDby porting qte 1.3.1'sxformlabranch exactly - the Melly-Santangelo (2015) quantile-regression conditional CiC in qte's simplified form:covariates=[...]fit kwarg (library convention) plus DiD-style trailing formula terms; numeric covariates only (dummy-encode categoricals);formulacombined with any explicit column argument now raises (uniform strictness, documented as deliberately stricter thanDifferenceInDifferences).scipy.optimize.linprog/HiGHS) on qte's fixed internalseq(0.01, 0.99, 0.01)tau grid (hardcoded R doubles, pinned against the fixture); quantregpredict.rqsFhat/Qhatstep-function conventions ported verbatim; per-observation conditional-rank imputation (CiC: control-cell QRs; QDiD: three cells, own-cell ranks, additive form with qte's asymmetric type-7/type-1 quantile pair). Covariates travel through both bootstrap schemes with QR refits per replicate (qtebootiterparity); LP failures NaN-row through the existing gate.q_lower/q_upperNaN under covariates - the NaN-mask interaction is explicitly gated and lock-tested); QDiD's monotonicity warning is skipped under covariates (moot by construction); paired cell sort keeps(y, X)aligned while preserving every sorted-y invariant.qr_casesconvention micro-fixtures (R coefficient matrices, prediction matrices,Fhat/Qhatoutputs) added tobenchmarks/data/qte_golden.json; quantreg 6.1 pinned alongside qte 1.3.1; seeded SE blocks now generated withcores=1after discovering qte's default forked bootstrap is not reproducible even underset.seed- the generator is now byte-reproducible run-to-run.xformlabranch is one), TODO row rescoped to the full MS estimator, stale ROADMAP row removed, CHANGELOG Unreleased bullet amended.Methodology references (required if estimator / math changes)
xformlaparity route)docs/methodology/papers/athey-imbens-2006-review.md); Melly & Santangelo (2015) working paper (reviewed:docs/methodology/papers/melly-santangelo-2015-review.md); Koenker & Bassett (1978), Econometrica 46(1); reference implementationqte1.3.1 (CiC()/QDiD()xformlabranches) withquantreg6.1.docs/methodology/REGISTRY.md(ChangesInChanges section, covariates block): qte's simplified MS pipeline (fixed 99-tau grid, treated-PRE covariate integration, raw un-rearranged step functions) is canonical; NA handling is fit-level dropna-with-warning rather than qte's silentna.omit; scipy-HiGHS-vs-quantreg-br solver deviation with exact-tie selection semantics; QDiD's asymmetric Q7/Q1 quantile types ported verbatim. The full Melly-Santangelo estimator, discrete bounds, analytical SEs, and staggered designs remain documented-deferred (TODO rows).Validation
tests/test_changes_in_changes.py(covariate validation, formula grammar, diagnostics scoping, bootstrap gates, NaN-guard lock test, x11-invariance),tests/test_methodology_changes_in_changes.py(irrelevant-covariate consistency, compositional-confounding correction, constant-covariate order-statistic hand-check),tests/test_changes_in_changes_parity.py(layered parity:qr_casesconventions at atol=0 conditioned on R inputs, per-tau LP optimality via coefficient-or-loss equality, end-to-end covariate gates at tie-selection bounds with sharpness floors, covariate SE statistical parity, quantreg version pin),tests/test_doc_snippets.py(allowlist).Security / privacy
Generated with Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_011hismLBFcUbUzvDRq8ruWb