Repository navigation
Take population growth from the OBR's March 2026 long-term determinants, and re-derive the per-capita series from it - #2611
Conversation
ons.population was the March 2025 vintage while the per-capita series divided March 2026 numerators by it. Use the population row of the OBR's long-term economic determinants for the March 2026 EFO (ONS 2024-based principal projection after 2030-31) for 2023-2073, and the same OBR series from the EFO economy tables (Table 1.1 real GDP over Table 1.5 real GDP per person, at Q2) for 2021-2022, which sources the old 2021 value. Re-derive per_capita.gdp, mixed_income and non_labour_income for 2021-2030 as (1 + aggregate) / (1 + population) - 1 from the March 2026 EFO, and the petrol and diesel spending proxies, and add tests that recompute each from its source. Fixes #2383. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Aggregate impact on certified data: passBase Checks: 0 broken by this change, 1 failing on both sides, 0 fixed. Moves flagged for review: 356 (thresholds in Notes
Moves flagged for review
316 more flags are in the report artifact. Receipts by tax head
Spending by benefit and service
Caseloads
Government balance, each component counted once
Model totals
Household income totals
Population
Poverty rates
People in poverty
Inequality
Mean household net income in each side's own deciles
Standard reform costings (change in gov_balance)
Standard reforms: change in each reform's target
Household net income by decileMean change in household net income per household, and as a share of the decile's base income; households stay in the base run's deciles.
Checks failing on both base and head (1)
Counted-once checkEach of 86 components was raised by £1 per unit in 2026-27, and every head of its totals was read household by household: its own head must rise by exactly £1, no other tax head may move, no other benefit may rise, and the model's own total must move by what its heads moved. A head that breaks a rule is measured again twice: with every other head held at its baseline value, and with every other head held at its value after the first £1 while the component rises again. It fails where either still shows the response. 0 failed; 0 were skipped.
Provenance
Dataset: Receipts by tax head: head levels
Spending by benefit and service: head levels
Caseloads: head levels
Government balance, each component counted once: head levels
Model totals: head levels
Household income totals: head levels
Population: head levels
Poverty rates: head levels
People in poverty: head levels
Inequality: head levels
Mean household net income in each side's own deciles: head levels
Standard reform costings (change in gov_balance): head levels
Standard reforms: change in each reform's target: head levels
|
CI at 9bbcef8 failed test_head_totals_agree_with_the_model_weighted_sums: receipts plus double counts were GBP 2,646 (2.8e-9) off gov_tax on GBP 935bn, against a GBP 935 tolerance. Merge-queue groups containing #2319 fail the same assertion (GBP 994 on GBP 939bn), so it is rounding, not a missing head. rel=1e-8 still catches any head above about GBP 9,000. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
CI at 9bbcef8: the two council tax expectations in 🤖 Generated with Claude Code |
Takes main's spec tolerance in the certified receipts check, which supersedes this branch's rel=1e-8. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Merged main in at f5c579e to clear the conflict in 🤖 Generated with Claude Code |
juaristi22
left a comment
There was a problem hiding this comment.
Reviewed at f5c579e3fbcc56191683a9205f6c60ce780458a1. No actionable findings from independent code and source reviews.
Original OBR workbooks reproduce all 51 population and 30 per-capita values. All 109 focused tests passed, covering synthetic dataset weights, country bills, weighted council-tax receipts and preservation of aggregate road-fuel litres. The inherited fiscal/Q2 population versus calendar-year income convention remains an explicit approximation.
Resolve the conflict in yoy_growth.yaml: keep main's March 2026 population references (#2611) and this branch's household interest block. Main's population vintage changed ons.population, so re-derive ons.per_capita.household_interest_income from it as (1 + aggregate) / (1 + population) - 1, which the branch's own test requires, and update the 2025 test case to match (per-person factor 0.9695, was 0.9654). The aggregate HAXV series is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…opulation main's ons.population is now the March 2026 vintage (#2611), so every year of orr.per_capita.rail_passenger_journeys is re-derived from it. The offset now runs to 2073, as far as ons.population does, so total journeys stay flat after 2030 (review A1). Tests check every population year and a flat weighted total to PROJECTION_END_YEAR. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes #2383
What and why
ons.populationwas the March 2025 vintage, while the per-capita series divided March 2026 numerators by it. This takes population from one release consistent with the March 2026 EFO and re-derives everything built on it.Population (
gov.economic_assumptions.yoy_growth.ons.population)8983f5a1ed4912fe5b950e3d9d9fbd0cd973eb8ac47940e622899eeef4d35112). Its footnote 2: OBR forecast to 2030-31, then the principal projection of the ONS 2024-based national population projections (28 April 2026). Same convention as before: fiscal year starting in t at t-01-01, 4 decimals.From 2031 the ONS 2024-based principal projection is much lower than the 2022-based migration variant. Growth turns negative from 2055 (-0.0010 a year by 2073).
Per-capita series (
obr.per_capita.*), 2021-2030, all as (1 + aggregate growth) / (1 + population) - 1 with the stored population, 4 decimals:gdp: calendar-year nominal GDP from EFO Table 1.4 annual totals (same growth as Table A.1).mixed_income: EFO Table 1.12 mixed income.non_labour_income: the storedobr.non_labour_income.Cumulative change from 2024, new against old: population -0.39% by 2025, -0.50% by 2027, -0.76% by 2030; per-capita GDP +0.51%, +0.56%, +0.72%; mixed income +0.39%, +0.48%, +0.75%; non-labour income +0.35%, +0.47%, +0.73%.
One error of ours this also corrects: the stored per-capita GDP values did not reproduce from their stated source. 2025 was 0.0438, against 0.0430 from Table A.1 and the old population; 2027 and 2030 were also off by 0.0004 to 0.0009. The YAML comments also said "minus" population growth, while the series that do reproduce were divided. The comments now give the formula, and a test recomputes every year.
Also re-derived:
petrol_spending_litre_proxyanddiesel_spending_litre_proxydivide by population by construction, so all their years are recomputed with the documented formula (the existing test checks this).docs/book/assumptions/growthfactors.mdpopulation and per-capita sections are updated.Effect on projection: household weights grow by
ons.population, so weights grow more slowly and per-household amounts on the per-capita indices faster; weighted aggregates move little. Council tax bills grow by receipts net of weight growth, so the expected values intest_council_tax_uprating.pychange (for example a 2025 London bill of GBP 1,000 becomes 1,000 x 1.0702 / 1.0030 = GBP 1,067.00, was GBP 1,062.55).Open questions
obr.non_labour_income2024 (0.0492) does not match the March 2026 Table 1.12 outturn (0.0484); the other sections ofgrowthfactors.mdstill quote an older vintage.Test plan
policyengine_uk/tests/test_population_vintage.py(86 cases): population against the release row, the economy-table method against the release for 2023-2025, 2021-2022 from the tables, and each per-capita series recomputed from its source. 72 fail on main.test_council_tax_uprating.pyexpected values for the new weight growth, with the arithmetic in the comments.ruff checkandruff format --checkon changed Python.changelog.d/2383.fixed.md.Reproducer
Reproducer: policyengine_uk/tests/test_population_vintage.py::test_population_is_the_march_2026_long_term_determinants_row, policyengine_uk/tests/test_population_vintage.py::test_population_before_the_release_comes_from_the_efo_tables, policyengine_uk/tests/test_population_vintage.py::test_per_capita_gdp_divides_nominal_gdp_by_population, policyengine_uk/tests/test_population_vintage.py::test_per_capita_mixed_income_divides_mixed_income_by_population, policyengine_uk/tests/test_population_vintage.py::test_per_capita_non_labour_income_divides_by_population
axiom: n/a: economic-assumption parameters, not a legal provision
🤖 Generated with Claude Code