Repository navigation
Supply is_claimant_or_partner from the FRS adult table - #524
Merged
Merged
Conversation
An FRS benefit unit is one adult or a couple plus dependent children. The adult table holds the head (UPERSON 1) and any partner (UPERSON 2); any other adult forms and heads their own benefit unit. So adult-table membership is policyengine-uk's is_claimant_or_partner (#1896), which the model otherwise infers from ages and cannot get right for unflagged large-age-gap households (policyengine-uk#2039). - frs.py derives it next to is_parent and rejects a benefit unit without one or two adult records (none in FRS 2020-21, 2022-23, 2023-24 or 2024-25). - SPI records are one-person benefit units, so each is its claimant. - The transfer builder marks the tax-unit head and a joint filer's spouse; the checked-in enhanced_cps_2025.h5 gains the builder's column in place (every other column unchanged). - stack_datasets refuses to stack a person table without the role onto one with it. Cloning, subsampling, uprating and the SPI and CGT donor copies carry whole person tables, so the column follows them. - Tests: examples, Hypothesis properties over generated FRS-shaped households, stacking, cloning, SPI, transfer builder vs checked-in H5, and built-dataset invariants. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 2, 2026
- Built datasets: every person aged 20+ is a claimant or partner (FRS dependent children are 19 at most), so an omitted partner in a childless couple fails. - The synthetic create_frs smoke test asserts the column, so dropping the assignment now fails a unit test, not only CI's built-dataset test. - Changelog: stacking refuses to combine a person table that has the role with one that does not (two tables both without it still stack). Suggested by review 20261002-095728-ukdata-cp-review-r1 (APPROVE at 61f93ba). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 2, 2026
MaxGhenis
marked this pull request as ready for review
October 3, 2026 11:51
MaxGhenis
added a commit
that referenced
this pull request
Oct 6, 2026
Conflict resolved per runbook A4 step 13: datasets/frs.py keeps all three new functions, #490's derive_all_claimants_over_state_pension_age, #524's derive_is_claimant_or_partner_from_frs_microdata and #525's derive_uc_is_in_gainful_self_employment. #525's uv.lock edits auto-merged and are reset to main's in R. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Author
|
Queued in release PR #544 for the 10/8 uk-data batch. It lands only on Max's go (d833). |
21 of 52 tasks
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.
Supplies policyengine-uk's
is_claimant_or_partner(each benefit unit's single adult or couple) from the survey, so the model stops inferring it from ages. Follow-up to PolicyEngine/policyengine-uk#2039 (PolicyEngine/policyengine-uk#2040). Companion to issue #486, which proposes supplyingis_hbai_dependent_childfrom the FRS child table: every FRS person is in exactly one of the two tables.What changes
is_claimant_or_partnerdatasets/frs.py)datasets/spi.py)datasets/enhanced_cps.py)derive_is_claimant_or_partner_from_frs_microdatasits next toderive_is_parent_from_frs_microdata. It raises if a benefit unit has no adult record or more than two, so a future FRS release with a different structure fails the build instead of passing wrong roles.stack_datasetsrefuses to stack a person table without the column onto one with it.imputations/frs_only.py) only rewrites its curated list.storage/enhanced_cps_2025.h5gains the column in place, without a rebuild. A rebuild would also recalibrate its weights against today's targets and lock. The column is copied from the builder's own uncalibrated pass over the same source manifest, after checking person id, household, benefit unit and age line up for every row. Every other column and table is unchanged (checked withassert_frame_equal). Result: 13,074 one-adult and 15,458 two-adult benefit units; the role equals "aged 18+" for this source.policyengine-uk defines the variable from PolicyEngine/policyengine-uk#1896 (merged 2 October).
Simulation.build_from_multi_year_datasetsets every dataset column as an input for every simulated year and skips columns the installed release does not define, so the column is inert for earlier releases, including the 2.93.0 lock this repo builds with. The runs below confirm that policyengine-uk main returns the supplied roles exactly.Evidence that adult-table membership is the role
Raw FRS adult and child tables, four releases, counts of survey records only:
Every benefit unit has exactly one UPERSON 1 adult, no benefit unit lacks an adult record, and adult and child person ids never overlap. A child record can be UPERSON 2 (under a single head), so UPERSON 2 alone does not identify a partner; adult-table membership does.
Impact (real microsimulation runs)
A seeded, production-settings build of this branch (512 epochs, 1 OA clone; the same cached calibration-target downloads as the other sessions' main builds) was simulated with policyengine-uk main (3c48247e, which includes PolicyEngine/policyengine-uk#1896), for 2025 and 2026. Builds are not bit-reproducible across worktrees, so the comparison is made within the built file: the column as supplied vs the column dropped, so that the model infers it. Weights and every other input are identical. The build's own simulations run on the 2.93.0 lock, whose loader skips the column, so calibration does not see it.
is_claimant_or_partner, income tax and NI; benefit-unit UC, HB, Pension Credit, Child Benefit, CTC, WTC, IS, benefit cap andrelation_type; household CTR, net income, HBAI net income BHC/AHC, benefits, tax and BHC/AHC poverty. The same holds on Presume a much younger member is a flagged parent's child at any age policyengine-uk#2040's head (c18c836a9).Cells under 10 survey households are suppressed; SPI-synthetic and donor copies do not count toward the 10.
Tests
tests/test_claimant_or_partner.py:is_parentis a claimant or partner; a unit with no adult or a third adult is rejected;stack_datasetskeeps the roles (bool) and refuses a one-sided stack;make data): base and enhanced FRS have the column, 1-2 per unit, every benefit-unit head and everyis_parentamong them, all aged 16+, and everyone aged 20+ has it (FRS dependent children are 19 at most), so a dropped partner fails.test_clone_and_assign.py: roles follow each clone.test_spi_build.py: every SPI record is its unit's claimant.test_policybench_transfer.py: the head and a joint spouse; the checked-in H5 matches the builder on the first 500 households. Its leaf-input check now allows dataset-supplied roles, which the installed policyengine-uk may not define.devextra.uv.lockadds it and also corrects the lock's stale self-version (1.56.16 to 1.57.4, matchingpyproject.toml).create_frssmoke test (test_legacy_benefit_proxies.py) asserts the column. Mutation check: all 8 mutants killed by unit tests, including not setting the FRS column at all.Invariants
is_parentadult is a claimant or partner; dependent children never are.Merge
Merging releases data (push.yaml builds and uploads). Per the batched-release ruling, this lands with the next batch when Max says go, after CI and an independent review approve the head.
Follow-ups
person_ids from 11-person households (household_id * 10 + indexoverflows). This predates this PR and is filed separately.🤖 Generated with Claude Code