US PUMA-anchored geography ladder (ACS/ASEC spine)#394
Merged
Conversation
Ports the UK OA ladder / US block ladder pattern to a PUMA-anchored ladder for the ACS/ASEC spine. The anchor is the 2020 PUMA — the finest geography the ACS PUMS publishes. An ACS record keeps its known PUMA; an ASEC record (state only) draws a PUMA within state proportional to 2020 PUMA population. Every coarser layer then derives from the PUMA by a population-weighted draw over the PUMA's block-population overlap: - congressional district (119th): (PUMA, CD) overlap - county: (PUMA, county) overlap - tract (behind assign_tract): (PUMA, tract) overlap, with county derived structurally from the drawn tract so the two never disagree Because 2020 blocks nest in 2020 tracts nest in 2020 PUMAs, all three overlap tables plus the anchor population come from one block pass joined to the Census 2020 tract-to-PUMA relationship file; each table conserves its PUMA's population exactly. Vintage discipline follows the geography-spine schema (vintage_policy: error) and the loader refuses any artifact missing a derived layer vintage or violating conservation. - us_runtime/puma_ladder_sources.py: parse_tract_to_puma_relationship + assemble_us_puma_ladder (pure, unit-tested) - us_runtime/puma_ladder.py: UsPumaLadder, load/assign/with_household, assignment summary, and a release gate (NYC-collapse check, the #34 form) - 35 tests: source parse/assemble + conservation, load refusals, deterministic + population-weighted ACS/ASEC assignment, tract flag, frame integration, summary, gate Refs #275, #205, #34, #354, #288. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
tools/build_us_puma_ladder_artifact.py downloads (and caches) the primary Census sources, runs one 2020-block pass, aggregates to the PUMA anchor plus the three overlap tables, writes one national NPZ, and self-checks it through load_us_puma_ladder before writing the provenance summary. It reuses the block ladder's block-population and 119th-CD parsers unchanged; the only source it adds is the small Census tract-to-PUMA relationship file. The committed manifest (us_puma_ladder.provenance.json) pins every source by URL + SHA-256 (tract-to-PUMA, the 119th-CD BEF, and all 51 P.L. 94-171 files), records the byte-reproducible artifact SHA-256, and the national validation totals. US_PUMA_LADDER.md is the human-readable recipe and wire-up note. National build (all 51 states) conserves exactly: 2,462 PUMAs, 331,449,281 people (the 2020 apportionment population), 436 congressional districts, 3,143 counties, 83,848 tract overlaps, from 5,769,942 populated blocks. The built NPZ lands in the gitignored build/ tree (no per-area files, not committed); it is byte-identical across runs. Refs #275, #354, #288. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
MaxGhenis
added a commit
that referenced
this pull request
Jul 12, 2026
Integrates the SNAP state surface (#256 #371 #376), the PUMA ladder (#394), the loader-test decouple (#392), and --no-latest (#397) with the campaign's 93 restored input families. Six builder conflict hunks + the us_runtime export merges resolved keeping BOTH lineages: main's snap_state_take_up stage/diagnostics/telemetry sequenced alongside the campaign's other_health_insurance and ssi_take_up stages. Full populace-build suite passes on the merged tree (exit 0, 1 cred skip). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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 join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
US PUMA-anchored geography ladder
Ports the UK output-area ladder (#354) to the US ACS/ASEC spine, the US analogue
of the block ladder (
geography_ladder.py). The anchor is the 2020 Public UseMicrodata Area — the finest geography the ACS PUMS publishes — so an ACS record
already knows its anchor, and every coarser geography derives from it. One
national dataset, filterable at any grain, never per-area files (#275).
The ladder
puma; the ladder keeps it.state_fips; the ladder draws a PUMA withinthat state proportional to 2020 PUMA population (the two-stage discipline of the
UK ladder: sample the anchor within the known marginal, then derive).
PUMA's block-population overlap with that layer:
(PUMA, CD)overlap(PUMA, county)overlapassign_tract, default off) —(PUMA, tract)overlap; whenassigned, the county derives structurally from the tract (
county = tract // 10**6)so the two never disagree.
Congressional district, county and state are the launch requirement; tract is
implemented but flagged off. Draws are consumed from one seeded generator in a
fixed sorted order (states, then PUMAs), so an assignment is reproducible from its
seed. The dense ACS-spine artifact is filtered to state / CD / county for localanalysis because every record carries all three.
PUMAs do not nest in congressional districts or counties, so each derived layer is
a genuine population-weighted draw over the PUMA's overlap distribution — not a
structural prefix as in the block ladder. That is the substantive difference from
the block ladder, and why the artifact is a set of overlap tables rather than one
row per anchor.
One block pass, exact conservation
2020 blocks nest in 2020 tracts nest in 2020 PUMAs, so
tract = block // 10**4,county = block // 10**10, andtract → PUMAis a lookup. Summing 2020 blockpopulations by
(PUMA, CD),(PUMA, county),(PUMA, tract)and by PUMA yieldsthe three overlap tables and the anchor population from a single block pass. Every
populated block contributes to exactly one PUMA, CD, county and tract, so each
overlap table conserves its PUMA's population exactly — the loader refuses an
artifact that violates conservation, omits a derived-layer vintage
(
vintage_policy: error, the #205 lesson), or carries an overlap PUMA absent fromthe anchor.
The
block → CDandblock → populationparsers are reused unchanged from theblock ladder (
block_ladder_sources.py, #288's lineage). The only source thisladder adds is the small Census tract-to-PUMA relationship file.
Sources (pinned: URL + SHA-256 in
us_puma_ladder.provenance.json)NationalCD119.txt)tools/build_us_puma_ladder_artifact.pydownloads/caches the sources, runs theblock pass, self-checks the artifact by loading it back through
load_us_puma_ladder, and records every source's URL + SHA-256, the artifactSHA-256, and the national totals in the committed manifest. Alignment with #288:
this reuses #288's Census-source lineage (same CD BEF and P.L. block populations)
rather than duplicating it; #288 remains the place the 117th→119th vintage
crosswalk lives.
National build (validation)
Built over all 51 states from the pinned sources, conserving exactly:
populated blocks
The built NPZ lands in the gitignored
build/tree (no per-area files, notcommitted) and is reproducible in ~46s via the one command in
US_PUMA_LADDER.md.Implemented vs flagged-off
congressional district, county, state; the loader, assignment, Frame wrapper,
provenance summary, and a release gate (structure, state consistency, and the
NYC-collapse check — the permanent form of the Fix
in_nycbefore enforcing no-formula exports #34in_nycregression).assign_tract=False): the tract rung. Theoverlap table ships in the artifact; assignment only draws it when the flag is
set, and then re-derives county from the tract for consistency.
Tests (35 new; 72 green including block-ladder regression)
width, and conflicting-PUMA refusals
vintage_policy: error),non-positive population, unsorted/duplicate PUMAs, non-conserving overlap,
overlap PUMA absent from anchor, CD/county/tract state mismatch
assignment: CD ~90/10 within a two-district PUMA, and the ASEC state→PUMA draw
matches the 1000/1500 population split within tolerance
CD stability across the tract flag; coverage and CD-vintage refusals
NYC-collapse / prefix / missing-column failures)
uv run ruff checkandruff format --checkclean.Wire-up (documented, not rewired)
load_us_puma_ladder→assign_us_puma_ladder/with_household_us_puma_ladder→
us_puma_ladder_gateis the drop-in for the ACS/ASEC spine's geography stage.Wiring it behind the country-spec geography-spine schema (a PUMA-anchored spine
method reading the built artifact from the release bundle) is the follow-up, kept
out of this change to stay reviewable — the same posture #354 took for the UK
ladder.
Refs #275, #205, #34, #354, #288.
🤖 Generated with Claude Code