This repository was archived by the owner on Nov 8, 2026. It is now read-only.
Repository navigation
Add experiment: how MOOP imputation methods change SPM poverty (CPS ASEC 2026) - #222
Merged
Merged
Conversation
Cross-fits six MOOP imputation methods (logit plus cell or OLS means, logit draw plus donor amounts, PMM within coverage groups, microimpute QRF and zero-inflated QRF) on held-out SPM units of the 2026 CPS ASEC and recomputes SPM poverty under each, with replicate-weight SEs. Property tests cover the invariants on synthetic data. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Final 5-fold run on the 2026 CPS ASEC (134,729 people, 55,401 SPM units). NOTE.md and TABLE.md render from results/results.json through NOTE.template.md, so every number in them comes from the run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The robustness subset now drops units where Census imputed any member's MOOP (I_MOOP 1-3) or Medicare premium (I_MCPREM 2), since SPM_MEDXPNS includes both. results.json records each person share. - The fold cache key now covers every microimpute source file, the versions of Python and the numerical packages, and a hash of the model inputs with their row keys. results.json takes its environment from the same dict. ensure_raw re-extracts any CSV whose size or CRC-32 differs from the pinned zip's member. - New tests cover the allocation flags, re-extraction, and cache misses on a changed environment, microimpute source, inputs or row keys. - The note describes PMM's log(1 + MOOP) regression, splits a long sentence, gives "we" the method steps, and states the new subset results. A rerun from an empty cache reproduced every other number. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GPT-6.1 Sol's review of 754e518 asked for two wording changes: drop the bold labels that open the PR description's results, and give the README's predictor sentence an actor. We make the README change and the same pass over the rest of the text: - README: the Data and Design bullets open with their sentences, and the predictor, cell-fallback, PMM and running text name who acts. The identity bullet names SPM_MEDXPNS and gives the poverty condition its own sentence. - NOTE: each method bullet introduces the method's name in a sentence instead of a bold label, and the reproduction sentence splits in two. - TABLE: render.py's source note, Table 2 caption and subset row name the actor and the weights. No number changes, and results.json stays as it was. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The test rebuilt the donor and recipient cell keys for every recipient and every finer level, about 250 row-wise pandas joins per example. Building each level's keys once per example keeps every assertion and cuts the test from 46 s to 2.6 s locally, and the suite from 52 s to 6.3 s. Assigners that skip the finest level, admit cells one donor short or shift pools between recipients still fail it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tors GPT-6.1 Sol's review of e283c4c found no computation or reproducibility defect and asked for three wording fixes, two of them in the PR description. In the note, María Juaristi now holds the verb for the microimpute paper's finding, which keeps its scope (lowest average quantile loss of five methods for imputing SCF net worth onto the CPS). A pass for the same pattern moves the verbs that the README gave to documents ("NOTE.md summarizes", "the data dictionary labels", "results.json records") to us, Census and run.py, drops "(git-ignored)" for "which git ignores", and replaces the note's "With drawn participation" with "When we draw participation". No number changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
We add
experiments/moop_spm, which measures how the choice of method for imputing medical out-of-pocket spending (MOOP) changes Supplemental Poverty Measure (SPM) poverty. The American Community Survey (ACS) does not ask about MOOP, so researchers who build an SPM on the ACS impute it. The 2026 Current Population Survey Annual Social and Economic Supplement (CPS ASEC) asks about MOOP, and its public-use file covers calendar-year 2025. We impute MOOP within that file and check each method against the file's own MOOP. We compare logit models with cell means, OLS means or donor draws, predictive mean matching (PMM), and microimpute's quantile regression forests, alone (QRF) and zero-inflated (ZI-QRF). We summarize the results inNOTE.mdand give the full tables inTABLE.md.Results
We use the file's 134,729 people in 55,401 SPM units and 53,344 households.
What the code does
cps_asec.pydownloads the Census zip, checks its pinned SHA-256 and re-extracts any CSV that differs from the zip. It merges household state and the 160 replicate weights and flags people whose MOOP or Medicare premium Census imputed. It then builds SPM-unit predictors an ACS-style file carries: ages, sex, disability, current coverage, cash income, family type, tenure and state.methods.pyimplements (a) logit plus cell or OLS means, (b) logit draws with donors, PMM within coverage groups and (c) microimputeQRFandZeroInflatedImputer. It also implements the decomposition variants that cross the participation rule with the amount rule.run.pyassigns whole households to five folds, imputes each fold from models it fits on the rest (ten draws for stochastic methods) and writesresults/results.json. It caches each fold under a key that covers the settings, the experiment's and microimpute's source files, package versions and the model inputs.evaluate.pyrecomputes person-level SPM poverty for each method and computes rates, replicate-weight SEs, gross flows and MOOP distributions.render.pywritesTABLE.mdandNOTE.mdfromresults.jsonand fails if a placeholder inNOTE.template.mdhas no value.Invariants
tests/test_moop_spm_experiment.pychecks these on synthetic data:SPM_POORfor every person.logit_draw_hotdeckandlogit_threshold_hotdeckimpute positive amounts only from the recipient's own cell pool.allocation_flagsmarks a person's MOOP as Census-imputed whenI_MOOPruns 1 to 3, and their Medicare premium whenI_MCPREMequals 2.run.pyrefits every fold.extract_verifiedreplaces any extracted CSV whose size or CRC-32 differs from the zip's member.On every run,
run.pyalso checks the identities that rebuildSPM_RESOURCES,SPM_POORandSPM_MEDXPNSfrom their components. It raises if any method leaves a unit without an imputation or imputes negative MOOP.Changes after review
GPT-6.1 Sol, reviewing
da0144c, asked for these changes, which we make in754e518:I_MCPREMof 2) out of the comparison among units with no imputed MOOP, sinceSPM_MEDXPNSincludes the premium. The subset falls from 52% to 44% of SPM units. Logit plus cell means' change there moves from +0.65 to +0.36, the OLS variant's from +0.57 to +0.26, PMM's from +0.56 to +0.48 and QRF's from +0.10 to +0.01. In every record,I_MOOPflags an imputation if and only ifI_PHIPVAL,I_POTCVALorI_PMEDVALdoes, so it already covers MOOP's three components.run.pyrefits every fold when any of them changes.Rerunning from an empty cache at
754e518, we reproduced every other number inresults.jsonto full precision.Reviewing
754e518, Sol found no correctness, claim or reproducibility problems. Sol asked us to drop the bold labels that opened the results above and to give the README's predictor sentence an actor. We make both changes in2a92477and rewrite the other label openers and reduced passives in the README, note and table. No number changes.Reviewing
e283c4c, Sol found no computation or reproducibility defect and asked for three prose fixes, which we make in12fd398. We now state the nearest-donor invariant as its test checks it, with matching distances instead of identical donors. In the note, we credit María Juaristi with the microimpute paper's finding, and in this section we drop a reduced passive. We also give the verb to a person or program wherever a document or commit held it, and narrow two more invariants to what their tests check.Reviewing
12fd398, Sol asked us to narrow the donor-draw and nearest-donor invariants to what their tests check and to name Sol as the actor in this section. We make those changes in this description, with no new commit, and Sol approved them in a follow-up review at the same head.Two microimpute behaviors we leave unchanged
ZeroInflatedImputer.fitpassesweight_colonly to the auxiliary imputer for non-numeric targets, so it fits numeric targets without weights. We fit every method without weights, so our results don't depend on it.QRF's stochastic predict maps each draw onto a 101-point quantile grid from 1/102 to 101/102, so no draw exceeds the 101/102 quantile of a unit's predicted distribution.Other changes
hypothesisto the dev extras for the property tests.🤖 Generated with Claude Code