From 00185d93eae97286c8b695d29d5717df3220471b Mon Sep 17 00:00:00 2001 From: Maria Juaristi Date: Tue, 27 Oct 2026 11:56:35 +0000 Subject: [PATCH 01/13] Extract the UK calibration targets from the Microcosm UK releases scripts/extract_uk_calibration_targets.py builds src/data/ukCalibrationTargets.json from the national and local-area releases that policyengine.py serves. Their diagnostics are licensed, so the script runs locally and only the JSON is committed. It checks the national diagnostics' sha256 against the bundle manifest, and the local-area diagnostics' against that release's manifest, whose H5 must match the bundle's. Each target is reduced to whitelisted fields (value, period, citation, geography, unit, filter variable names, transformation notes) before grouping, and assert_clean() refuses forbidden keys in the output. Rows key on measure concept and filter variable names; a row without an entry in the LABELS table stops the extraction. This JSON comes from the pair policyengine.py 6.11.1 serves (national a6299504, local area c9df4b86): 24,303 targets in 152 rows. Co-Authored-By: Claude Opus 5.5 --- scripts/extract_uk_calibration_targets.py | 1026 ++++ .../test_extract_uk_calibration_targets.py | 392 ++ src/data/ukCalibrationTargets.json | 4788 +++++++++++++++++ 3 files changed, 6206 insertions(+) create mode 100644 scripts/extract_uk_calibration_targets.py create mode 100644 scripts/test_extract_uk_calibration_targets.py create mode 100644 src/data/ukCalibrationTargets.json diff --git a/scripts/extract_uk_calibration_targets.py b/scripts/extract_uk_calibration_targets.py new file mode 100644 index 0000000..541ccbe --- /dev/null +++ b/scripts/extract_uk_calibration_targets.py @@ -0,0 +1,1026 @@ +""" +Extract the UK calibration targets from the Microcosm UK releases that +policyengine.py serves, and write a grouped JSON for the Calibration page. + +Usage: + uv run --no-project python scripts/extract_uk_calibration_targets.py \ + --national PATH/TO/national/calibration_diagnostics.json \ + --dense-release-manifest PATH/TO/dense/release_manifest.json \ + --dense PATH/TO/dense/calibration_diagnostics.json \ + [--bundle-manifest PATH_OR_URL] + +policyengine.py serves two UK files built from one Microcosm spine: the +national file (the UK and its four nations) and the local-area file (the 650 +constituencies and 361 local authorities). Each is calibrated separately, so +the page lists the union of both target surfaces. + +The input files are licensed: only UK Data Service licence holders can read +them, so, unlike the US extractor, this script cannot run in CI. A maintainer +with access runs it locally on the three files below and commits only +src/data/ukCalibrationTargets.json. + +- ``--national``: the national release's calibration_diagnostics.json. Its + sha256 must equal ``data_releases.uk.datasets.calibration_diagnostics`` in + the bundle manifest. +- ``--dense-release-manifest``: the local-area release's release_manifest.json. + Its cut tag and its H5 artifact must equal the bundle's + ``microcosm_uk_2024_25_dense`` entry. +- ``--dense``: the local-area release's calibration_diagnostics.json. Its + sha256 must equal that release manifest's ``calibration_diagnostics``. +- ``--bundle-manifest``: policyengine.py's + src/policyengine/data/bundle/manifest.json (default: main on GitHub). + +TARGET VALUES ONLY. Every target is reduced to the whitelisted fields in +``slim_target``; nothing else in the inputs is read, and the output is scanned +for forbidden keys before it is written. Every target value is a published +official statistic or a Microcosm transformation of one. + +Labels come from the LABELS table below, keyed by row key. A row without a +label stops the extraction, so a new release's new rows are named by hand. +""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import math +import os +import re +import subprocess +import urllib.request +from collections import Counter, defaultdict + +BUNDLE_MANIFEST_URL = ( + "https://raw.githubusercontent.com/PolicyEngine/policyengine.py/main/" + "src/policyengine/data/bundle/manifest.json" +) +OUTPUT_PATH = os.path.join( + os.path.dirname(__file__), "..", "src", "data", "ukCalibrationTargets.json" +) + +# calibration_diagnostics.json schema versions this script understands. +SUPPORTED_SCHEMA_VERSIONS = {8} +# ledger_measure_unit values a national-scope target may carry. +MEASURE_UNITS = {"gbp", "count", "gbp_per_week x 52"} +# Plausible calibration years for a target's period. +CALIBRATION_YEARS = (2000, 2100) + +# Page levels, in display order, and the row field counting each. +LEVEL_KEYS = ( + "uk", + "gb", + "englandAndWales", + "nation", + "region", + "constituency", + "localAuthority", + "other", +) +COUNT_FIELDS = { + "uk": "nationalCount", + "gb": "gbCount", + "englandAndWales": "englandAndWalesCount", + "nation": "nationCount", + "region": "regionCount", + "constituency": "constituencyCount", + "localAuthority": "localAuthorityCount", + "other": "otherCount", +} +# Ledger geography ids above the English regions. +TOP_GEOGRAPHIES = { + "K02000001": ("uk", "United Kingdom"), + "K03000001": ("gb", "Great Britain"), + "K04000001": ("englandAndWales", "England and Wales"), +} +NATIONS = { + "E92000001": "England", + "N92000002": "Northern Ireland", + "S92000003": "Scotland", + "W92000004": "Wales", +} +REGION_ID = re.compile(r"^E12\d{6}$") +# The row's headline value comes from its single target at the first of these +# levels that it has; failing that, from a row whose only target is a nation's. +HEADLINE_LEVELS = ("uk", "gb", "englandAndWales") +# Local (dense-release) geography levels. Scotland's Broad Rental Market Areas +# are 'statistical_scope' rows with area_type 'brma'; the page counts them +# under 'other'. +LOCAL_LEVELS = { + ("constituency", "constituency"): "constituency", + ("local_authority", "la"): "localAuthority", + ("statistical_scope", "brma"): "other", +} +# Units of the local metrics that are not counts. +LOCAL_UNITS = { + "hmrc/employment_income/amount": "gbp", + "hmrc/self_employment_income/amount": "gbp", + "rent/private_rent": "gbp", + "rent/private_rent_brma": "gbp", + "energy/electricity_kwh": "kwh", + "energy/gas_kwh": "kwh", +} +# Local metric prefixes whose last segment is a breakdown cell. +LOCAL_STEMS = ( + (re.compile(r"^age/"), "age", "age_band"), + (re.compile(r"^council_tax/band_"), "council_tax", "council_tax_band"), + (re.compile(r"^uc_hh_"), "uc_households_by_children", "number_of_children"), + (re.compile(r"^tenure/"), "tenure", "tenure"), + (re.compile(r"^high_value_homes/"), "high_value_homes", "value_threshold"), +) + +# Who publishes each citation family (the first field of a target's source). +# The diagnostics' hierarchy files NRS and NISRA rows under ONS, so the +# citation family is the authority. +PUBLISHERS = { + "desnz": "DESNZ", + "dfc_ni": "Department for Communities (NI)", + "dfe": "DfE", + "dfi_ni": "Department for Infrastructure (NI)", + "dft": "DfT", + "dwp": "DWP", + "hmrc": "HMRC", + "isc": "Independent Schools Council", + "mhclg": "MHCLG", + "nisra": "NISRA", + "nithc": "NI Transport Holding Company", + "nrs": "National Records of Scotland", + "obr": "OBR", + "ons": "ONS", + "orr": "ORR", + "scotgov": "Scottish Government", + "slc": "Student Loans Company", + "welshgov": "Welsh Government", + # Built by Microcosm from open data only; each row names its inputs + # (LABELS[...]["derivation"]). + "microcosm": "Microcosm derivation from open data", +} +# Families whose citation locator is a repository path, not a URL. +NON_URL_FAMILIES = {"microcosm"} +REPO_PATH = re.compile(r"^(?!/)(?!.*\.\.)[A-Za-z0-9_./-]+$") + +# Value operations that read, add, average or difference published cells: +# the target is the published figure as published. +PUBLISHED_OPERATIONS = { + "identity", + "sum", + "difference", + "linear_combination", + "monthly_window_average", + "monthly_window_sum_average", + "calendar_year_average", +} +# Value operations that transform published figures, and how the page says so. +OPERATION_NOTES = { + "calendar_year_window": "Calendar-year window of fiscal-year figures", + "calendar_year_window_scaled_by_ratio": "Calendar-year window of fiscal-year figures, scaled by a published ratio", + "scaled_by_ratio": "Scaled by a ratio of published figures", + "rolled_forward_by_ratio": "Rolled forward by a ratio of published figures", + "monthly_window_count_x_mean": "Published caseload times published mean award", + "count_x_mean": "Published count times published mean", +} +# Uprating indices, as the page names them. +UPRATING_INDICES = { + "hmrc.itl_2026.taxpayer_count_growth_by_total_income_band": "HMRC Income Tax liabilities statistics (taxpayer growth by income band)", + "hmrc.itl_2026.total_income_growth_by_total_income_band": "HMRC Income Tax liabilities statistics (income growth by income band)", + "hmrc.itl_2026.total_tax_growth_by_total_income_band": "HMRC Income Tax liabilities statistics (tax growth by income band)", + "hmrc.itl_2026.taxpayer_count_growth_times_new_state_pension_rate_by_total_income_band": "HMRC Income Tax liabilities statistics (taxpayer growth by income band) and the new State Pension rate", + "policyengine_uk.household_interest_income_to_dataset_year_then_per_capita_gdp": "policyengine-uk’s household interest income index, then OBR per-capita GDP", + "policyengine_uk_parameter:gov.economic_assumptions.indices.obr.per_capita.gdp": "OBR per-capita GDP (policyengine-uk)", + "policyengine_uk_parameter:gov.dwp.state_pension.new_state_pension.amount": "the new State Pension rate (policyengine-uk)", + "policyengine_uk_parameter:gov.hmrc.national_insurance.class_1.rates.employer": "the employer National Insurance rate (policyengine-uk)", + "dft.bus01.stage_pricing_year": "DfT bus statistics (BUS01)", + "dft.local_bus_fares_index": "the DfT local bus fares index", +} +WEEKLY_NOTE = "Annual: 52 times the published weekly amount" +PROJECTION_NOTE = "The publisher’s forecast or projection" +CROSS_GRAIN_NOTE = "Rescaled to sum to its published higher-level control" + +# Rows that put one band or line in each measure concept merge into one row, +# whose breakdown is named here. +CONCEPT_GROUPS = ( + (re.compile(r"^dwp\.uc_capped_households_(up_to_|\d+_01_)"), "dwp.uc_capped_households_by_band", "monthly cap reduction"), + (re.compile(r"^dwp\.uc_tcl_households_by_children_"), "dwp.uc_tcl_households_by_children", "number of children"), + (re.compile(r"^dwp\.uc_tcl_children_by_children_"), "dwp.uc_tcl_children_by_children", "number of children"), + (re.compile(r"^dwp\.uc_tcl_households_by_disability_"), "dwp.uc_tcl_households_by_disability", "disability"), + (re.compile(r"^dwp\.uc_tcl_children_by_disability_"), "dwp.uc_tcl_children_by_disability", "disability"), + (re.compile(r"^scotgov\.chargeable_dwellings_band_"), "scotgov.chargeable_dwellings_by_band", "council tax band"), +) + +# Breakdown names for the filter variables that vary within a row (and for the +# local stems' breakdowns). Variables fixed across a row are part of its label. +BREAKDOWNS = { + "age": "age", + "age_band": "age", + "age_bands_and_single_year": "age", + "annual_salary_sacrificed": "amount sacrificed", + "salary_sacrifice_threshold_position": "amount sacrificed", + "borrower_status": "borrower status", + "category_of_pension": "State Pension type", + "cgt_badr_ir_gain_band": "qualifying gain", + "cgt_badr_ir_qualifying_gain": "qualifying gain", + "cgt_gain": "size of gain", + "cgt_gain_band": "size of gain", + "cgt_taxable_income_band": "taxable income", + "taxable_income": "taxable income", + "council_tax_band": "council tax band", + "entitlement_type": "entitlement", + "family_type": "family type", + "gender": "sex", + "sex": "sex", + "grouped_amount_of_benefit": "weekly amount", + "household_type": "household type", + "housing_entitlement_tenure": "tenure", + "marginal_rate": "marginal tax rate", + "microcosm.value_threshold": "value threshold", + "monthly_award_amount_bands": "monthly award", + "number_of_children": "number of children", + "partner_indicator": "single or couple", + "payment_type": "payment type", + "private_or_social_rented": "private or social rented", + "student_support_product": "product", + "tenure": "tenure", + "total_income": "total income band", + "type_of_pension_credit": "Pension Credit type", + "value_threshold": "value threshold", +} + +# Row labels, keyed by row key: '||'. Never +# build a label from the concept string: a linear combination takes the +# concept of its first operand (e.g. the MHCLG and Welsh dwelling-stock rows). +# Optional fields: 'notes' (how a local row's values are built, which the +# local diagnostics do not record) and 'derivation' (the inputs of a +# Microcosm-derived row, shown as its source). +HVH_DERIVATION = ( + "Microcosm derivation from open data (HM Land Registry Price Paid, UK HPI, " + "ONS NSPL, VOA council tax stock, Census 2021 tenure)" +) +LABELS: dict[str, dict] = { + # Benefits, DWP and Department for Communities (NI) + "ledger|dwp.attendance_allowance_cases_with_entitlement|aa_award_type,age_bands_and_single_year,gender": {"label": "Attendance Allowance cases with entitlement"}, + "ledger|dwp.benefit_expenditure_amount|benefit_family,benefit_forecast_line": {"label": "Housing Benefit spending, pension age"}, + "ledger|dwp.esa_claimants|payment_type,phase_of_esa_claim": {"label": "Employment and Support Allowance claimants"}, + "ledger|dwp.hb_claimants|client_type,private_or_social_rented": {"label": "Housing Benefit claimants of pension age"}, + "ledger|dwp.pension_credit_benefit_units|": {"label": "Pension Credit caseload"}, + "ledger|dwp.pension_credit_benefit_units|age,age_bands_and_single_year,partner_indicator,type_of_pension_credit": {"label": "Pension Credit caseload"}, + "ledger|dwp.pension_credit_benefit_units|age_bands_and_single_year,partner_indicator,type_of_pension_credit": {"label": "Pension Credit caseload"}, + "ledger|dwp.state_pension_mean_weekly_amount|": {"label": "State Pension paid"}, + "ledger|dwp.state_pension_mean_weekly_amount|age_bands_and_single_year,category_of_pension,gender": {"label": "State Pension paid"}, + "ledger|dwp.state_pension_recipients|": {"label": "State Pension recipients"}, + "ledger|dwp.state_pension_recipients|age,age_bands_and_single_year,category_of_pension,gender": {"label": "State Pension recipients"}, + "ledger|dwp.state_pension_recipients|age_bands_and_single_year,category_of_pension,gender": {"label": "State Pension recipients"}, + "ledger|dwp.state_pension_recipients|category_of_pension,gender": {"label": "State Pension recipients"}, + "ledger|dwp.state_pension_recipients|category_of_pension,grouped_amount_of_benefit": {"label": "State Pension recipients"}, + "ledger|dwp.uc_benefit_units|age_of_youngest_child_bands_and_single_year": {"label": "Universal Credit households with a child under 1"}, + "ledger|dwp.uc_benefit_units|carer_entitlement,payment_indicator": {"label": "Universal Credit households with carer entitlement"}, + "ledger|dwp.uc_benefit_units|child_entitlement,family_type,payment_indicator": {"label": "Universal Credit households with child entitlement"}, + "ledger|dwp.uc_benefit_units|child_entitlement,number_of_children,payment_indicator": {"label": "Universal Credit households"}, + "ledger|dwp.uc_benefit_units|family_type,monthly_award_amount_bands": {"label": "Universal Credit households"}, + "ledger|dwp.uc_benefit_units|housing_entitlement_tenure,payment_indicator": {"label": "Universal Credit households with housing entitlement"}, + "ledger|dwp.uc_benefit_units|limited_capability_for_work_entitlement,payment_indicator": {"label": "Universal Credit households with LCWRA entitlement"}, + "ledger|dwp.uc_benefit_units_with_childcare_element|uc_element": {"label": "Universal Credit households receiving the childcare element"}, + "ledger|dwp.uc_benefit_units_with_deduction|": {"label": "Universal Credit households with a deduction"}, + "ledger|dwp.uc_capped_households_by_band|": {"label": "Benefit-capped households"}, + "ledger|dwp.uc_capped_households_total|": {"label": "Benefit-capped households"}, + "ledger|dwp.uc_tcl_headline_households_affected|two_child_limit_status": {"label": "Households affected by the two-child limit"}, + "ledger|dwp.uc_tcl_households_by_children|two_child_limit_status": {"label": "Households affected by the two-child limit"}, + "ledger|dwp.uc_tcl_households_by_disability|two_child_limit_status": {"label": "Households affected by the two-child limit"}, + "ledger|dwp.uc_tcl_headline_children_affected_by_policy|two_child_limit_status": {"label": "Children not receiving the child element under the two-child limit"}, + "ledger|dwp.uc_tcl_headline_children_within_affected_households|two_child_limit_status": {"label": "Children in households affected by the two-child limit"}, + "ledger|dwp.uc_tcl_children_by_children|two_child_limit_status": {"label": "Children in households affected by the two-child limit"}, + "ledger|dwp.uc_tcl_children_by_disability|two_child_limit_status": {"label": "Children in households affected by the two-child limit"}, + "ledger|dwp.workplace_pension_annual_saving|price_basis,saver_population,saving_component,sector": {"label": "Employer workplace pension contributions"}, + # Capital Gains Tax, HMRC + "ledger|hmrc.cgt_badr_ir_qualifying_gains|cgt_badr_ir_gain_band,cgt_badr_ir_qualifying_gain,taxpayer_type": {"label": "Capital Gains Tax: gains qualifying for Business Asset Disposal or Investors’ Relief"}, + "ledger|hmrc.cgt_badr_ir_taxpayers|cgt_badr_ir_gain_band,cgt_badr_ir_qualifying_gain,taxpayer_type": {"label": "Capital Gains Tax: Business Asset Disposal or Investors’ Relief claimants"}, + "ledger|hmrc.cgt_gains_individuals|": {"label": "Capital Gains Tax: gains of individuals"}, + "ledger|hmrc.cgt_gains_individuals|age,age_band": {"label": "Capital Gains Tax: gains of individuals"}, + "ledger|hmrc.cgt_gains_individuals|cgt_gain,cgt_gain_band": {"label": "Capital Gains Tax: gains of individuals"}, + "ledger|hmrc.cgt_gains_individuals|cgt_taxable_income_band,taxable_income": {"label": "Capital Gains Tax: gains of individuals"}, + "ledger|hmrc.cgt_gains_total|": {"label": "Capital Gains Tax: gains"}, + "ledger|hmrc.cgt_residential_property_gains|channel": {"label": "Capital Gains Tax: residential property gains"}, + "ledger|hmrc.cgt_residential_property_taxpayers|channel": {"label": "Capital Gains Tax: residential property taxpayers"}, + "ledger|hmrc.cgt_tax_individuals|": {"label": "Capital Gains Tax: liabilities of individuals"}, + "ledger|hmrc.cgt_tax_individuals|age,age_band": {"label": "Capital Gains Tax: liabilities of individuals"}, + "ledger|hmrc.cgt_taxpayers_individuals|": {"label": "Capital Gains Tax: individual taxpayers"}, + "ledger|hmrc.cgt_taxpayers_individuals|age,age_band": {"label": "Capital Gains Tax: individual taxpayers"}, + "ledger|hmrc.cgt_taxpayers_individuals|cgt_gain,cgt_gain_band": {"label": "Capital Gains Tax: individual taxpayers"}, + "ledger|hmrc.cgt_taxpayers_individuals|cgt_taxable_income_band,taxable_income": {"label": "Capital Gains Tax: individual taxpayers"}, + "ledger|hmrc.cgt_taxpayers_total|": {"label": "Capital Gains Tax: taxpayers"}, + # Incomes and Income Tax, HMRC + "ledger|hmrc.spi_dividend_income_amount|total_income": {"label": "Income: dividends"}, + "ledger|hmrc.spi_dividend_income_count|total_income": {"label": "Income: people with dividends"}, + "ledger|hmrc.spi_employment_income_amount|total_income": {"label": "Income: employment"}, + "ledger|hmrc.spi_employment_income_count|total_income": {"label": "Income: people with employment income"}, + "ledger|hmrc.spi_private_pension_income_amount|total_income": {"label": "Income: private pensions"}, + "ledger|hmrc.spi_private_pension_income_count|total_income": {"label": "Income: people with private pension income"}, + "ledger|hmrc.spi_property_income_amount|total_income": {"label": "Income: property"}, + "ledger|hmrc.spi_property_income_count|total_income": {"label": "Income: people with property income"}, + "ledger|hmrc.spi_savings_interest_income_amount|total_income": {"label": "Income: savings interest"}, + "ledger|hmrc.spi_savings_interest_income_count|total_income": {"label": "Income: people with savings interest"}, + "ledger|hmrc.spi_self_employment_income_amount|total_income": {"label": "Income: self-employment"}, + "ledger|hmrc.spi_self_employment_income_count|total_income": {"label": "Income: people with self-employment income"}, + "ledger|hmrc.spi_state_pension_amount|total_income": {"label": "Income: State Pension"}, + "ledger|hmrc.spi_state_pension_count|total_income": {"label": "Income: people with State Pension income"}, + "ledger|hmrc.spi_total_income_before_tax_amount|sex,total_income": {"label": "Income: total before tax"}, + "ledger|hmrc.spi_total_income_before_tax_amount|total_income": {"label": "Income: total before tax"}, + "ledger|hmrc.spi_total_income_before_tax_count|sex,total_income": {"label": "Income: people with income"}, + "ledger|hmrc.spi_taxpayer_count|marginal_rate": {"label": "Income Tax payers"}, + "ledger|hmrc.spi_taxpayer_count|total_income": {"label": "Income Tax payers"}, + "ledger|hmrc.spi_total_tax_amount|sex,total_income": {"label": "Income Tax liabilities"}, + "ledger|hmrc.spi_total_tax_amount|total_income": {"label": "Income Tax liabilities"}, + # Pensions and childcare, HMRC + "ledger|hmrc.salary_sacrifice_pension_users|annual_salary_sacrificed,salary_sacrifice_threshold_position": {"label": "Salary-sacrifice pension users"}, + "ledger|hmrc.ss_income_tax_relief_basic_rate|": {"label": "Salary-sacrifice pension contributions: Income Tax relief at the basic rate"}, + "ledger|hmrc.ss_income_tax_relief_higher_rate|": {"label": "Salary-sacrifice pension contributions: Income Tax relief at the higher rate"}, + "ledger|hmrc.ss_income_tax_relief_additional_rate|": {"label": "Salary-sacrifice pension contributions: Income Tax relief at the additional rate"}, + "ledger|hmrc.ss_nics_relief_employee|": {"label": "Salary-sacrifice pension contributions: employee National Insurance relief"}, + "ledger|hmrc.ss_nics_relief_employer|": {"label": "Salary-sacrifice pension contributions: employer National Insurance relief"}, + "ledger|hmrc.tfc_children_with_used_accounts_annual_unique|account_status,count_basis,definition_regime,source_release": {"label": "Tax-Free Childcare: children with a used account"}, + "ledger|hmrc.tfc_government_top_up|flow_basis,flow_type,source_release": {"label": "Tax-Free Childcare: government top-up"}, + # Expected in the release built from microcosm 90ff43f95 (HMRC Child + # Benefit families by number of children); unused until then. + "ledger|hmrc.child_benefit_families_in_payment|number_of_children": {"label": "Child Benefit families in payment"}, + # Government receipts and spending, OBR + "ledger|obr.attendance_allowance|": {"label": "Attendance Allowance spending"}, + "ledger|obr.carers_allowance|": {"label": "Carer’s Allowance spending"}, + "ledger|obr.child_benefit|": {"label": "Child Benefit spending"}, + "ledger|obr.council_tax_receipts_england_scotland_wales|": {"label": "Council Tax receipts (England, Scotland and Wales)"}, + "ledger|obr.dla_and_pip|": {"label": "Disability Living Allowance and Personal Independence Payment spending"}, + "ledger|obr.incapacity_benefits|": {"label": "Incapacity benefits spending"}, + "ledger|obr.income_tax|": {"label": "Income Tax receipts"}, + "ledger|obr.jobseekers_allowance|": {"label": "Jobseeker’s Allowance spending"}, + "ledger|obr.ni_employee|": {"label": "Employee National Insurance contributions"}, + "ledger|obr.ni_self_employed|": {"label": "Self-employed National Insurance contributions"}, + "ledger|obr.pension_credit|": {"label": "Pension Credit spending"}, + "ledger|obr.sdlt|": {"label": "Stamp Duty Land Tax receipts"}, + "ledger|obr.statutory_maternity_pay|": {"label": "Statutory Maternity Pay spending"}, + "ledger|obr.tv_licence_fee|": {"label": "TV licence fee receipts"}, + "ledger|obr.universal_credit_outside_cap|": {"label": "Universal Credit spending outside the welfare cap"}, + "ledger|obr.winter_fuel_payment|": {"label": "Winter Fuel Payment spending"}, + # Population, households and the economy, ONS, NRS, NISRA, Welsh Government + "ledger|ons.mid_year_population_estimate|": {"label": "Population"}, + "ledger|ons.mid_year_population_estimate|age": {"label": "Population"}, + "ledger|ons.mid_year_population_estimate|age,sex": {"label": "Population"}, + "ledger|ons.households_by_type|household_type": {"label": "Households"}, + "ledger|ons.households_total|": {"label": "Households"}, + "ledger|nrs.households_total|": {"label": "Households"}, + "ledger|nisra.census2021_households|": {"label": "Households"}, + "ledger|welshgov.households_total|Data description,Household Type": {"label": "Households"}, + "ledger|ons.household_expenditure.personal_transport_fuels_lubricants|coicop,consumption_concept,frequency,price_basis,published_unit,seasonal_adjustment,source_sheet": {"label": "Household spending on fuels and lubricants for personal transport"}, + "ledger|ons.nbs_land_value_households|publication_status": {"label": "Land held by households (national balance sheet)"}, + "ledger|ons.nbs_land_value_nfc|publication_status": {"label": "Land held by non-financial corporations (national balance sheet)"}, + "ledger|ons.nbs_land_value_total|publication_status": {"label": "Land, all sectors (national balance sheet)"}, + "ledger|ons.pse_headcount_total_public_sector|": {"label": "Public sector employment"}, + # Council Tax and housing, MHCLG, Scottish Government, Welsh Government + "ledger|mhclg.council_taxbase.empty_dwellings|": {"label": "Council Tax dwelling stock"}, + "ledger|mhclg.council_taxbase.empty_dwellings|council_tax_band": {"label": "Council Tax dwelling stock"}, + "ledger|mhclg.council_taxbase.single_adult_discount_dwellings|": {"label": "Dwellings with a Council Tax single-adult discount"}, + "ledger|scotgov.chargeable_dwellings_by_band|": {"label": "Council Tax dwelling stock"}, + "ledger|scotgov.chargeable_dwellings_total|": {"label": "Council Tax dwelling stock"}, + "ledger|welshgov.council_tax_dwellings.total_chargeable_second_homes|": {"label": "Council Tax dwelling stock"}, + "ledger|welshgov.council_tax_dwellings.total_chargeable_second_homes|council_tax_band": {"label": "Council Tax dwelling stock"}, + "ledger|uk.dwelling.count|microcosm.value_threshold,uk.dwelling.tenure": {"label": "Owner-occupied homes above high-value thresholds", "derivation": HVH_DERIVATION}, + # Public transport + "ledger|dft.local_bus_passenger_fare_receipts|": {"label": "Local bus passenger fare receipts"}, + "ledger|dft.local_bus_passenger_journeys|": {"label": "Local bus passenger journeys"}, + "ledger|dft.local_bus_total_estimated_net_support|": {"label": "Local bus services: estimated net government support"}, + "ledger|scotgov.bus.government_support|funding_component": {"label": "Bus services: government support"}, + "ledger|scotgov.bus.operator_revenue|revenue_component": {"label": "Bus services: passenger revenue"}, + "ledger|welshgov.local_transport.gross_expenditure|expenditure_component,service_line": {"label": "Local authority support to bus operators"}, + "ledger|dfi_ni.translink_bus.passenger_receipts|service": {"label": "Ulsterbus passenger receipts"}, + "ledger|nithc.dfi_transaction_value|funding_component,mode_scope": {"label": "Public transport Public Service Obligation compensation"}, + # Education and childcare, DfE, ISC, SLC, Scottish Government + "ledger|dfe.funded_childcare_registered_children_nursery|age,entitlement_type,provision,registration_basis": {"label": "Funded early education: 3-year-olds using the universal hours at a nursery"}, + "ledger|dfe.funded_childcare_registered_children_total|age,entitlement_type,provision,registration_basis": {"label": "Funded early education and childcare: registered children"}, + "ledger|isc.pupils_at_member_schools|school_membership": {"label": "Pupils at Independent Schools Council schools"}, + "ledger|slc.maintenance_loan_amount_paid|": {"label": "Student maintenance loans paid"}, + "ledger|slc.maintenance_loan_recipients|": {"label": "Student maintenance loan recipients"}, + "ledger|slc.student_loan_borrowers|borrower_status,repayment_plan,row_group": {"label": "Plan 5 student loan borrowers"}, + "ledger|slc.student_loan_net_repayments_plan_1|": {"label": "Student loan net repayments: Plan 1"}, + "ledger|slc.student_loan_net_repayments_plan_2_full_time|": {"label": "Student loan net repayments: Plan 2, full-time"}, + "ledger|slc.student_loan_net_repayments_total_higher_education|": {"label": "Student loan net repayments: all higher education"}, + "ledger|slc.targeted_support_amount_awarded|student_support_product": {"label": "Targeted student support awarded"}, + "ledger|slc.targeted_support_recipients|student_support_product": {"label": "Targeted student support recipients"}, + "ledger|scotgov.social_security_assistance_spending|social_security_line": {"label": "Scottish Child Payment spending"}, + # Constituency, local authority and Scottish BRMA cells (local-area file) + "local|age|age_band": { + "label": "Population", + "notes": ["Published small-area and local-authority estimates summed to ten-year bands", CROSS_GRAIN_NOTE], + }, + "local|households|": { + "label": "Households", + "notes": ["Census 2021 counts (Scotland: Census 2022)", CROSS_GRAIN_NOTE], + }, + "local|tenure|tenure": { + "label": "Households", + "notes": ["Census tenure counts moved to 2025 with ONS tenure shares", CROSS_GRAIN_NOTE], + }, + "local|uc_households|": { + "label": "Universal Credit households", + "notes": ["Average of the published 2025 months, paid claims only", CROSS_GRAIN_NOTE], + }, + "local|uc_households_by_children|number_of_children": { + "label": "Universal Credit households", + "notes": ["Average of the published 2025 months, paid claims only", CROSS_GRAIN_NOTE], + }, + "local|hmrc/employment_income/amount|": { + "label": "Income: employment", + "notes": ["Published count times published mean, uprated with HMRC Income Tax liabilities statistics", CROSS_GRAIN_NOTE], + }, + "local|hmrc/employment_income/count|": { + "label": "Income: people with employment income", + "notes": ["Uprated with HMRC Income Tax liabilities statistics", CROSS_GRAIN_NOTE], + }, + "local|hmrc/self_employment_income/amount|": { + "label": "Income: self-employment", + "notes": ["Published count times published mean, uprated with HMRC Income Tax liabilities statistics", CROSS_GRAIN_NOTE], + }, + "local|hmrc/self_employment_income/count|": { + "label": "Income: people with self-employment income", + "notes": ["Uprated with HMRC Income Tax liabilities statistics", CROSS_GRAIN_NOTE], + }, + "local|hmrc/higher_rate_taxpayers/count|": { + "label": "Higher-rate Income Tax payers", + "notes": ["Uprated with HMRC Income Tax liabilities statistics", CROSS_GRAIN_NOTE], + }, + "local|council_tax|council_tax_band": { + "label": "Council Tax dwelling stock", + "notes": ["Sum of published taxbase lines", CROSS_GRAIN_NOTE], + }, + # Expected in the release built from microcosm 90ff43f95; unused until then. + "local|council_tax/single_adult_discount|": { + "label": "Dwellings with a Council Tax single-adult discount", + "notes": ["Sum of published taxbase lines"], + }, + "local|rent/private_rent|": { + "label": "Private rents paid", + "notes": ["12 times the ONS Price Index of Private Rents mean monthly rent for 2025, times private-renter households"], + }, + "local|rent/private_rent_brma|": { + "label": "Private rents paid", + "notes": ["12 times the ONS Price Index of Private Rents mean monthly rent for 2025, times private-renter households"], + }, + "local|energy/electricity_kwh|": { + "label": "Domestic electricity consumption", + "notes": ["Uprated to 2025 with DESNZ Energy Trends (Great Britain domestic level)"], + "derivation": "Microcosm derivation from open data (DESNZ LSOA and data zone consumption)", + }, + "local|energy/gas_kwh|": { + "label": "Domestic gas consumption", + "notes": ["Uprated to 2025 with DESNZ Energy Trends (Great Britain domestic level)"], + "derivation": "Microcosm derivation from open data (DESNZ LSOA and data zone consumption)", + }, + "local|energy/gas_connected_households|": { + "label": "Gas-connected households", + "notes": ["Census households times the DESNZ share of households with a gas meter"], + "derivation": "Microcosm derivation from open data (DESNZ LSOA and data zone consumption)", + }, + "local|high_value_homes|value_threshold": { + "label": "Owner-occupied homes above high-value thresholds", + "derivation": HVH_DERIVATION, + }, +} + +# Never copied from the diagnostics; the output is scanned for these keys. +FORBIDDEN_KEYS = { + "initial_estimate", + "final_estimate", + "relative_error", + "within_tolerance", + "compiled_target", + "target_loss_weight", + "target_loss_weight_share", + "target_loss_scale", + "final_capped_scaled_error", + "final_loss_contribution", + "fraction_within_10pct", + "withinTenPctShare", + "uk_diagnostics", + "effective_sample_size", + "realized_max_weight_ratio", + "top_1pct_weight_share", + "n_nonzero", + "loss_trajectory", + "initial_loss", + "final_loss", + "past_cap_census", + "carrier_floor_met", + "model_figures_withheld", + "area_support", + "reviewed_limitations", +} + + +class UnsupportedDiagnostics(ValueError): + """The input files are not what this extraction understands.""" + + +def fetch_bytes(url: str) -> bytes: + try: + req = urllib.request.Request(url, headers={"User-Agent": "policyengine-model"}) + with urllib.request.urlopen(req) as resp: + return resp.read() + except Exception: + # Fallback to curl if Python SSL certs aren't configured + return subprocess.check_output(["curl", "-sfL", url]) + + +def read_bytes(path_or_url: str) -> bytes: + if path_or_url.startswith(("http://", "https://")): + return fetch_bytes(path_or_url) + with open(path_or_url, "rb") as f: + return f.read() + + +def sha256(raw: bytes) -> str: + return hashlib.sha256(raw).hexdigest() + + +def _is_finite_number(value) -> bool: + if isinstance(value, bool): + return False + if isinstance(value, int): + return True + return isinstance(value, float) and math.isfinite(value) + + +def _is_nonempty_string(value) -> bool: + return isinstance(value, str) and bool(value.strip()) + + +def _as_factor(value) -> float | None: + """A finite number, or a string holding one (metadata stores some factors + as strings); None when the value is neither.""" + if isinstance(value, str): + try: + value = float(value) + except ValueError: + return None + return float(value) if _is_finite_number(value) else None + + +def _field(obj, *path): + for key in path: + if not isinstance(obj, dict) or key not in obj: + raise UnsupportedDiagnostics(f"missing {'.'.join(path)}") + obj = obj[key] + return obj + + +def verify_releases( + bundle: dict, dense_manifest: dict, national_raw: bytes, dense_raw: bytes +) -> dict: + """Tie both diagnostics files to the UK pair the bundle manifest serves.""" + uk = _field(bundle, "data_releases", "uk") + datasets = _field(uk, "datasets") + + national_entry = _field(datasets, "calibration_diagnostics") + national_id = _field(uk, "build_id") + if national_entry.get("revision") != national_id: + raise UnsupportedDiagnostics( + f"bundle calibration_diagnostics revision {national_entry.get('revision')!r} " + f"is not the UK build {national_id!r}" + ) + if sha256(national_raw) != national_entry.get("sha256"): + raise UnsupportedDiagnostics( + f"national diagnostics sha256 {sha256(national_raw)} does not match the " + f"bundle manifest ({national_entry.get('sha256')})" + ) + + dense_entry = _field(datasets, "microcosm_uk_2024_25_dense") + dense_id = dense_entry.get("revision") + if _field(dense_manifest, "build", "cut_tag") != dense_id: + raise UnsupportedDiagnostics( + f"dense release manifest is for {dense_manifest['build']['cut_tag']!r}, " + f"not the bundle's local-area release {dense_id!r}" + ) + artifacts = _field(dense_manifest, "artifacts") + if _field(artifacts, "microcosm_uk_2024_25_dense", "sha256") != dense_entry.get("sha256"): + raise UnsupportedDiagnostics( + "dense release manifest's microcosm_uk_2024_25_dense sha256 does not " + "match the bundle manifest" + ) + expected = _field(artifacts, "calibration_diagnostics", "sha256") + if sha256(dense_raw) != expected: + raise UnsupportedDiagnostics( + f"dense diagnostics sha256 {sha256(dense_raw)} does not match the " + f"dense release manifest ({expected})" + ) + + model = uk.get("model_package") or {} + return { + "policyengineVersion": uk.get("policyengine_version") or bundle.get("policyengine_version"), + "modelVersion": model.get("version"), + "national": { + "buildId": national_id, + "dataset": uk.get("default_dataset"), + "diagnosticsSha256": national_entry["sha256"], + }, + "localArea": { + "buildId": dense_id, + "dataset": "microcosm_uk_2024_25_dense", + "diagnosticsSha256": expected, + }, + } + + +def source_parts(t: dict) -> tuple[str, str, str]: + """(family, table, locator) from 'family | table | file | vintage | locator'.""" + name = t.get("name") + source = t.get("source") + parts = [p.strip() for p in source.split(" | ")] if isinstance(source, str) else [] + if len(parts) != 5 or not all(parts): + raise UnsupportedDiagnostics( + f"target {name!r}: source {source!r} is not 'family | table | file | vintage | url'" + ) + family, table, _file, _vintage, locator = parts + if family not in PUBLISHERS: + raise UnsupportedDiagnostics(f"target {name!r}: no publisher label for {family!r}") + if family in NON_URL_FAMILIES: + if not (locator.startswith("https://") or REPO_PATH.match(locator)): + raise UnsupportedDiagnostics( + f"target {name!r}: locator {locator!r} is neither a URL nor a repository path" + ) + elif not locator.startswith(("http://", "https://")): + raise UnsupportedDiagnostics(f"target {name!r}: citation {locator!r} is not a URL") + return family, table, locator + + +def ledger_level(md: dict, name: str) -> tuple[str, str | None]: + gid = md.get("ledger_geography_id") + level = md.get("ledger_geography_level") + if level == "country" and gid in TOP_GEOGRAPHIES: + return TOP_GEOGRAPHIES[gid][0], None + if level == "country" and gid in NATIONS: + return "nation", NATIONS[gid] + if level == "region" and isinstance(gid, str) and REGION_ID.match(gid): + return "region", None + raise UnsupportedDiagnostics( + f"target {name!r}: unrecognised geography {gid!r} at level {level!r}" + ) + + +def local_level(t: dict, md: dict) -> str: + geo = (t.get("hierarchy") or {}).get("geography") or {} + key = (geo.get("level"), md.get("area_type")) + if key not in LOCAL_LEVELS: + raise UnsupportedDiagnostics( + f"target {t.get('name')!r}: unrecognised local geography {key!r}" + ) + return LOCAL_LEVELS[key] + + +def filters_of(md: dict) -> dict[str, str]: + """Filter variable -> canonical JSON of its filter values (bounds folded in).""" + by_var: dict[str, dict] = defaultdict(dict) + for key, value in md.items(): + if key.startswith("ledger_filter_"): + var = key[len("ledger_filter_"):] + base = re.sub(r"_(lower|upper)_bound(_inclusive|_exclusive)?$", "", var) + by_var[base][var] = value + return {var: json.dumps(v, sort_keys=True) for var, v in by_var.items()} + + +def concept_group(concept: str) -> tuple[str, str | None]: + for pattern, group, breakdown in CONCEPT_GROUPS: + if pattern.search(concept): + return group, breakdown + return concept, None + + +def slim_target(t, release: str) -> dict: + """The only fields kept from a diagnostics target; anything malformed stops.""" + if not isinstance(t, dict) or not _is_nonempty_string(t.get("name")): + raise UnsupportedDiagnostics(f"target without a name: {t!r:.200}") + name = t["name"] + md = t.get("metadata") + if not isinstance(md, dict): + raise UnsupportedDiagnostics(f"target {name!r}: metadata is not an object") + value = t.get("target") + if not _is_finite_number(value): + raise UnsupportedDiagnostics(f"target {name!r}: target value {value!r} is not a finite number") + period = t.get("period") + if ( + not isinstance(period, int) + or isinstance(period, bool) + or not CALIBRATION_YEARS[0] <= period <= CALIBRATION_YEARS[1] + ): + raise UnsupportedDiagnostics(f"target {name!r}: period {period!r} is not a calibration year") + family, table, _locator = source_parts(t) + out = { + "name": name, + "release": release, + "value": float(value), + "period": period, + "family": family, + "table": table, + } + + concept = md.get("ledger_measure_concept") + if concept is not None: + if not _is_nonempty_string(concept) or concept != concept.strip(): + raise UnsupportedDiagnostics(f"target {name!r}: ledger_measure_concept {concept!r} is missing or padded") + unit = md.get("ledger_measure_unit") + if unit not in MEASURE_UNITS: + raise UnsupportedDiagnostics(f"target {name!r}: ledger_measure_unit {unit!r} is not one of {sorted(MEASURE_UNITS)}") + level, nation = ledger_level(md, name) + notes = [] + op = md.get("ledger_value_operation") + if op not in PUBLISHED_OPERATIONS: + if op not in OPERATION_NOTES: + raise UnsupportedDiagnostics(f"target {name!r}: unknown value operation {op!r}") + notes.append(OPERATION_NOTES[op]) + factor = md.get("uprating_factor") + if factor is not None: + if _as_factor(factor) is None: + raise UnsupportedDiagnostics(f"target {name!r}: uprating_factor {factor!r} is not a number") + if abs(_as_factor(factor) - 1.0) > 1e-12: + index = md.get("uprating_index") + if index not in UPRATING_INDICES: + raise UnsupportedDiagnostics(f"target {name!r}: no label for uprating index {index!r}") + to = md.get("uprating_to_period") + notes.append(f"Uprated{f' to {to}' if to else ''} with {UPRATING_INDICES[index]}") + cross = md.get("cross_grain_factor") + if cross is not None: + if _as_factor(cross) is None: + raise UnsupportedDiagnostics(f"target {name!r}: cross_grain_factor {cross!r} is not a number") + if abs(_as_factor(cross) - 1.0) > 1e-12: + notes.append(CROSS_GRAIN_NOTE) + if md.get("ledger_assertion") == "source_projection": + notes.append(PROJECTION_NOTE) + if unit == "gbp_per_week x 52": + notes.append(WEEKLY_NOTE) + group, group_breakdown = concept_group(concept) + filters = filters_of(md) + out.update( + scope="ledger", + level=level, + nation=nation, + concept=group, + groupBreakdown=group_breakdown, + filters=filters, + unit="gbp" if unit == "gbp_per_week x 52" else unit, + notes=notes, + ) + else: + if any(str(k).startswith("ledger_") for k in md): + raise UnsupportedDiagnostics(f"target {name!r}: ledger metadata without a measure concept") + metric = md.get("metric") + if not _is_nonempty_string(metric): + raise UnsupportedDiagnostics(f"target {name!r}: local target without a metric") + stem, breakdown_var = metric, None + for pattern, s, var in LOCAL_STEMS: + if pattern.search(metric): + stem, breakdown_var = s, var + break + out.update( + scope="local", + level=local_level(t, md), + nation=None, + concept=stem, + groupBreakdown=None, + filters={breakdown_var: json.dumps(metric)} if breakdown_var else {}, + unit=LOCAL_UNITS.get(metric, "count"), + notes=[], + ) + return out + + +def read_release(raw: bytes, release: str) -> tuple[dict, list[dict]]: + """Validate a diagnostics file and slim its targets.""" + try: + diagnostics = json.loads(raw) + except ValueError as e: + raise UnsupportedDiagnostics(f"{release} diagnostics are not JSON: {e}") from e + if not isinstance(diagnostics, dict): + raise UnsupportedDiagnostics(f"{release} diagnostics is not an object") + version = diagnostics.get("schema_version") + if not isinstance(version, int) or isinstance(version, bool) or version not in SUPPORTED_SCHEMA_VERSIONS: + raise UnsupportedDiagnostics( + f"{release} diagnostics schema_version {version!r} is not one of {sorted(SUPPORTED_SCHEMA_VERSIONS)}" + ) + if diagnostics.get("weight_entity") != "household": + raise UnsupportedDiagnostics(f"{release} weight_entity {diagnostics.get('weight_entity')!r} is not 'household'") + n_records = diagnostics.get("n_records") + if not isinstance(n_records, int) or isinstance(n_records, bool) or n_records <= 0: + raise UnsupportedDiagnostics(f"{release} n_records {n_records!r} is not a positive integer") + targets = diagnostics.get("targets") + if not isinstance(targets, list) or not targets: + raise UnsupportedDiagnostics(f"{release} targets is missing or empty") + slim = [slim_target(t, release) for t in targets] + + # Cross-checks against the totals the diagnostics state about themselves, + # so a truncated or duplicated target list is caught. + names = [t["name"] for t in slim] + if len(set(names)) != len(names): + raise UnsupportedDiagnostics(f"{release} target names are not unique") + surface = diagnostics.get("target_surface") + surface = surface if isinstance(surface, dict) else {} + if surface.get("n_targets") != len(slim): + raise UnsupportedDiagnostics( + f"{release} target_surface.n_targets {surface.get('n_targets')!r} does not match {len(slim)} targets" + ) + if surface.get("n_records") != n_records: + raise UnsupportedDiagnostics( + f"{release} target_surface.n_records {surface.get('n_records')!r} does not match n_records {n_records!r}" + ) + return {"households": n_records, "targets": len(slim)}, slim + + +def merge_releases(national: list[dict], dense: list[dict]) -> list[dict]: + """Union of both target surfaces. A target in both releases (same name) + must carry the same value there; it is kept once.""" + by_name: dict[str, dict] = {} + for t in dense: + by_name[t["name"]] = dict(t, inNational=False) + for t in national: + if t["name"] in by_name: + d = by_name[t["name"]] + if abs(d["value"] - t["value"]) > 1e-9 * max(1.0, abs(t["value"])): + raise UnsupportedDiagnostics(f"target {t['name']!r}: national and local-area values differ") + d["inNational"] = True + else: + by_name[t["name"]] = dict(t, inNational=True) + return list(by_name.values()) + + +def row_key(scope: str, concept: str, filters) -> str: + return "|".join([scope, concept, ",".join(sorted(filters))]) + + +def breakdown_of(targets: list[dict]) -> str | None: + """Names of the filter variables whose values differ within the row.""" + names: list[str] = [] + group = {t["groupBreakdown"] for t in targets} + if len(group) != 1: + raise UnsupportedDiagnostics(f"row {targets[0]['concept']!r} mixes concept groups") + variables = sorted({v for t in targets for v in t["filters"]}) + for var in variables: + values = {t["filters"].get(var) for t in targets} + if len(values) > 1: + if var not in BREAKDOWNS: + raise UnsupportedDiagnostics(f"no breakdown name for filter variable {var!r}") + if BREAKDOWNS[var] not in names: + names.append(BREAKDOWNS[var]) + breakdown = next(iter(group)) + if breakdown and breakdown not in names: + names.append(breakdown) + if not names: + return None + text = " × ".join(names) + return text[:1].upper() + text[1:] + + +def group_rows(targets: list[dict]) -> tuple[list[dict], list[str]]: + """One row per (scope, measure concept, filter variable names). + + This is the US page's (variable, domain) grouping: the ledger measure + concept stands for the variable and the filter variable names (never + their values) for the domain. Local rows key on the metric stem. + Returns the rows and the LABELS keys no row used. + """ + grouped: dict[str, list[dict]] = defaultdict(list) + for t in targets: + grouped[row_key(t["scope"], t["concept"], t["filters"])].append(t) + + unlabelled = sorted(k for k in grouped if k not in LABELS) + if unlabelled: + raise UnsupportedDiagnostics( + "no label for row key(s):\n " + "\n ".join(unlabelled) + + "\nAdd them to LABELS in scripts/extract_uk_calibration_targets.py" + ) + + rows = [] + for key, ts in grouped.items(): + entry = LABELS[key] + counts = Counter(t["level"] for t in ts) + headline_value, headline_geo = None, None + for level in HEADLINE_LEVELS: + at_level = [t for t in ts if t["level"] == level] + if at_level: + if len(at_level) == 1: + headline_value = at_level[0]["value"] + headline_geo = next(n for lv, n in TOP_GEOGRAPHIES.values() if lv == level) + break + else: + if len(ts) == 1 and ts[0]["level"] == "nation": + headline_value, headline_geo = ts[0]["value"], ts[0]["nation"] + families = [f for f, _ in Counter(t["family"] for t in ts).most_common()] + sources = [] + for f in families: + label = entry.get("derivation") if f == "microcosm" else PUBLISHERS[f] + if f == "microcosm" and not label: + raise UnsupportedDiagnostics(f"row {key!r}: Microcosm-derived targets need a 'derivation' label") + sources.append(label) + units = {t["unit"] for t in ts} + if len(units) != 1: + raise UnsupportedDiagnostics(f"row {key!r} mixes units {sorted(units)}") + periods = {t["period"] for t in ts} + notes = sorted({n for t in ts for n in t["notes"]}) + list(entry.get("notes", [])) + rows.append( + { + "key": key, + "scope": "national" if ts[0]["scope"] == "ledger" else "local", + "label": entry["label"], + "breakdown": breakdown_of(ts), + "geoLevels": [lvl for lvl in LEVEL_KEYS if counts.get(lvl)], + **{COUNT_FIELDS[lvl]: counts.get(lvl, 0) for lvl in LEVEL_KEYS}, + "nations": sorted({t["nation"] for t in ts if t["nation"]}), + "targetCount": len(ts), + "nationalValue": headline_value, + "nationalValueGeography": headline_geo, + "unit": next(iter(units)), + "source": ", ".join(sources), + "sourceTables": sorted({t["table"] for t in ts}), + "period": next(iter(periods)) if len(periods) == 1 else None, + "notes": notes, + # True when the national file calibrates none of the row's targets. + "localAreaFileOnly": not any(t["inNational"] for t in ts), + } + ) + rows.sort(key=lambda r: (r["scope"] != "national", r["label"].lower(), (r["breakdown"] or "").lower(), r["key"])) + unused = sorted(k for k in LABELS if k not in grouped) + return rows, unused + + +def level_counts(targets: list[dict]) -> dict: + return {lvl: sum(1 for t in targets if t["level"] == lvl) for lvl in LEVEL_KEYS} + + +def assert_clean(obj, path="$"): + """Refuse output that carries any key a diagnostics file uses for model figures.""" + if isinstance(obj, dict): + for k, v in obj.items(): + if k in FORBIDDEN_KEYS: + raise AssertionError(f"forbidden key {k!r} at {path}") + assert_clean(v, f"{path}.{k}") + elif isinstance(obj, list): + for i, v in enumerate(obj): + assert_clean(v, f"{path}[{i}]") + + +def extract(bundle: dict, dense_manifest: dict, national_raw: bytes, dense_raw: bytes) -> tuple[dict, list[str]]: + releases = verify_releases(bundle, dense_manifest, national_raw, dense_raw) + national_info, national = read_release(national_raw, "national") + dense_info, dense = read_release(dense_raw, "localArea") + merged = merge_releases(national, dense) + years = sorted({t["period"] for t in merged}) + if len(years) != 1: + raise UnsupportedDiagnostics(f"targets span several calibration years: {years}") + rows, unused = group_rows(merged) + output = { + "schemaVersion": 1, + "country": "uk", + "policyengineVersion": releases["policyengineVersion"], + "modelVersion": releases["modelVersion"], + "calibrationYear": years[0], + "releases": { + "national": {**releases["national"], **national_info}, + "localArea": {**releases["localArea"], **dense_info}, + }, + "totalTargets": { + "national": len(national), + "localArea": len(dense), + "union": len(merged), + }, + "levelCounts": { + "national": level_counts(national), + "localArea": level_counts(dense), + "union": level_counts(merged), + }, + "rows": rows, + } + assert_clean(output) + return output, unused + + +def main(): + parser = argparse.ArgumentParser(description=__doc__.split("\n\n")[0]) + parser.add_argument("--national", required=True, help="national release calibration_diagnostics.json") + parser.add_argument("--dense", required=True, help="local-area release calibration_diagnostics.json") + parser.add_argument("--dense-release-manifest", required=True, help="local-area release release_manifest.json") + parser.add_argument("--bundle-manifest", default=BUNDLE_MANIFEST_URL, help="policyengine.py bundle manifest (path or URL)") + parser.add_argument("--out", default=os.path.normpath(OUTPUT_PATH)) + args = parser.parse_args() + + bundle = json.loads(read_bytes(args.bundle_manifest)) + dense_manifest = json.loads(read_bytes(args.dense_release_manifest)) + output, unused = extract(bundle, dense_manifest, read_bytes(args.national), read_bytes(args.dense)) + + total = output["totalTargets"] + print( + f"policyengine.py {output['policyengineVersion']}: national {output['releases']['national']['buildId']} " + f"({total['national']} targets), local area {output['releases']['localArea']['buildId']} " + f"({total['localArea']} targets); {total['union']} in all, in {len(output['rows'])} rows" + ) + if unused: + print("Labels no row used (expected for a later release, or stale):\n " + "\n ".join(unused)) + with open(args.out, "w") as f: + json.dump(output, f, indent=2, allow_nan=False, ensure_ascii=False) + f.write("\n") + print(f"Written to {args.out}") + + +if __name__ == "__main__": + main() diff --git a/scripts/test_extract_uk_calibration_targets.py b/scripts/test_extract_uk_calibration_targets.py new file mode 100644 index 0000000..88aec49 --- /dev/null +++ b/scripts/test_extract_uk_calibration_targets.py @@ -0,0 +1,392 @@ +""" +Tests for extract_uk_calibration_targets. + +Run: + PYTHONDONTWRITEBYTECODE=1 uv run --no-project --with pytest \ + pytest -p no:cacheprovider scripts/test_extract_uk_calibration_targets.py + +The fixtures are synthetic. To also check that the committed +src/data/ukCalibrationTargets.json is exactly what the script produces from +the licensed release files, set UK_NATIONAL_DIAGNOSTICS, UK_DENSE_DIAGNOSTICS, +UK_DENSE_RELEASE_MANIFEST and UK_BUNDLE_MANIFEST to local copies. +""" + +import copy +import hashlib +import json +import os +import sys + +import pytest + +sys.path.insert(0, os.path.dirname(__file__)) +from extract_uk_calibration_targets import ( # noqa: E402 + FORBIDDEN_KEYS, + LABELS, + UnsupportedDiagnostics, + assert_clean, + extract, + read_bytes, + slim_target, +) + +COMMITTED = os.path.join( + os.path.dirname(__file__), "..", "src", "data", "ukCalibrationTargets.json" +) + +NATIONAL_ID = "microcosm-uk-2024-25-national-20260101T000000Z-aaaaaaaa" +DENSE_ID = "microcosm-uk-2024-25-dense-20260101T000000Z-bbbbbbbb" +DENSE_H5_SHA = "c" * 64 +OBR = "obr | EFO detailed forecast tables: receipts | efo.xlsx | march_2026 | https://example.org/obr" +HMRC_LOCAL = "hmrc | Personal Incomes Statistics tables 3.12 to 3.15a | t.ods | tax_year_2023_24 | https://example.org/hmrc" +HVH = "microcosm | HM Land Registry Price Paid Data, Microcosm derivation | facts.json | ppd_2026_08 | tools/build_uk_high_value_home_stock.py" + +# Model figures a diagnostics target carries; none may reach the output. +MODEL_FIGURES = { + "initial_estimate": 9876501.25, + "final_estimate": 9876502.25, + "relative_error": 0.4321987, + "within_tolerance": False, + "target_loss_weight": 0.0123987, + "final_loss_contribution": 0.0456987, +} + + +def ledger_target( + name="obr.income_tax@2025", + concept="obr.income_tax", + geography="K02000001", + level="country", + value=325.0, + source=OBR, + **metadata, +): + return { + "name": name, + "source": source, + "target": value, + "period": 2025, + "metadata": { + "ledger_measure_concept": concept, + "ledger_measure_unit": "gbp", + "ledger_geography_id": geography, + "ledger_geography_level": level, + "ledger_value_operation": "identity", + **metadata, + }, + **MODEL_FIGURES, + } + + +def local_target( + area="E14000001", + metric="hmrc/employment_income/amount", + level="constituency", + area_type="constituency", + value=10.0, + source=HMRC_LOCAL, +): + return { + "name": f"{metric.replace('/', '.')}@{area}@2025", + "source": source, + "target": value, + "period": 2025, + "metadata": { + "metric": metric, + "family": "hmrc_income_by_area", + "area_type": area_type, + "area_code": area, + "carrier_floor_met": True, + "model_figures_withheld": False, + }, + "hierarchy": {"geography": {"id": area, "level": level}}, + **MODEL_FIGURES, + } + + +def diagnostics(targets, n_records=100, **overrides): + diag = { + "schema_version": 8, + "weight_entity": "household", + "n_records": n_records, + "target_surface": {"n_targets": len(targets), "n_records": n_records}, + "targets": targets, + "fraction_within_10pct": 0.5, + "effective_sample_size": 42.5, + **overrides, + } + return diag + + +def encode(obj) -> bytes: + return json.dumps(obj).encode() + + +def sha(raw: bytes) -> str: + return hashlib.sha256(raw).hexdigest() + + +def bundle_for(national_raw: bytes) -> dict: + return { + "policyengine_version": "6.11.1", + "data_releases": { + "uk": { + "build_id": NATIONAL_ID, + "default_dataset": "microcosm_uk_2024_25", + "policyengine_version": "6.11.1", + "model_package": {"name": "policyengine-uk", "version": "2.151.0"}, + "datasets": { + "calibration_diagnostics": {"revision": NATIONAL_ID, "sha256": sha(national_raw)}, + "microcosm_uk_2024_25_dense": {"revision": DENSE_ID, "sha256": DENSE_H5_SHA}, + }, + } + }, + } + + +def dense_manifest_for(dense_raw: bytes) -> dict: + return { + "build": {"cut_tag": DENSE_ID}, + "artifacts": { + "microcosm_uk_2024_25_dense": {"sha256": DENSE_H5_SHA}, + "calibration_diagnostics": {"sha256": sha(dense_raw)}, + }, + } + + +def run(national_targets, dense_targets): + national_raw = encode(diagnostics(national_targets)) + dense_raw = encode(diagnostics(dense_targets, n_records=150)) + return extract(bundle_for(national_raw), dense_manifest_for(dense_raw), national_raw, dense_raw) + + +def default_pair(): + national = [ledger_target()] + dense = [ledger_target(), local_target("E14000001"), local_target("E14000002")] + return national, dense + + +def test_extracts_target_values_and_counts(): + output, _unused = run(*default_pair()) + assert output["calibrationYear"] == 2025 + assert output["totalTargets"] == {"national": 1, "localArea": 3, "union": 3} + assert output["releases"]["national"]["buildId"] == NATIONAL_ID + assert output["releases"]["localArea"]["households"] == 150 + rows = {r["key"]: r for r in output["rows"]} + income_tax = rows["ledger|obr.income_tax|"] + assert income_tax["label"] == "Income Tax receipts" + assert income_tax["nationalValue"] == 325.0 + assert income_tax["nationalValueGeography"] == "United Kingdom" + assert income_tax["nationalCount"] == 1 + assert income_tax["localAreaFileOnly"] is False + local = rows["local|hmrc/employment_income/amount|"] + assert local["constituencyCount"] == 2 + assert local["nationalValue"] is None + assert local["localAreaFileOnly"] is True + + +def test_output_carries_no_model_figures(): + output, _unused = run(*default_pair()) + text = json.dumps(output) + for key, value in MODEL_FIGURES.items(): + assert key not in text + if not isinstance(value, bool): + assert repr(value) not in text + for key in ("carrier_floor_met", "model_figures_withheld", "effective_sample_size", "fraction_within_10pct"): + assert key not in text + + +@pytest.mark.parametrize("key", sorted(FORBIDDEN_KEYS)) +def test_assert_clean_refuses_forbidden_keys(key): + with pytest.raises(AssertionError, match=key): + assert_clean({"rows": [{"label": "x", "nested": {key: 1}}]}) + + +def test_slim_target_keeps_only_whitelisted_fields(): + slim = slim_target(ledger_target(), "national") + assert not set(slim) & FORBIDDEN_KEYS + assert not set(slim) & set(MODEL_FIGURES) + + +@pytest.mark.parametrize( + "source", + [ + "obr | EFO tables | efo.xlsx | march_2026", # four fields + "obr | EFO tables | efo.xlsx | march_2026 | https://example.org | extra", # six fields + "obr | | efo.xlsx | march_2026 | https://example.org", # empty table + "obr | EFO tables | efo.xlsx | march_2026 | example.org/obr", # not a URL + "nobody | EFO tables | efo.xlsx | march_2026 | https://example.org", # unknown publisher + "microcosm | HVH | f.json | v | /etc/passwd", # absolute path + "microcosm | HVH | f.json | v | tools/../../secret.py", # path escape + "microcosm | HVH | f.json | v | tools/build stock.py", # not a path + ], +) +def test_rejects_malformed_citations(source): + national = [ledger_target(source=source)] + with pytest.raises(UnsupportedDiagnostics): + run(national, national) + + +def test_accepts_repository_path_for_microcosm_derivations(): + hvh = ledger_target( + name="uk.dwelling.count.e12000007.over_1m@2025", + concept="uk.dwelling.count", + geography="E12000007", + level="region", + source=HVH, + **{"ledger_filter_microcosm.value_threshold": "1000000", "ledger_filter_uk.dwelling.tenure": "owner_occupied"}, + ) + hvh["metadata"]["ledger_measure_unit"] = "count" + output, _unused = run([ledger_target()], [ledger_target(), hvh]) + row = next(r for r in output["rows"] if r["key"].startswith("ledger|uk.dwelling.count|")) + assert row["source"].startswith("Microcosm derivation from open data") + assert row["localAreaFileOnly"] is True + + +@pytest.mark.parametrize( + "geography,level", + [ + ("E14000001", "country"), # a constituency code at the ledger's country level + ("E12000007", "country"), # a region filed as a country + ("K02000001", "state"), # unknown ledger level + ("E92000001", "region"), # a nation filed as a region + (None, "country"), + ], +) +def test_rejects_unknown_ledger_geography(geography, level): + national = [ledger_target(geography=geography, level=level)] + with pytest.raises(UnsupportedDiagnostics, match="geography"): + run(national, national) + + +@pytest.mark.parametrize( + "level,area_type", + [("ward", "ward"), ("constituency", "la"), ("statistical_scope", "lsoa"), (None, None)], +) +def test_rejects_unknown_local_geography(level, area_type): + national, dense = default_pair() + dense = dense + [local_target("E05000001", level=level, area_type=area_type)] + with pytest.raises(UnsupportedDiagnostics, match="local geography"): + run(national, dense) + + +def test_rejects_unlabelled_row_keys(): + national = [ledger_target(name="obr.new_line@2025", concept="obr.new_line")] + with pytest.raises(UnsupportedDiagnostics, match=r"(?s)no label for row key.*ledger\|obr\.new_line\|"): + run(national, national) + + +def test_rejects_unlabelled_local_metric(): + national, dense = default_pair() + dense = dense + [local_target(metric="energy/heat_pumps")] + with pytest.raises(UnsupportedDiagnostics, match=r"local\|energy/heat_pumps\|"): + run(national, dense) + + +def test_rejects_unknown_value_operation_and_uprating_index(): + with pytest.raises(UnsupportedDiagnostics, match="value operation"): + national = [ledger_target(ledger_value_operation="model_estimate")] + run(national, national) + with pytest.raises(UnsupportedDiagnostics, match="uprating index"): + national = [ledger_target(uprating_factor="1.05", uprating_index="made.up.index")] + run(national, national) + + +def test_rejects_breakdowns_without_a_name(monkeypatch): + monkeypatch.setitem(LABELS, "ledger|obr.income_tax|new_dimension", {"label": "Income Tax receipts"}) + national = [ + ledger_target(name=f"obr.income_tax.{v}@2025", ledger_filter_new_dimension=v) + for v in ("a", "b") + ] + with pytest.raises(UnsupportedDiagnostics, match="breakdown name"): + run(national, national) + + +def _raw_pair(): + national, dense = default_pair() + national_raw = encode(diagnostics(national)) + dense_raw = encode(diagnostics(dense, n_records=150)) + return national_raw, dense_raw + + +def test_rejects_national_sha_mismatch(): + national_raw, dense_raw = _raw_pair() + bundle = bundle_for(national_raw) + tampered = national_raw.replace(b"325.0", b"326.0") + with pytest.raises(UnsupportedDiagnostics, match="national diagnostics sha256"): + extract(bundle, dense_manifest_for(dense_raw), tampered, dense_raw) + + +def test_rejects_dense_sha_mismatch(): + national_raw, dense_raw = _raw_pair() + manifest = dense_manifest_for(dense_raw) + tampered = dense_raw.replace(b"10.0", b"11.0") + with pytest.raises(UnsupportedDiagnostics, match="dense diagnostics sha256"): + extract(bundle_for(national_raw), manifest, national_raw, tampered) + + +def test_rejects_dense_manifest_not_served_by_the_bundle(): + national_raw, dense_raw = _raw_pair() + other_h5 = dense_manifest_for(dense_raw) + other_h5["artifacts"]["microcosm_uk_2024_25_dense"]["sha256"] = "d" * 64 + with pytest.raises(UnsupportedDiagnostics, match="microcosm_uk_2024_25_dense sha256"): + extract(bundle_for(national_raw), other_h5, national_raw, dense_raw) + other_tag = dense_manifest_for(dense_raw) + other_tag["build"]["cut_tag"] = "microcosm-uk-2024-25-dense-20250101T000000Z-cccccccc" + with pytest.raises(UnsupportedDiagnostics, match="not the bundle's local-area release"): + extract(bundle_for(national_raw), other_tag, national_raw, dense_raw) + + +def test_rejects_values_that_differ_between_releases(): + national = [ledger_target(value=325.0)] + dense = [ledger_target(value=330.0)] + with pytest.raises(UnsupportedDiagnostics, match="values differ"): + run(national, dense) + + +@pytest.mark.parametrize( + "change", + [ + {"schema_version": 5}, + {"weight_entity": "person"}, + {"n_records": 0}, + {"target_surface": {"n_targets": 99, "n_records": 100}}, + ], +) +def test_rejects_diagnostics_it_would_misread(change): + national, dense = default_pair() + national_raw = encode({**diagnostics(national), **change}) + dense_raw = encode(diagnostics(dense, n_records=150)) + with pytest.raises(UnsupportedDiagnostics): + extract(bundle_for(national_raw), dense_manifest_for(dense_raw), national_raw, dense_raw) + + +def test_rejects_non_finite_or_missing_values(): + for bad in (float("nan"), None, "1", True): + target = ledger_target() + target["target"] = bad + with pytest.raises(UnsupportedDiagnostics, match="target value"): + run([target], [copy.deepcopy(target)]) + + +RELEASE_FILES = ( + "UK_NATIONAL_DIAGNOSTICS", + "UK_DENSE_DIAGNOSTICS", + "UK_DENSE_RELEASE_MANIFEST", + "UK_BUNDLE_MANIFEST", +) + + +@pytest.mark.skipif( + not all(os.environ.get(v) for v in RELEASE_FILES), + reason="set " + ", ".join(RELEASE_FILES) + " to check the committed JSON", +) +def test_committed_json_is_the_extraction(): + output, _unused = extract( + json.loads(read_bytes(os.environ["UK_BUNDLE_MANIFEST"])), + json.loads(read_bytes(os.environ["UK_DENSE_RELEASE_MANIFEST"])), + read_bytes(os.environ["UK_NATIONAL_DIAGNOSTICS"]), + read_bytes(os.environ["UK_DENSE_DIAGNOSTICS"]), + ) + with open(COMMITTED) as f: + assert json.load(f) == json.loads(json.dumps(output)) diff --git a/src/data/ukCalibrationTargets.json b/src/data/ukCalibrationTargets.json new file mode 100644 index 0000000..f458ea4 --- /dev/null +++ b/src/data/ukCalibrationTargets.json @@ -0,0 +1,4788 @@ +{ + "schemaVersion": 1, + "country": "uk", + "policyengineVersion": "6.11.1", + "modelVersion": "2.151.0", + "calibrationYear": 2025, + "releases": { + "national": { + "buildId": "microcosm-uk-2024-25-national-20261025T091414Z-a6299504", + "dataset": "microcosm_uk_2024_25", + "diagnosticsSha256": "e61a60a1a8d57204be15600b2f98dbc864123589d48ede8414ebe43139d7c09b", + "households": 78234, + "targets": 1268 + }, + "localArea": { + "buildId": "microcosm-uk-2024-25-dense-20261026T105309Z-c9df4b86", + "dataset": "microcosm_uk_2024_25_dense", + "diagnosticsSha256": "44ab1c29d47c1899704be1a6f7036e00e2f281a7d305ecec69a77d0c51683e8d", + "households": 150000, + "targets": 24303 + } + }, + "totalTargets": { + "national": 1268, + "localArea": 24303, + "union": 24303 + }, + "levelCounts": { + "national": { + "uk": 368, + "gb": 192, + "englandAndWales": 4, + "nation": 198, + "region": 506, + "constituency": 0, + "localAuthority": 0, + "other": 0 + }, + "localArea": { + "uk": 368, + "gb": 195, + "englandAndWales": 4, + "nation": 200, + "region": 532, + "constituency": 12047, + "localAuthority": 10939, + "other": 18 + }, + "union": { + "uk": 368, + "gb": 195, + "englandAndWales": 4, + "nation": 200, + "region": 532, + "constituency": 12047, + "localAuthority": 10939, + "other": 18 + } + }, + "rows": [ + { + "key": "ledger|dwp.attendance_allowance_cases_with_entitlement|aa_award_type,age_bands_and_single_year,gender", + "scope": "national", + "label": "Attendance Allowance cases with entitlement", + "breakdown": null, + "geoLevels": [ + "nation" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 2, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "England", + "Wales" + ], + "targetCount": 2, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "DWP", + "sourceTables": [ + "Attendance Allowance cases with entitlement by country, award type, age band and gender, February 2023 to March 2026" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|obr.attendance_allowance|", + "scope": "national", + "label": "Attendance Allowance spending", + "breakdown": null, + "geoLevels": [ + "englandAndWales" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 1, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 8309942303.358463, + "nationalValueGeography": "England and Wales", + "unit": "gbp", + "source": "OBR", + "sourceTables": [ + "EFO March 2026 detailed forecast tables: expenditure" + ], + "period": 2025, + "notes": [ + "Calendar-year window of fiscal-year figures", + "The publisher’s forecast or projection" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|dwp.uc_capped_households_total|", + "scope": "national", + "label": "Benefit-capped households", + "breakdown": null, + "geoLevels": [ + "gb" + ], + "nationalCount": 0, + "gbCount": 1, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 110637.0, + "nationalValueGeography": "Great Britain", + "unit": "count", + "source": "DWP", + "sourceTables": [ + "Benefit cap: number of households capped to November 2025" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|dwp.uc_capped_households_by_band|", + "scope": "national", + "label": "Benefit-capped households", + "breakdown": "Monthly cap reduction", + "geoLevels": [ + "gb" + ], + "nationalCount": 0, + "gbCount": 14, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 14, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "DWP", + "sourceTables": [ + "Benefit cap: number of households capped to November 2025" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|scotgov.bus.government_support|funding_component", + "scope": "national", + "label": "Bus services: government support", + "breakdown": null, + "geoLevels": [ + "nation" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 1, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "Scotland" + ], + "targetCount": 1, + "nationalValue": 499000000.0, + "nationalValueGeography": "Scotland", + "unit": "gbp", + "source": "Scottish Government", + "sourceTables": [ + "Scottish Transport Statistics 2025 bus and coach reference tables" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|scotgov.bus.operator_revenue|revenue_component", + "scope": "national", + "label": "Bus services: passenger revenue", + "breakdown": null, + "geoLevels": [ + "nation" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 1, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "Scotland" + ], + "targetCount": 1, + "nationalValue": 391000000.0, + "nationalValueGeography": "Scotland", + "unit": "gbp", + "source": "Scottish Government", + "sourceTables": [ + "Scottish Transport Statistics 2025 bus and coach reference tables" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.cgt_badr_ir_taxpayers|cgt_badr_ir_gain_band,cgt_badr_ir_qualifying_gain,taxpayer_type", + "scope": "national", + "label": "Capital Gains Tax: Business Asset Disposal or Investors’ Relief claimants", + "breakdown": "Qualifying gain", + "geoLevels": [ + "uk" + ], + "nationalCount": 8, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 8, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "HMRC", + "sourceTables": [ + "Capital Gains Tax statistics Table 4: BADR and Investors' Relief by size of qualifying gain" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.cgt_gains_total|", + "scope": "national", + "label": "Capital Gains Tax: gains", + "breakdown": null, + "geoLevels": [ + "nation", + "region" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 3, + "regionCount": 9, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "Northern Ireland", + "Scotland", + "Wales" + ], + "targetCount": 12, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "gbp", + "source": "HMRC", + "sourceTables": [ + "Capital Gains Tax statistics Table 5: taxpayers, gains and liabilities by UK country and region" + ], + "period": 2025, + "notes": [ + "Rescaled to sum to its published higher-level control", + "Scaled by a ratio of published figures" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.cgt_gains_individuals|", + "scope": "national", + "label": "Capital Gains Tax: gains of individuals", + "breakdown": null, + "geoLevels": [ + "uk" + ], + "nationalCount": 1, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 119258000000.0, + "nationalValueGeography": "United Kingdom", + "unit": "gbp", + "source": "HMRC", + "sourceTables": [ + "Capital Gains Tax statistics Table 1: taxpayer numbers, gains and tax liabilities by year of disposal" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.cgt_gains_individuals|age,age_band", + "scope": "national", + "label": "Capital Gains Tax: gains of individuals", + "breakdown": "Age", + "geoLevels": [ + "uk" + ], + "nationalCount": 8, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 8, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "gbp", + "source": "HMRC", + "sourceTables": [ + "Capital Gains Tax statistics Table 6: individual taxpayers, gains and liabilities by age" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.cgt_gains_individuals|cgt_gain,cgt_gain_band", + "scope": "national", + "label": "Capital Gains Tax: gains of individuals", + "breakdown": "Size of gain", + "geoLevels": [ + "uk" + ], + "nationalCount": 12, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 12, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "gbp", + "source": "HMRC", + "sourceTables": [ + "Capital Gains Tax statistics Table 2: estimated number of taxpayers, amounts of gains and tax liabilities by size of gain" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.cgt_gains_individuals|cgt_taxable_income_band,taxable_income", + "scope": "national", + "label": "Capital Gains Tax: gains of individuals", + "breakdown": "Taxable income", + "geoLevels": [ + "uk" + ], + "nationalCount": 6, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 6, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "gbp", + "source": "HMRC", + "sourceTables": [ + "Capital Gains Tax statistics Table 3: individual taxpayers and gains by size of gain and taxable income" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.cgt_badr_ir_qualifying_gains|cgt_badr_ir_gain_band,cgt_badr_ir_qualifying_gain,taxpayer_type", + "scope": "national", + "label": "Capital Gains Tax: gains qualifying for Business Asset Disposal or Investors’ Relief", + "breakdown": "Qualifying gain", + "geoLevels": [ + "uk" + ], + "nationalCount": 8, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 8, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "gbp", + "source": "HMRC", + "sourceTables": [ + "Capital Gains Tax statistics Table 4: BADR and Investors' Relief by size of qualifying gain" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.cgt_taxpayers_individuals|", + "scope": "national", + "label": "Capital Gains Tax: individual taxpayers", + "breakdown": null, + "geoLevels": [ + "uk" + ], + "nationalCount": 1, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 551000.0, + "nationalValueGeography": "United Kingdom", + "unit": "count", + "source": "HMRC", + "sourceTables": [ + "Capital Gains Tax statistics Table 1: taxpayer numbers, gains and tax liabilities by year of disposal" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.cgt_taxpayers_individuals|age,age_band", + "scope": "national", + "label": "Capital Gains Tax: individual taxpayers", + "breakdown": "Age", + "geoLevels": [ + "uk" + ], + "nationalCount": 8, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 8, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "HMRC", + "sourceTables": [ + "Capital Gains Tax statistics Table 6: individual taxpayers, gains and liabilities by age" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.cgt_taxpayers_individuals|cgt_gain,cgt_gain_band", + "scope": "national", + "label": "Capital Gains Tax: individual taxpayers", + "breakdown": "Size of gain", + "geoLevels": [ + "uk" + ], + "nationalCount": 12, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 12, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "HMRC", + "sourceTables": [ + "Capital Gains Tax statistics Table 2: estimated number of taxpayers, amounts of gains and tax liabilities by size of gain" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.cgt_taxpayers_individuals|cgt_taxable_income_band,taxable_income", + "scope": "national", + "label": "Capital Gains Tax: individual taxpayers", + "breakdown": "Taxable income", + "geoLevels": [ + "uk" + ], + "nationalCount": 6, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 6, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "HMRC", + "sourceTables": [ + "Capital Gains Tax statistics Table 3: individual taxpayers and gains by size of gain and taxable income" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.cgt_tax_individuals|", + "scope": "national", + "label": "Capital Gains Tax: liabilities of individuals", + "breakdown": null, + "geoLevels": [ + "uk" + ], + "nationalCount": 1, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 22503000000.0, + "nationalValueGeography": "United Kingdom", + "unit": "gbp", + "source": "HMRC", + "sourceTables": [ + "Capital Gains Tax statistics Table 1: taxpayer numbers, gains and tax liabilities by year of disposal" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.cgt_tax_individuals|age,age_band", + "scope": "national", + "label": "Capital Gains Tax: liabilities of individuals", + "breakdown": "Age", + "geoLevels": [ + "uk" + ], + "nationalCount": 8, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 8, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "gbp", + "source": "HMRC", + "sourceTables": [ + "Capital Gains Tax statistics Table 6: individual taxpayers, gains and liabilities by age" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.cgt_residential_property_gains|channel", + "scope": "national", + "label": "Capital Gains Tax: residential property gains", + "breakdown": null, + "geoLevels": [ + "uk" + ], + "nationalCount": 1, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 12242481474.688189, + "nationalValueGeography": "United Kingdom", + "unit": "gbp", + "source": "HMRC", + "sourceTables": [ + "Capital Gains Tax statistics Table 8: residential property disposals" + ], + "period": 2025, + "notes": [ + "Scaled by a ratio of published figures" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.cgt_residential_property_taxpayers|channel", + "scope": "national", + "label": "Capital Gains Tax: residential property taxpayers", + "breakdown": null, + "geoLevels": [ + "uk" + ], + "nationalCount": 1, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 202630.0578034682, + "nationalValueGeography": "United Kingdom", + "unit": "count", + "source": "HMRC", + "sourceTables": [ + "Capital Gains Tax statistics Table 8: residential property disposals" + ], + "period": 2025, + "notes": [ + "Scaled by a ratio of published figures" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.cgt_taxpayers_total|", + "scope": "national", + "label": "Capital Gains Tax: taxpayers", + "breakdown": null, + "geoLevels": [ + "nation", + "region" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 3, + "regionCount": 9, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "Northern Ireland", + "Scotland", + "Wales" + ], + "targetCount": 12, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "HMRC", + "sourceTables": [ + "Capital Gains Tax statistics Table 5: taxpayers, gains and liabilities by UK country and region" + ], + "period": 2025, + "notes": [ + "Rescaled to sum to its published higher-level control", + "Scaled by a ratio of published figures" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|obr.carers_allowance|", + "scope": "national", + "label": "Carer’s Allowance spending", + "breakdown": null, + "geoLevels": [ + "englandAndWales" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 1, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 4487029829.347929, + "nationalValueGeography": "England and Wales", + "unit": "gbp", + "source": "OBR", + "sourceTables": [ + "EFO March 2026 detailed forecast tables: expenditure" + ], + "period": 2025, + "notes": [ + "Calendar-year window of fiscal-year figures", + "The publisher’s forecast or projection" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|obr.child_benefit|", + "scope": "national", + "label": "Child Benefit spending", + "breakdown": null, + "geoLevels": [ + "uk" + ], + "nationalCount": 1, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 13368730968.878168, + "nationalValueGeography": "United Kingdom", + "unit": "gbp", + "source": "OBR", + "sourceTables": [ + "EFO March 2026 detailed forecast tables: expenditure" + ], + "period": 2025, + "notes": [ + "Calendar-year window of fiscal-year figures", + "The publisher’s forecast or projection" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|dwp.uc_tcl_headline_children_within_affected_households|two_child_limit_status", + "scope": "national", + "label": "Children in households affected by the two-child limit", + "breakdown": null, + "geoLevels": [ + "gb" + ], + "nationalCount": 0, + "gbCount": 1, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 1665540.0, + "nationalValueGeography": "Great Britain", + "unit": "count", + "source": "DWP", + "sourceTables": [ + "Universal Credit claimants: statistics on the two-child limit policy, April 2025 data tables" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|dwp.uc_tcl_children_by_disability|two_child_limit_status", + "scope": "national", + "label": "Children in households affected by the two-child limit", + "breakdown": "Disability", + "geoLevels": [ + "gb" + ], + "nationalCount": 0, + "gbCount": 2, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 2, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "DWP", + "sourceTables": [ + "Universal Credit claimants: statistics on the two-child limit policy, April 2025 data tables" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|dwp.uc_tcl_children_by_children|two_child_limit_status", + "scope": "national", + "label": "Children in households affected by the two-child limit", + "breakdown": "Number of children", + "geoLevels": [ + "gb" + ], + "nationalCount": 0, + "gbCount": 4, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 4, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "DWP", + "sourceTables": [ + "Universal Credit claimants: statistics on the two-child limit policy, April 2025 data tables" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|dwp.uc_tcl_headline_children_affected_by_policy|two_child_limit_status", + "scope": "national", + "label": "Children not receiving the child element under the two-child limit", + "breakdown": null, + "geoLevels": [ + "gb" + ], + "nationalCount": 0, + "gbCount": 1, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 597920.0, + "nationalValueGeography": "Great Britain", + "unit": "count", + "source": "DWP", + "sourceTables": [ + "Universal Credit claimants: statistics on the two-child limit policy, April 2025 data tables" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|mhclg.council_taxbase.empty_dwellings|", + "scope": "national", + "label": "Council Tax dwelling stock", + "breakdown": null, + "geoLevels": [ + "region" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 9, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 9, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "MHCLG", + "sourceTables": [ + "Council Taxbase: Local authority level data for 2025 (revised), Council Taxbase Data worksheet" + ], + "period": 2025, + "notes": [ + "Rescaled to sum to its published higher-level control" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|scotgov.chargeable_dwellings_total|", + "scope": "national", + "label": "Council Tax dwelling stock", + "breakdown": null, + "geoLevels": [ + "nation" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 1, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "Scotland" + ], + "targetCount": 1, + "nationalValue": 2570799.0, + "nationalValueGeography": "Scotland", + "unit": "count", + "source": "Scottish Government", + "sourceTables": [ + "Number of chargeable dwellings, September 2025 (CTAXBASE 2025)" + ], + "period": 2025, + "notes": [ + "Rescaled to sum to its published higher-level control" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|welshgov.council_tax_dwellings.total_chargeable_second_homes|", + "scope": "national", + "label": "Council Tax dwelling stock", + "breakdown": null, + "geoLevels": [ + "nation" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 1, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "Wales" + ], + "targetCount": 1, + "nationalValue": 1387333.6411848539, + "nationalValueGeography": "Wales", + "unit": "count", + "source": "Welsh Government", + "sourceTables": [ + "StatsWales: Council tax dwellings by authority and band (CT1 returns), full-dataset CSV export" + ], + "period": 2025, + "notes": [ + "Rescaled to sum to its published higher-level control" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|mhclg.council_taxbase.empty_dwellings|council_tax_band", + "scope": "national", + "label": "Council Tax dwelling stock", + "breakdown": "Council tax band", + "geoLevels": [ + "region" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 72, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 72, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "MHCLG", + "sourceTables": [ + "Council Taxbase: Local authority level data for 2025 (revised), Council Taxbase Data worksheet" + ], + "period": 2025, + "notes": [ + "Rescaled to sum to its published higher-level control" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|scotgov.chargeable_dwellings_by_band|", + "scope": "national", + "label": "Council Tax dwelling stock", + "breakdown": "Council tax band", + "geoLevels": [ + "nation" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 8, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "Scotland" + ], + "targetCount": 8, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "Scottish Government", + "sourceTables": [ + "Number of chargeable dwellings, September 2025 (CTAXBASE 2025)" + ], + "period": 2025, + "notes": [ + "Rescaled to sum to its published higher-level control" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|welshgov.council_tax_dwellings.total_chargeable_second_homes|council_tax_band", + "scope": "national", + "label": "Council Tax dwelling stock", + "breakdown": "Council tax band", + "geoLevels": [ + "nation" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 9, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "Wales" + ], + "targetCount": 9, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "Welsh Government", + "sourceTables": [ + "StatsWales: Council tax dwellings by authority and band (CT1 returns), full-dataset CSV export" + ], + "period": 2025, + "notes": [ + "Rescaled to sum to its published higher-level control" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|obr.council_tax_receipts_england_scotland_wales|", + "scope": "national", + "label": "Council Tax receipts (England, Scotland and Wales)", + "breakdown": null, + "geoLevels": [ + "uk" + ], + "nationalCount": 1, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 48774253146.51462, + "nationalValueGeography": "United Kingdom", + "unit": "gbp", + "source": "OBR", + "sourceTables": [ + "EFO March 2026 detailed forecast tables: expenditure" + ], + "period": 2025, + "notes": [ + "Calendar-year window of fiscal-year figures", + "The publisher’s forecast or projection" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|obr.dla_and_pip|", + "scope": "national", + "label": "Disability Living Allowance and Personal Independence Payment spending", + "breakdown": null, + "geoLevels": [ + "englandAndWales" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 1, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 36103194307.639694, + "nationalValueGeography": "England and Wales", + "unit": "gbp", + "source": "OBR", + "sourceTables": [ + "EFO March 2026 detailed forecast tables: expenditure" + ], + "period": 2025, + "notes": [ + "Calendar-year window of fiscal-year figures", + "The publisher’s forecast or projection" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|mhclg.council_taxbase.single_adult_discount_dwellings|", + "scope": "national", + "label": "Dwellings with a Council Tax single-adult discount", + "breakdown": null, + "geoLevels": [ + "nation" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 1, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "England" + ], + "targetCount": 1, + "nationalValue": 8496178.0, + "nationalValueGeography": "England", + "unit": "count", + "source": "MHCLG", + "sourceTables": [ + "Council Taxbase: Local authority level data for 2025 (revised), Council Taxbase Data worksheet" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|obr.ni_employee|", + "scope": "national", + "label": "Employee National Insurance contributions", + "breakdown": null, + "geoLevels": [ + "uk" + ], + "nationalCount": 1, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 49002029830.141045, + "nationalValueGeography": "United Kingdom", + "unit": "gbp", + "source": "OBR", + "sourceTables": [ + "EFO March 2026 detailed forecast tables: receipts" + ], + "period": 2025, + "notes": [ + "Calendar-year window of fiscal-year figures", + "The publisher’s forecast or projection" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|dwp.workplace_pension_annual_saving|price_basis,saver_population,saving_component,sector", + "scope": "national", + "label": "Employer workplace pension contributions", + "breakdown": null, + "geoLevels": [ + "gb" + ], + "nationalCount": 0, + "gbCount": 1, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 104200000000.0, + "nationalValueGeography": "Great Britain", + "unit": "gbp", + "source": "DWP", + "sourceTables": [ + "Workplace pension participation and savings trends of employees: 2009 to 2025" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|dwp.esa_claimants|payment_type,phase_of_esa_claim", + "scope": "national", + "label": "Employment and Support Allowance claimants", + "breakdown": "Payment type", + "geoLevels": [ + "gb" + ], + "nationalCount": 0, + "gbCount": 3, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 3, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "DWP", + "sourceTables": [ + "Employment and Support Allowance caseload by payment type and phase of claim, May 2018 to March 2026" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|dfe.funded_childcare_registered_children_total|age,entitlement_type,provision,registration_basis", + "scope": "national", + "label": "Funded early education and childcare: registered children", + "breakdown": "Age × entitlement", + "geoLevels": [ + "nation" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 7, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "England" + ], + "targetCount": 7, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "DfE", + "sourceTables": [ + "Funded early education and childcare 2026, Headline figures" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|dfe.funded_childcare_registered_children_nursery|age,entitlement_type,provision,registration_basis", + "scope": "national", + "label": "Funded early education: 3-year-olds using the universal hours at a nursery", + "breakdown": null, + "geoLevels": [ + "nation" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 1, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "England" + ], + "targetCount": 1, + "nationalValue": 565954.0, + "nationalValueGeography": "England", + "unit": "count", + "source": "DfE", + "sourceTables": [ + "Funded early education and childcare 2026, Headline figures" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|ons.household_expenditure.personal_transport_fuels_lubricants|coicop,consumption_concept,frequency,price_basis,published_unit,seasonal_adjustment,source_sheet", + "scope": "national", + "label": "Household spending on fuels and lubricants for personal transport", + "breakdown": null, + "geoLevels": [ + "uk" + ], + "nationalCount": 1, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 37025000000.0, + "nationalValueGeography": "United Kingdom", + "unit": "gbp", + "source": "ONS", + "sourceTables": [ + "Consumer Trends current price, not seasonally adjusted, 0CN, 01CN-12CN and TOURCN" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|nisra.census2021_households|", + "scope": "national", + "label": "Households", + "breakdown": null, + "geoLevels": [ + "nation" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 1, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "Northern Ireland" + ], + "targetCount": 1, + "nationalValue": 794887.0879380013, + "nationalValueGeography": "Northern Ireland", + "unit": "count", + "source": "NISRA", + "sourceTables": [ + "NISRA Census 2021 flexible table: number of households by LGD14" + ], + "period": 2025, + "notes": [ + "Rolled forward by a ratio of published figures" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|nrs.households_total|", + "scope": "national", + "label": "Households", + "breakdown": null, + "geoLevels": [ + "nation" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 1, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "Scotland" + ], + "targetCount": 1, + "nationalValue": 2570799.0, + "nationalValueGeography": "Scotland", + "unit": "count", + "source": "National Records of Scotland", + "sourceTables": [ + "NRS households and dwellings by Scottish council area, 2024 and 2025" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|ons.households_total|", + "scope": "national", + "label": "Households", + "breakdown": null, + "geoLevels": [ + "region" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 9, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 9, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "ONS", + "sourceTables": [ + "Households by type of household and family, regions of England and GB constituent countries, Table 7" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|welshgov.households_total|Data description,Household Type", + "scope": "national", + "label": "Households", + "breakdown": null, + "geoLevels": [ + "nation" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 1, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "Wales" + ], + "targetCount": 1, + "nationalValue": 1387333.6411848539, + "nationalValueGeography": "Wales", + "unit": "count", + "source": "Welsh Government", + "sourceTables": [ + "Welsh Government household estimates, mid-2022 to mid-2024 (revised series)" + ], + "period": 2025, + "notes": [ + "Rolled forward by a ratio of published figures" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|ons.households_by_type|household_type", + "scope": "national", + "label": "Households", + "breakdown": "Household type", + "geoLevels": [ + "uk" + ], + "nationalCount": 10, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 10, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "ONS", + "sourceTables": [ + "Families and households in the UK 2025, Tables 1, 3, 5 and 7: families by type and presence of children, families with dependent children by number, household size and households by type" + ], + "period": 2025, + "notes": [ + "Rescaled to sum to its published higher-level control" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|dwp.uc_tcl_headline_households_affected|two_child_limit_status", + "scope": "national", + "label": "Households affected by the two-child limit", + "breakdown": null, + "geoLevels": [ + "gb" + ], + "nationalCount": 0, + "gbCount": 1, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 469780.0, + "nationalValueGeography": "Great Britain", + "unit": "count", + "source": "DWP", + "sourceTables": [ + "Universal Credit claimants: statistics on the two-child limit policy, April 2025 data tables" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|dwp.uc_tcl_households_by_disability|two_child_limit_status", + "scope": "national", + "label": "Households affected by the two-child limit", + "breakdown": "Disability", + "geoLevels": [ + "gb" + ], + "nationalCount": 0, + "gbCount": 2, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 2, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "DWP", + "sourceTables": [ + "Universal Credit claimants: statistics on the two-child limit policy, April 2025 data tables" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|dwp.uc_tcl_households_by_children|two_child_limit_status", + "scope": "national", + "label": "Households affected by the two-child limit", + "breakdown": "Number of children", + "geoLevels": [ + "gb" + ], + "nationalCount": 0, + "gbCount": 4, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 4, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "DWP", + "sourceTables": [ + "Universal Credit claimants: statistics on the two-child limit policy, April 2025 data tables" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|dwp.hb_claimants|client_type,private_or_social_rented", + "scope": "national", + "label": "Housing Benefit claimants of pension age", + "breakdown": "Private or social rented", + "geoLevels": [ + "gb" + ], + "nationalCount": 0, + "gbCount": 3, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 3, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "DWP", + "sourceTables": [ + "Housing Benefit claimants by client type and tenure, January 2023 to February 2026" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|dwp.benefit_expenditure_amount|benefit_family,benefit_forecast_line", + "scope": "national", + "label": "Housing Benefit spending, pension age", + "breakdown": null, + "geoLevels": [ + "gb" + ], + "nationalCount": 0, + "gbCount": 1, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 7048818510.96767, + "nationalValueGeography": "Great Britain", + "unit": "gbp", + "source": "DWP", + "sourceTables": [ + "DWP benefit expenditure and caseload tables 2026: outturn and forecast, Spring Forecast 2026" + ], + "period": 2025, + "notes": [ + "Calendar-year window of fiscal-year figures", + "The publisher’s forecast or projection" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|obr.incapacity_benefits|", + "scope": "national", + "label": "Incapacity benefits spending", + "breakdown": null, + "geoLevels": [ + "gb" + ], + "nationalCount": 0, + "gbCount": 1, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 8507979735.87899, + "nationalValueGeography": "Great Britain", + "unit": "gbp", + "source": "OBR", + "sourceTables": [ + "EFO March 2026 detailed forecast tables: expenditure" + ], + "period": 2025, + "notes": [ + "Calendar-year window of fiscal-year figures", + "The publisher’s forecast or projection" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.spi_total_tax_amount|sex,total_income", + "scope": "national", + "label": "Income Tax liabilities", + "breakdown": "Total income band", + "geoLevels": [ + "nation", + "region" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 30, + "regionCount": 90, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "Northern Ireland", + "Scotland", + "Wales" + ], + "targetCount": 120, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "gbp", + "source": "HMRC", + "sourceTables": [ + "SPI collated Tables 3.1 to 3.11, tax year 2023-24: Table 3.11 income and tax by sex, region and country" + ], + "period": 2025, + "notes": [ + "Rescaled to sum to its published higher-level control", + "Uprated to 2025 with HMRC Income Tax liabilities statistics (tax growth by income band)" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.spi_total_tax_amount|total_income", + "scope": "national", + "label": "Income Tax liabilities", + "breakdown": "Total income band", + "geoLevels": [ + "uk" + ], + "nationalCount": 11, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 11, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "gbp", + "source": "HMRC", + "sourceTables": [ + "HMRC Income Tax liabilities statistics, collated Tables 2.1 to 2.6: tax year 2023-24 to tax year 2026-27" + ], + "period": 2025, + "notes": [ + "Calendar-year window of fiscal-year figures", + "The publisher’s forecast or projection" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.spi_taxpayer_count|marginal_rate", + "scope": "national", + "label": "Income Tax payers", + "breakdown": "Marginal tax rate", + "geoLevels": [ + "nation", + "region" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 6, + "regionCount": 18, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "Northern Ireland", + "Scotland", + "Wales" + ], + "targetCount": 24, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "HMRC", + "sourceTables": [ + "HMRC Income Tax liabilities statistics, collated Tables 2.1 to 2.6: tax year 2023-24 to tax year 2026-27" + ], + "period": 2025, + "notes": [ + "Calendar-year window of fiscal-year figures", + "The publisher’s forecast or projection" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.spi_taxpayer_count|total_income", + "scope": "national", + "label": "Income Tax payers", + "breakdown": "Total income band", + "geoLevels": [ + "uk" + ], + "nationalCount": 11, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 11, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "HMRC", + "sourceTables": [ + "HMRC Income Tax liabilities statistics, collated Tables 2.1 to 2.6: tax year 2023-24 to tax year 2026-27" + ], + "period": 2025, + "notes": [ + "Calendar-year window of fiscal-year figures", + "The publisher’s forecast or projection" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|obr.income_tax|", + "scope": "national", + "label": "Income Tax receipts", + "breakdown": null, + "geoLevels": [ + "uk" + ], + "nationalCount": 1, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 325054437305.83215, + "nationalValueGeography": "United Kingdom", + "unit": "gbp", + "source": "OBR", + "sourceTables": [ + "EFO March 2026 detailed forecast tables: receipts" + ], + "period": 2025, + "notes": [ + "Calendar-year window of fiscal-year figures", + "The publisher’s forecast or projection" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.spi_dividend_income_amount|total_income", + "scope": "national", + "label": "Income: dividends", + "breakdown": "Total income band", + "geoLevels": [ + "uk" + ], + "nationalCount": 12, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 12, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "gbp", + "source": "HMRC", + "sourceTables": [ + "SPI collated Tables 3.1 to 3.11, tax year 2023-24: income by range of total income" + ], + "period": 2025, + "notes": [ + "Uprated to 2025 with OBR per-capita GDP (policyengine-uk)" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.spi_employment_income_amount|total_income", + "scope": "national", + "label": "Income: employment", + "breakdown": "Total income band", + "geoLevels": [ + "uk" + ], + "nationalCount": 12, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 12, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "gbp", + "source": "HMRC", + "sourceTables": [ + "SPI collated Tables 3.1 to 3.11, tax year 2023-24: income by range of total income" + ], + "period": 2025, + "notes": [ + "Uprated to 2025 with HMRC Income Tax liabilities statistics (income growth by income band)" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.spi_dividend_income_count|total_income", + "scope": "national", + "label": "Income: people with dividends", + "breakdown": "Total income band", + "geoLevels": [ + "uk" + ], + "nationalCount": 12, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 12, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "HMRC", + "sourceTables": [ + "SPI collated Tables 3.1 to 3.11, tax year 2023-24: income by range of total income" + ], + "period": 2025, + "notes": [ + "Uprated to 2025 with HMRC Income Tax liabilities statistics (taxpayer growth by income band)" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.spi_employment_income_count|total_income", + "scope": "national", + "label": "Income: people with employment income", + "breakdown": "Total income band", + "geoLevels": [ + "uk" + ], + "nationalCount": 13, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 13, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "HMRC", + "sourceTables": [ + "SPI collated Tables 3.1 to 3.11, tax year 2023-24: income by range of total income" + ], + "period": 2025, + "notes": [ + "Uprated to 2025 with HMRC Income Tax liabilities statistics (taxpayer growth by income band)" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.spi_total_income_before_tax_count|sex,total_income", + "scope": "national", + "label": "Income: people with income", + "breakdown": "Total income band", + "geoLevels": [ + "nation", + "region" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 30, + "regionCount": 90, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "Northern Ireland", + "Scotland", + "Wales" + ], + "targetCount": 120, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "HMRC", + "sourceTables": [ + "SPI collated Tables 3.1 to 3.11, tax year 2023-24: Table 3.11 income and tax by sex, region and country" + ], + "period": 2025, + "notes": [ + "Rescaled to sum to its published higher-level control", + "Uprated to 2025 with HMRC Income Tax liabilities statistics (taxpayer growth by income band)" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.spi_private_pension_income_count|total_income", + "scope": "national", + "label": "Income: people with private pension income", + "breakdown": "Total income band", + "geoLevels": [ + "uk" + ], + "nationalCount": 12, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 12, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "HMRC", + "sourceTables": [ + "SPI collated Tables 3.1 to 3.11, tax year 2023-24: income by range of total income" + ], + "period": 2025, + "notes": [ + "Uprated to 2025 with HMRC Income Tax liabilities statistics (taxpayer growth by income band)" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.spi_property_income_count|total_income", + "scope": "national", + "label": "Income: people with property income", + "breakdown": "Total income band", + "geoLevels": [ + "uk" + ], + "nationalCount": 12, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 12, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "HMRC", + "sourceTables": [ + "SPI collated Tables 3.1 to 3.11, tax year 2023-24: income by range of total income" + ], + "period": 2025, + "notes": [ + "Uprated to 2025 with HMRC Income Tax liabilities statistics (taxpayer growth by income band)" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.spi_savings_interest_income_count|total_income", + "scope": "national", + "label": "Income: people with savings interest", + "breakdown": "Total income band", + "geoLevels": [ + "uk" + ], + "nationalCount": 13, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 13, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "HMRC", + "sourceTables": [ + "SPI collated Tables 3.1 to 3.11, tax year 2023-24: income by range of total income" + ], + "period": 2025, + "notes": [ + "Uprated to 2025 with HMRC Income Tax liabilities statistics (taxpayer growth by income band)" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.spi_self_employment_income_count|total_income", + "scope": "national", + "label": "Income: people with self-employment income", + "breakdown": "Total income band", + "geoLevels": [ + "uk" + ], + "nationalCount": 12, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 12, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "HMRC", + "sourceTables": [ + "SPI collated Tables 3.1 to 3.11, tax year 2023-24: income by range of total income" + ], + "period": 2025, + "notes": [ + "Uprated to 2025 with HMRC Income Tax liabilities statistics (taxpayer growth by income band)" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.spi_state_pension_count|total_income", + "scope": "national", + "label": "Income: people with State Pension income", + "breakdown": "Total income band", + "geoLevels": [ + "uk" + ], + "nationalCount": 12, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 12, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "HMRC", + "sourceTables": [ + "SPI collated Tables 3.1 to 3.11, tax year 2023-24: income by range of total income" + ], + "period": 2025, + "notes": [ + "Uprated to 2025 with HMRC Income Tax liabilities statistics (taxpayer growth by income band)" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.spi_private_pension_income_amount|total_income", + "scope": "national", + "label": "Income: private pensions", + "breakdown": "Total income band", + "geoLevels": [ + "uk" + ], + "nationalCount": 12, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 12, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "gbp", + "source": "HMRC", + "sourceTables": [ + "SPI collated Tables 3.1 to 3.11, tax year 2023-24: income by range of total income" + ], + "period": 2025, + "notes": [ + "Uprated to 2025 with HMRC Income Tax liabilities statistics (income growth by income band)" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.spi_property_income_amount|total_income", + "scope": "national", + "label": "Income: property", + "breakdown": "Total income band", + "geoLevels": [ + "uk" + ], + "nationalCount": 13, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 13, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "gbp", + "source": "HMRC", + "sourceTables": [ + "SPI collated Tables 3.1 to 3.11, tax year 2023-24: income by range of total income" + ], + "period": 2025, + "notes": [ + "Uprated to 2025 with HMRC Income Tax liabilities statistics (income growth by income band)" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.spi_savings_interest_income_amount|total_income", + "scope": "national", + "label": "Income: savings interest", + "breakdown": "Total income band", + "geoLevels": [ + "uk" + ], + "nationalCount": 13, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 13, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "gbp", + "source": "HMRC", + "sourceTables": [ + "SPI collated Tables 3.1 to 3.11, tax year 2023-24: income by range of total income" + ], + "period": 2025, + "notes": [ + "Uprated to 2025 with policyengine-uk’s household interest income index, then OBR per-capita GDP" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.spi_self_employment_income_amount|total_income", + "scope": "national", + "label": "Income: self-employment", + "breakdown": "Total income band", + "geoLevels": [ + "uk" + ], + "nationalCount": 12, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 12, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "gbp", + "source": "HMRC", + "sourceTables": [ + "SPI collated Tables 3.1 to 3.11, tax year 2023-24: income by range of total income" + ], + "period": 2025, + "notes": [ + "Uprated to 2025 with HMRC Income Tax liabilities statistics (income growth by income band)" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.spi_state_pension_amount|total_income", + "scope": "national", + "label": "Income: State Pension", + "breakdown": "Total income band", + "geoLevels": [ + "uk" + ], + "nationalCount": 12, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 12, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "gbp", + "source": "HMRC", + "sourceTables": [ + "SPI collated Tables 3.1 to 3.11, tax year 2023-24: income by range of total income" + ], + "period": 2025, + "notes": [ + "Uprated to 2025 with HMRC Income Tax liabilities statistics (taxpayer growth by income band) and the new State Pension rate" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.spi_total_income_before_tax_amount|sex,total_income", + "scope": "national", + "label": "Income: total before tax", + "breakdown": "Total income band", + "geoLevels": [ + "nation", + "region" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 30, + "regionCount": 90, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "Northern Ireland", + "Scotland", + "Wales" + ], + "targetCount": 120, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "gbp", + "source": "HMRC", + "sourceTables": [ + "SPI collated Tables 3.1 to 3.11, tax year 2023-24: Table 3.11 income and tax by sex, region and country" + ], + "period": 2025, + "notes": [ + "Rescaled to sum to its published higher-level control", + "Uprated to 2025 with HMRC Income Tax liabilities statistics (income growth by income band)" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.spi_total_income_before_tax_amount|total_income", + "scope": "national", + "label": "Income: total before tax", + "breakdown": "Total income band", + "geoLevels": [ + "uk" + ], + "nationalCount": 11, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 11, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "gbp", + "source": "HMRC", + "sourceTables": [ + "HMRC Income Tax liabilities statistics, collated Tables 2.1 to 2.6: tax year 2023-24 to tax year 2026-27" + ], + "period": 2025, + "notes": [ + "Calendar-year window of fiscal-year figures", + "The publisher’s forecast or projection" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|obr.jobseekers_allowance|", + "scope": "national", + "label": "Jobseeker’s Allowance spending", + "breakdown": null, + "geoLevels": [ + "gb" + ], + "nationalCount": 0, + "gbCount": 1, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 288407204.431046, + "nationalValueGeography": "Great Britain", + "unit": "gbp", + "source": "OBR", + "sourceTables": [ + "EFO March 2026 detailed forecast tables: expenditure" + ], + "period": 2025, + "notes": [ + "Calendar-year window of fiscal-year figures", + "The publisher’s forecast or projection" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|ons.nbs_land_value_households|publication_status", + "scope": "national", + "label": "Land held by households (national balance sheet)", + "breakdown": null, + "geoLevels": [ + "uk" + ], + "nationalCount": 1, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 4485171000000.0, + "nationalValueGeography": "United Kingdom", + "unit": "gbp", + "source": "ONS", + "sourceTables": [ + "Preliminary UK national balance sheet estimates, 10 June 2026, Tables 2, 3 and 11" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|ons.nbs_land_value_nfc|publication_status", + "scope": "national", + "label": "Land held by non-financial corporations (national balance sheet)", + "breakdown": null, + "geoLevels": [ + "uk" + ], + "nationalCount": 1, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 2063534000000.0, + "nationalValueGeography": "United Kingdom", + "unit": "gbp", + "source": "ONS", + "sourceTables": [ + "Preliminary UK national balance sheet estimates, 10 June 2026, Tables 2, 3 and 11" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|ons.nbs_land_value_total|publication_status", + "scope": "national", + "label": "Land, all sectors (national balance sheet)", + "breakdown": null, + "geoLevels": [ + "uk" + ], + "nationalCount": 1, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 6872455000000.0, + "nationalValueGeography": "United Kingdom", + "unit": "gbp", + "source": "ONS", + "sourceTables": [ + "Preliminary UK national balance sheet estimates, 10 June 2026, Tables 2, 3 and 11" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|welshgov.local_transport.gross_expenditure|expenditure_component,service_line", + "scope": "national", + "label": "Local authority support to bus operators", + "breakdown": null, + "geoLevels": [ + "nation" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 1, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "Wales" + ], + "targetCount": 1, + "nationalValue": 131489970.0, + "nationalValueGeography": "Wales", + "unit": "gbp", + "source": "Welsh Government", + "sourceTables": [ + "Roads and transport revenue outturn expenditure by authority and service" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|dft.local_bus_passenger_fare_receipts|", + "scope": "national", + "label": "Local bus passenger fare receipts", + "breakdown": null, + "geoLevels": [ + "nation", + "region" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 1, + "regionCount": 1, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "England" + ], + "targetCount": 2, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "gbp", + "source": "DfT", + "sourceTables": [ + "BUS05i estimated operating revenue and net support for local bus services" + ], + "period": 2025, + "notes": [ + "Uprated to 2025 with the DfT local bus fares index" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|dft.local_bus_passenger_journeys|", + "scope": "national", + "label": "Local bus passenger journeys", + "breakdown": null, + "geoLevels": [ + "region" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 9, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 9, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "DfT", + "sourceTables": [ + "BUS01a passenger journeys and BUS01c concessionary passenger journeys" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|dft.local_bus_total_estimated_net_support|", + "scope": "national", + "label": "Local bus services: estimated net government support", + "breakdown": null, + "geoLevels": [ + "nation", + "region" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 1, + "regionCount": 1, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "England" + ], + "targetCount": 2, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "gbp", + "source": "DfT", + "sourceTables": [ + "BUS05i estimated operating revenue and net support for local bus services" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|uk.dwelling.count|microcosm.value_threshold,uk.dwelling.tenure", + "scope": "national", + "label": "Owner-occupied homes above high-value thresholds", + "breakdown": "Value threshold", + "geoLevels": [ + "nation", + "region" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 2, + "regionCount": 26, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "Wales" + ], + "targetCount": 28, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "Microcosm derivation from open data (HM Land Registry Price Paid, UK HPI, ONS NSPL, VOA council tax stock, Census 2021 tenure)", + "sourceTables": [ + "HM Land Registry Price Paid Data, UK HPI and ONS NSPL, Microcosm derivation" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": true + }, + { + "key": "ledger|dwp.pension_credit_benefit_units|", + "scope": "national", + "label": "Pension Credit caseload", + "breakdown": null, + "geoLevels": [ + "nation" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 1, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "Northern Ireland" + ], + "targetCount": 1, + "nationalValue": 60195.0, + "nationalValueGeography": "Northern Ireland", + "unit": "count", + "source": "Department for Communities (NI)", + "sourceTables": [ + "Benefits Statistics Summary Publication May 2026: Pension Credit workbook" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|dwp.pension_credit_benefit_units|age,age_bands_and_single_year,partner_indicator,type_of_pension_credit", + "scope": "national", + "label": "Pension Credit caseload", + "breakdown": "Age", + "geoLevels": [ + "gb" + ], + "nationalCount": 0, + "gbCount": 6, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 6, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "DWP", + "sourceTables": [ + "Pension Credit caseload and mean weekly award by type of Pension Credit, partner indicator and age band, February 2023 to March 2026" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|dwp.pension_credit_benefit_units|age_bands_and_single_year,partner_indicator,type_of_pension_credit", + "scope": "national", + "label": "Pension Credit caseload", + "breakdown": "Single or couple × Pension Credit type", + "geoLevels": [ + "gb" + ], + "nationalCount": 0, + "gbCount": 7, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 7, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "DWP", + "sourceTables": [ + "Pension Credit caseload and mean weekly award by type of Pension Credit, partner indicator and age band, February 2023 to March 2026" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|obr.pension_credit|", + "scope": "national", + "label": "Pension Credit spending", + "breakdown": null, + "geoLevels": [ + "gb" + ], + "nationalCount": 0, + "gbCount": 1, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 6112217133.109846, + "nationalValueGeography": "Great Britain", + "unit": "gbp", + "source": "OBR", + "sourceTables": [ + "EFO March 2026 detailed forecast tables: expenditure" + ], + "period": 2025, + "notes": [ + "Calendar-year window of fiscal-year figures", + "The publisher’s forecast or projection" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|slc.student_loan_borrowers|borrower_status,repayment_plan,row_group", + "scope": "national", + "label": "Plan 5 student loan borrowers", + "breakdown": "Borrower status", + "geoLevels": [ + "nation" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 2, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "England" + ], + "targetCount": 2, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "Student Loans Company", + "sourceTables": [ + "Student loan forecasts for England, Table 6a: forecast borrowers liable to repay and earning above threshold, by product" + ], + "period": 2025, + "notes": [ + "The publisher’s forecast or projection" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|ons.mid_year_population_estimate|", + "scope": "national", + "label": "Population", + "breakdown": null, + "geoLevels": [ + "uk" + ], + "nationalCount": 1, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 69483939.0, + "nationalValueGeography": "United Kingdom", + "unit": "count", + "source": "ONS", + "sourceTables": [ + "ONS mid-2025 population estimates: UK, constituent countries and English regions by single year of age and sex" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|ons.mid_year_population_estimate|age", + "scope": "national", + "label": "Population", + "breakdown": "Age", + "geoLevels": [ + "uk", + "nation", + "region" + ], + "nationalCount": 7, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 36, + "regionCount": 81, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "England", + "Northern Ireland", + "Scotland", + "Wales" + ], + "targetCount": 124, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "ONS", + "sourceTables": [ + "ONS mid-2025 population estimates: UK, constituent countries and English regions by single year of age and sex" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|ons.mid_year_population_estimate|age,sex", + "scope": "national", + "label": "Population", + "breakdown": "Age × sex", + "geoLevels": [ + "uk" + ], + "nationalCount": 42, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 42, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "ONS", + "sourceTables": [ + "ONS mid-2025 population estimates: UK, constituent countries and English regions by single year of age and sex" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|ons.pse_headcount_total_public_sector|", + "scope": "national", + "label": "Public sector employment", + "breakdown": null, + "geoLevels": [ + "uk" + ], + "nationalCount": 1, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 6176000.0, + "nationalValueGeography": "United Kingdom", + "unit": "count", + "source": "ONS", + "sourceTables": [ + "ONS public sector employment reference table, June 2026 edition, Table 1" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|nithc.dfi_transaction_value|funding_component,mode_scope", + "scope": "national", + "label": "Public transport Public Service Obligation compensation", + "breakdown": null, + "geoLevels": [ + "nation" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 1, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "Northern Ireland" + ], + "targetCount": 1, + "nationalValue": 111700000.0, + "nationalValueGeography": "Northern Ireland", + "unit": "gbp", + "source": "NI Transport Holding Company", + "sourceTables": [ + "Northern Ireland Transport Holding Company Annual Report and Accounts 2024/25, note 23 Related Party Transactions" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|isc.pupils_at_member_schools|school_membership", + "scope": "national", + "label": "Pupils at Independent Schools Council schools", + "breakdown": null, + "geoLevels": [ + "uk" + ], + "nationalCount": 1, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 545640.0, + "nationalValueGeography": "United Kingdom", + "unit": "count", + "source": "Independent Schools Council", + "sourceTables": [ + "ISC annual Census 2025" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.ss_nics_relief_employee|", + "scope": "national", + "label": "Salary-sacrifice pension contributions: employee National Insurance relief", + "breakdown": null, + "geoLevels": [ + "uk" + ], + "nationalCount": 1, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 1000000000.0, + "nationalValueGeography": "United Kingdom", + "unit": "gbp", + "source": "HMRC", + "sourceTables": [ + "Private pension statistics Tables 6.1 and 6.2: salary sacrifice pension contributions, income tax and NICs relief, tax year 2024 to 2025" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.ss_nics_relief_employer|", + "scope": "national", + "label": "Salary-sacrifice pension contributions: employer National Insurance relief", + "breakdown": null, + "geoLevels": [ + "uk" + ], + "nationalCount": 1, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 3695652173.9130435, + "nationalValueGeography": "United Kingdom", + "unit": "gbp", + "source": "HMRC", + "sourceTables": [ + "Private pension statistics Tables 6.1 and 6.2: salary sacrifice pension contributions, income tax and NICs relief, tax year 2024 to 2025" + ], + "period": 2025, + "notes": [ + "Uprated to 2025 with the employer National Insurance rate (policyengine-uk)" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.ss_income_tax_relief_additional_rate|", + "scope": "national", + "label": "Salary-sacrifice pension contributions: Income Tax relief at the additional rate", + "breakdown": null, + "geoLevels": [ + "uk" + ], + "nationalCount": 1, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 1800000000.0, + "nationalValueGeography": "United Kingdom", + "unit": "gbp", + "source": "HMRC", + "sourceTables": [ + "Private pension statistics Tables 6.1 and 6.2: salary sacrifice pension contributions, income tax and NICs relief, tax year 2024 to 2025" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.ss_income_tax_relief_basic_rate|", + "scope": "national", + "label": "Salary-sacrifice pension contributions: Income Tax relief at the basic rate", + "breakdown": null, + "geoLevels": [ + "uk" + ], + "nationalCount": 1, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 1600000000.0, + "nationalValueGeography": "United Kingdom", + "unit": "gbp", + "source": "HMRC", + "sourceTables": [ + "Private pension statistics Tables 6.1 and 6.2: salary sacrifice pension contributions, income tax and NICs relief, tax year 2024 to 2025" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.ss_income_tax_relief_higher_rate|", + "scope": "national", + "label": "Salary-sacrifice pension contributions: Income Tax relief at the higher rate", + "breakdown": null, + "geoLevels": [ + "uk" + ], + "nationalCount": 1, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 5500000000.0, + "nationalValueGeography": "United Kingdom", + "unit": "gbp", + "source": "HMRC", + "sourceTables": [ + "Private pension statistics Tables 6.1 and 6.2: salary sacrifice pension contributions, income tax and NICs relief, tax year 2024 to 2025" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.salary_sacrifice_pension_users|annual_salary_sacrificed,salary_sacrifice_threshold_position", + "scope": "national", + "label": "Salary-sacrifice pension users", + "breakdown": "Amount sacrificed", + "geoLevels": [ + "uk" + ], + "nationalCount": 2, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 2, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "HMRC", + "sourceTables": [ + "Salary sacrifice reform for pension contributions (effective 6 April 2029), policy paper" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|scotgov.social_security_assistance_spending|social_security_line", + "scope": "national", + "label": "Scottish Child Payment spending", + "breakdown": null, + "geoLevels": [ + "nation" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 1, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "Scotland" + ], + "targetCount": 1, + "nationalValue": 471000000.0, + "nationalValueGeography": "Scotland", + "unit": "gbp", + "source": "Scottish Government", + "sourceTables": [ + "Scottish Budget 2026-27, Chapter 5 Social Justice, Table 5.08 Social Security Assistance" + ], + "period": 2025, + "notes": [ + "The publisher’s forecast or projection" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|obr.ni_self_employed|", + "scope": "national", + "label": "Self-employed National Insurance contributions", + "breakdown": null, + "geoLevels": [ + "uk" + ], + "nationalCount": 1, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 2876506585.4950743, + "nationalValueGeography": "United Kingdom", + "unit": "gbp", + "source": "OBR", + "sourceTables": [ + "EFO March 2026 detailed forecast tables: receipts" + ], + "period": 2025, + "notes": [ + "Calendar-year window of fiscal-year figures", + "The publisher’s forecast or projection" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|obr.sdlt|", + "scope": "national", + "label": "Stamp Duty Land Tax receipts", + "breakdown": null, + "geoLevels": [ + "uk" + ], + "nationalCount": 1, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 10161716337.860817, + "nationalValueGeography": "United Kingdom", + "unit": "gbp", + "source": "OBR", + "sourceTables": [ + "EFO March 2026 detailed forecast tables: receipts" + ], + "period": 2025, + "notes": [ + "Calendar-year window of fiscal-year figures, scaled by a published ratio", + "The publisher’s forecast or projection" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|dwp.state_pension_mean_weekly_amount|", + "scope": "national", + "label": "State Pension paid", + "breakdown": null, + "geoLevels": [ + "nation" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 1, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "Northern Ireland" + ], + "targetCount": 1, + "nationalValue": 3710881810.1898503, + "nationalValueGeography": "Northern Ireland", + "unit": "gbp", + "source": "Department for Communities (NI)", + "sourceTables": [ + "Benefits Statistics Summary Publication May 2026: State Pension workbook" + ], + "period": 2025, + "notes": [ + "Annual: 52 times the published weekly amount", + "Published caseload times published mean award", + "Uprated with the new State Pension rate (policyengine-uk)" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|dwp.state_pension_mean_weekly_amount|age_bands_and_single_year,category_of_pension,gender", + "scope": "national", + "label": "State Pension paid", + "breakdown": null, + "geoLevels": [ + "gb" + ], + "nationalCount": 0, + "gbCount": 1, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 138623908111.01526, + "nationalValueGeography": "Great Britain", + "unit": "gbp", + "source": "DWP", + "sourceTables": [ + "State Pension caseload and mean weekly amount by age, gender and type of pension, Great Britain residents, February 2023 to March 2026" + ], + "period": 2025, + "notes": [ + "Annual: 52 times the published weekly amount", + "Published caseload times published mean award", + "Uprated with the new State Pension rate (policyengine-uk)" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|dwp.state_pension_recipients|", + "scope": "national", + "label": "State Pension recipients", + "breakdown": null, + "geoLevels": [ + "nation" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 1, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "Northern Ireland" + ], + "targetCount": 1, + "nationalValue": 330147.5, + "nationalValueGeography": "Northern Ireland", + "unit": "count", + "source": "Department for Communities (NI)", + "sourceTables": [ + "Benefits Statistics Summary Publication May 2026: State Pension workbook" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|dwp.state_pension_recipients|age_bands_and_single_year,category_of_pension,gender", + "scope": "national", + "label": "State Pension recipients", + "breakdown": null, + "geoLevels": [ + "gb" + ], + "nationalCount": 0, + "gbCount": 1, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 12052874.0, + "nationalValueGeography": "Great Britain", + "unit": "count", + "source": "DWP", + "sourceTables": [ + "State Pension caseload and mean weekly amount by age, gender and type of pension, Great Britain residents, February 2023 to March 2026" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|dwp.state_pension_recipients|age,age_bands_and_single_year,category_of_pension,gender", + "scope": "national", + "label": "State Pension recipients", + "breakdown": "Age × State Pension type × sex", + "geoLevels": [ + "gb" + ], + "nationalCount": 0, + "gbCount": 14, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 14, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "DWP", + "sourceTables": [ + "State Pension caseload and mean weekly amount by age, gender and type of pension, Great Britain residents, February 2023 to March 2026" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|dwp.state_pension_recipients|category_of_pension,gender", + "scope": "national", + "label": "State Pension recipients", + "breakdown": "State Pension type", + "geoLevels": [ + "nation", + "region" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 4, + "regionCount": 18, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "Scotland", + "Wales" + ], + "targetCount": 22, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "DWP", + "sourceTables": [ + "State Pension caseload and mean weekly amount by region, type of pension and gender, February 2023 to March 2026" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|dwp.state_pension_recipients|category_of_pension,grouped_amount_of_benefit", + "scope": "national", + "label": "State Pension recipients", + "breakdown": "State Pension type × weekly amount", + "geoLevels": [ + "gb" + ], + "nationalCount": 0, + "gbCount": 19, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 19, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "DWP", + "sourceTables": [ + "State Pension caseload by grouped weekly amount of benefit and type of pension, Great Britain residents, February 2023 to March 2026" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|obr.statutory_maternity_pay|", + "scope": "national", + "label": "Statutory Maternity Pay spending", + "breakdown": null, + "geoLevels": [ + "gb" + ], + "nationalCount": 0, + "gbCount": 1, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 3179100661.1404104, + "nationalValueGeography": "Great Britain", + "unit": "gbp", + "source": "OBR", + "sourceTables": [ + "EFO March 2026 detailed forecast tables: expenditure" + ], + "period": 2025, + "notes": [ + "Calendar-year window of fiscal-year figures", + "The publisher’s forecast or projection" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|slc.student_loan_net_repayments_total_higher_education|", + "scope": "national", + "label": "Student loan net repayments: all higher education", + "breakdown": null, + "geoLevels": [ + "nation" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 1, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "England" + ], + "targetCount": 1, + "nationalValue": 5339303976.37, + "nationalValueGeography": "England", + "unit": "gbp", + "source": "Student Loans Company", + "sourceTables": [ + "Student loans in England, financial year 2025-26 (corrected 2 July 2026), Table 1A" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|slc.student_loan_net_repayments_plan_1|", + "scope": "national", + "label": "Student loan net repayments: Plan 1", + "breakdown": null, + "geoLevels": [ + "nation" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 1, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "England" + ], + "targetCount": 1, + "nationalValue": 1604190700.89, + "nationalValueGeography": "England", + "unit": "gbp", + "source": "Student Loans Company", + "sourceTables": [ + "Student loans in England, financial year 2025-26 (corrected 2 July 2026), Table 1A" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|slc.student_loan_net_repayments_plan_2_full_time|", + "scope": "national", + "label": "Student loan net repayments: Plan 2, full-time", + "breakdown": null, + "geoLevels": [ + "nation" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 1, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "England" + ], + "targetCount": 1, + "nationalValue": 3234379913.2999997, + "nationalValueGeography": "England", + "unit": "gbp", + "source": "Student Loans Company", + "sourceTables": [ + "Student loans in England, financial year 2025-26 (corrected 2 July 2026), Table 1A" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|slc.maintenance_loan_recipients|", + "scope": "national", + "label": "Student maintenance loan recipients", + "breakdown": null, + "geoLevels": [ + "nation" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 1, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "England" + ], + "targetCount": 1, + "nationalValue": 1159761.0, + "nationalValueGeography": "England", + "unit": "count", + "source": "Student Loans Company", + "sourceTables": [ + "Student support for higher education in England 2025, Tables 3A and 4C" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|slc.maintenance_loan_amount_paid|", + "scope": "national", + "label": "Student maintenance loans paid", + "breakdown": null, + "geoLevels": [ + "nation" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 1, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "England" + ], + "targetCount": 1, + "nationalValue": 8591659718.080004, + "nationalValueGeography": "England", + "unit": "gbp", + "source": "Student Loans Company", + "sourceTables": [ + "Student support for higher education in England 2025, Tables 3A and 4C" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|slc.targeted_support_amount_awarded|student_support_product", + "scope": "national", + "label": "Targeted student support awarded", + "breakdown": "Product", + "geoLevels": [ + "nation" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 2, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "England" + ], + "targetCount": 2, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "gbp", + "source": "Student Loans Company", + "sourceTables": [ + "Student support for higher education in England 2025, Tables 3A and 4C" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|slc.targeted_support_recipients|student_support_product", + "scope": "national", + "label": "Targeted student support recipients", + "breakdown": "Product", + "geoLevels": [ + "nation" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 2, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "England" + ], + "targetCount": 2, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "Student Loans Company", + "sourceTables": [ + "Student support for higher education in England 2025, Tables 3A and 4C" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.tfc_children_with_used_accounts_annual_unique|account_status,count_basis,definition_regime,source_release", + "scope": "national", + "label": "Tax-Free Childcare: children with a used account", + "breakdown": null, + "geoLevels": [ + "uk" + ], + "nationalCount": 1, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 1151515.0, + "nationalValueGeography": "United Kingdom", + "unit": "count", + "source": "HMRC", + "sourceTables": [ + "Tax-Free Childcare Statistics March 2026, Table 2: Numbers of Children with Open and Used Tax-Free Childcare Accounts and Government Top-up" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|hmrc.tfc_government_top_up|flow_basis,flow_type,source_release", + "scope": "national", + "label": "Tax-Free Childcare: government top-up", + "breakdown": null, + "geoLevels": [ + "uk" + ], + "nationalCount": 1, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 599800000.0, + "nationalValueGeography": "United Kingdom", + "unit": "gbp", + "source": "HMRC", + "sourceTables": [ + "Tax-Free Childcare Statistics March 2026, Table 2: Numbers of Children with Open and Used Tax-Free Childcare Accounts and Government Top-up" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|obr.tv_licence_fee|", + "scope": "national", + "label": "TV licence fee receipts", + "breakdown": null, + "geoLevels": [ + "uk" + ], + "nationalCount": 1, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 3858750000.0, + "nationalValueGeography": "United Kingdom", + "unit": "gbp", + "source": "OBR", + "sourceTables": [ + "EFO March 2026 detailed forecast tables: expenditure" + ], + "period": 2025, + "notes": [ + "Calendar-year window of fiscal-year figures", + "The publisher’s forecast or projection" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|dfi_ni.translink_bus.passenger_receipts|service", + "scope": "national", + "label": "Ulsterbus passenger receipts", + "breakdown": null, + "geoLevels": [ + "nation" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 1, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "Northern Ireland" + ], + "targetCount": 1, + "nationalValue": 152500000.0, + "nationalValueGeography": "Northern Ireland", + "unit": "gbp", + "source": "Department for Infrastructure (NI)", + "sourceTables": [ + "Public Transport Statistics Northern Ireland 2025-26, Figures 3 and 5 journeys and receipts" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|dwp.uc_benefit_units|family_type,monthly_award_amount_bands", + "scope": "national", + "label": "Universal Credit households", + "breakdown": "Family type × monthly award", + "geoLevels": [ + "gb" + ], + "nationalCount": 0, + "gbCount": 89, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 89, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "DWP", + "sourceTables": [ + "Households on Universal Credit by monthly award amount band and family type, April to December 2025" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|dwp.uc_benefit_units|child_entitlement,number_of_children,payment_indicator", + "scope": "national", + "label": "Universal Credit households", + "breakdown": "Number of children", + "geoLevels": [ + "gb" + ], + "nationalCount": 0, + "gbCount": 4, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 4, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "DWP", + "sourceTables": [ + "Households on Universal Credit by number of children, payment indicator and child entitlement, April 2023 to May 2026" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|dwp.uc_benefit_units_with_childcare_element|uc_element", + "scope": "national", + "label": "Universal Credit households receiving the childcare element", + "breakdown": null, + "geoLevels": [ + "gb" + ], + "nationalCount": 0, + "gbCount": 1, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 178666.66666666666, + "nationalValueGeography": "Great Britain", + "unit": "count", + "source": "DWP", + "sourceTables": [ + "Universal Credit childcare element statistics to May 2026, Tables 1 and 3" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|dwp.uc_benefit_units|age_of_youngest_child_bands_and_single_year", + "scope": "national", + "label": "Universal Credit households with a child under 1", + "breakdown": null, + "geoLevels": [ + "nation" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 1, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [ + "Scotland" + ], + "targetCount": 1, + "nationalValue": 14333.222222222223, + "nationalValueGeography": "Scotland", + "unit": "count", + "source": "DWP", + "sourceTables": [ + "Households on Universal Credit in Scotland by single year of age of youngest child, April to December 2025" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|dwp.uc_benefit_units_with_deduction|", + "scope": "national", + "label": "Universal Credit households with a deduction", + "breakdown": null, + "geoLevels": [ + "gb" + ], + "nationalCount": 0, + "gbCount": 1, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 3130000.0, + "nationalValueGeography": "Great Britain", + "unit": "count", + "source": "DWP", + "sourceTables": [ + "Universal Credit deductions statistics March 2025 to February 2026, Table 1" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|dwp.uc_benefit_units|carer_entitlement,payment_indicator", + "scope": "national", + "label": "Universal Credit households with carer entitlement", + "breakdown": null, + "geoLevels": [ + "gb" + ], + "nationalCount": 0, + "gbCount": 1, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 1078514.5, + "nationalValueGeography": "Great Britain", + "unit": "count", + "source": "DWP", + "sourceTables": [ + "Households on Universal Credit by carer entitlement and payment indicator, January 2023 to May 2026" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|dwp.uc_benefit_units|child_entitlement,family_type,payment_indicator", + "scope": "national", + "label": "Universal Credit households with child entitlement", + "breakdown": "Family type", + "geoLevels": [ + "gb" + ], + "nationalCount": 0, + "gbCount": 5, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 5, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "DWP", + "sourceTables": [ + "Households on Universal Credit by family type, payment indicator and child entitlement, April 2023 to May 2026" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|dwp.uc_benefit_units|housing_entitlement_tenure,payment_indicator", + "scope": "national", + "label": "Universal Credit households with housing entitlement", + "breakdown": "Tenure", + "geoLevels": [ + "gb" + ], + "nationalCount": 0, + "gbCount": 2, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 2, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "DWP", + "sourceTables": [ + "Households on Universal Credit by housing entitlement tenure and payment indicator, January 2023 to May 2026" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|dwp.uc_benefit_units|limited_capability_for_work_entitlement,payment_indicator", + "scope": "national", + "label": "Universal Credit households with LCWRA entitlement", + "breakdown": null, + "geoLevels": [ + "gb" + ], + "nationalCount": 0, + "gbCount": 1, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 2272425.1666666665, + "nationalValueGeography": "Great Britain", + "unit": "count", + "source": "DWP", + "sourceTables": [ + "Households on Universal Credit by limited capability for work entitlement group and payment indicator, January 2023 to May 2026" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": false + }, + { + "key": "ledger|obr.universal_credit_outside_cap|", + "scope": "national", + "label": "Universal Credit spending outside the welfare cap", + "breakdown": null, + "geoLevels": [ + "gb" + ], + "nationalCount": 0, + "gbCount": 1, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 76054754387.77309, + "nationalValueGeography": "Great Britain", + "unit": "gbp", + "source": "OBR", + "sourceTables": [ + "EFO March 2026 detailed forecast tables: expenditure" + ], + "period": 2025, + "notes": [ + "Calendar-year window of fiscal-year figures", + "The publisher’s forecast or projection" + ], + "localAreaFileOnly": false + }, + { + "key": "ledger|obr.winter_fuel_payment|", + "scope": "national", + "label": "Winter Fuel Payment spending", + "breakdown": null, + "geoLevels": [ + "englandAndWales" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 1, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 1, + "nationalValue": 1508660934.2839375, + "nationalValueGeography": "England and Wales", + "unit": "gbp", + "source": "OBR", + "sourceTables": [ + "EFO March 2026 detailed forecast tables: expenditure" + ], + "period": 2025, + "notes": [ + "Calendar-year window of fiscal-year figures", + "The publisher’s forecast or projection" + ], + "localAreaFileOnly": false + }, + { + "key": "local|council_tax|council_tax_band", + "scope": "local", + "label": "Council Tax dwelling stock", + "breakdown": "Council tax band", + "geoLevels": [ + "localAuthority" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 2495, + "otherCount": 0, + "nations": [], + "targetCount": 2495, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "MHCLG, Scottish Government, Welsh Government", + "sourceTables": [ + "Council Taxbase: Local authority level data for 2025 (revised), Council Taxbase Data worksheet", + "Number of chargeable dwellings, September 2025 (CTAXBASE 2025)", + "StatsWales: Council tax dwellings by authority and band (CT1 returns), full-dataset CSV export" + ], + "period": 2025, + "notes": [ + "Sum of published taxbase lines", + "Rescaled to sum to its published higher-level control" + ], + "localAreaFileOnly": true + }, + { + "key": "local|energy/electricity_kwh|", + "scope": "local", + "label": "Domestic electricity consumption", + "breakdown": null, + "geoLevels": [ + "localAuthority" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 348, + "otherCount": 0, + "nations": [], + "targetCount": 348, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "kwh", + "source": "DESNZ", + "sourceTables": [ + "Subnational electricity consumption, Great Britain, 2005 to 2024" + ], + "period": 2025, + "notes": [ + "Uprated to 2025 with DESNZ Energy Trends (Great Britain domestic level)" + ], + "localAreaFileOnly": true + }, + { + "key": "local|energy/gas_kwh|", + "scope": "local", + "label": "Domestic gas consumption", + "breakdown": null, + "geoLevels": [ + "localAuthority" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 346, + "otherCount": 0, + "nations": [], + "targetCount": 346, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "kwh", + "source": "DESNZ", + "sourceTables": [ + "Subnational gas consumption, Great Britain, 2005 to 2024" + ], + "period": 2025, + "notes": [ + "Uprated to 2025 with DESNZ Energy Trends (Great Britain domestic level)" + ], + "localAreaFileOnly": true + }, + { + "key": "local|energy/gas_connected_households|", + "scope": "local", + "label": "Gas-connected households", + "breakdown": null, + "geoLevels": [ + "localAuthority" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 346, + "otherCount": 0, + "nations": [], + "targetCount": 346, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "ONS, National Records of Scotland", + "sourceTables": [ + "Census 2021 TS041 number of households, local authority districts (April 2023), England and Wales", + "Scotland's Census 2022 UV404 tenure of household, council areas" + ], + "period": 2025, + "notes": [ + "Census households times the DESNZ share of households with a gas meter" + ], + "localAreaFileOnly": true + }, + { + "key": "local|hmrc/higher_rate_taxpayers/count|", + "scope": "local", + "label": "Higher-rate Income Tax payers", + "breakdown": null, + "geoLevels": [ + "constituency", + "localAuthority" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 438, + "localAuthorityCount": 289, + "otherCount": 0, + "nations": [], + "targetCount": 727, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "HMRC", + "sourceTables": [ + "Personal Incomes Statistics tables 3.12 to 3.15a, tax year 2023 to 2024" + ], + "period": 2025, + "notes": [ + "Uprated with HMRC Income Tax liabilities statistics", + "Rescaled to sum to its published higher-level control" + ], + "localAreaFileOnly": true + }, + { + "key": "local|households|", + "scope": "local", + "label": "Households", + "breakdown": null, + "geoLevels": [ + "constituency", + "localAuthority" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 650, + "localAuthorityCount": 361, + "otherCount": 0, + "nations": [], + "targetCount": 1011, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "ONS, National Records of Scotland, NISRA", + "sourceTables": [ + "Census 2021 TS041 number of households, Westminster Parliamentary Constituencies (July 2024), England and Wales", + "Census 2021 TS041 number of households, local authority districts (April 2023), England and Wales", + "NISRA Census 2021 flexible table: number of households by LGD14", + "NISRA Census 2021 flexible table: number of households by PARLCON24", + "Scotland Census 2022 UV404 tenure of household, UK Parliamentary Constituencies 2024", + "Scotland's Census 2022 UV404 tenure of household, council areas" + ], + "period": 2025, + "notes": [ + "Census 2021 counts (Scotland: Census 2022)", + "Rescaled to sum to its published higher-level control" + ], + "localAreaFileOnly": true + }, + { + "key": "local|tenure|tenure", + "scope": "local", + "label": "Households", + "breakdown": "Tenure", + "geoLevels": [ + "localAuthority" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 1436, + "otherCount": 0, + "nations": [], + "targetCount": 1436, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "ONS, National Records of Scotland, NISRA", + "sourceTables": [ + "Census 2021 TS054 tenure of household, local authority districts (April 2023), England and Wales", + "NISRA Census 2021 flexible table: households by tenure and local government district", + "Scotland's Census 2022 UV404 tenure of household, council areas" + ], + "period": 2025, + "notes": [ + "Census tenure counts moved to 2025 with ONS tenure shares", + "Rescaled to sum to its published higher-level control" + ], + "localAreaFileOnly": true + }, + { + "key": "local|hmrc/employment_income/amount|", + "scope": "local", + "label": "Income: employment", + "breakdown": null, + "geoLevels": [ + "constituency", + "localAuthority" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 650, + "localAuthorityCount": 358, + "otherCount": 0, + "nations": [], + "targetCount": 1008, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "gbp", + "source": "HMRC", + "sourceTables": [ + "Personal Incomes Statistics tables 3.12 to 3.15a, tax year 2023 to 2024" + ], + "period": 2025, + "notes": [ + "Published count times published mean, uprated with HMRC Income Tax liabilities statistics", + "Rescaled to sum to its published higher-level control" + ], + "localAreaFileOnly": true + }, + { + "key": "local|hmrc/employment_income/count|", + "scope": "local", + "label": "Income: people with employment income", + "breakdown": null, + "geoLevels": [ + "constituency", + "localAuthority" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 650, + "localAuthorityCount": 358, + "otherCount": 0, + "nations": [], + "targetCount": 1008, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "HMRC", + "sourceTables": [ + "Personal Incomes Statistics tables 3.12 to 3.15a, tax year 2023 to 2024" + ], + "period": 2025, + "notes": [ + "Uprated with HMRC Income Tax liabilities statistics", + "Rescaled to sum to its published higher-level control" + ], + "localAreaFileOnly": true + }, + { + "key": "local|hmrc/self_employment_income/count|", + "scope": "local", + "label": "Income: people with self-employment income", + "breakdown": null, + "geoLevels": [ + "constituency", + "localAuthority" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 650, + "localAuthorityCount": 358, + "otherCount": 0, + "nations": [], + "targetCount": 1008, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "HMRC", + "sourceTables": [ + "Personal Incomes Statistics tables 3.12 to 3.15a, tax year 2023 to 2024" + ], + "period": 2025, + "notes": [ + "Uprated with HMRC Income Tax liabilities statistics", + "Rescaled to sum to its published higher-level control" + ], + "localAreaFileOnly": true + }, + { + "key": "local|hmrc/self_employment_income/amount|", + "scope": "local", + "label": "Income: self-employment", + "breakdown": null, + "geoLevels": [ + "constituency", + "localAuthority" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 649, + "localAuthorityCount": 358, + "otherCount": 0, + "nations": [], + "targetCount": 1007, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "gbp", + "source": "HMRC", + "sourceTables": [ + "Personal Incomes Statistics tables 3.12 to 3.15a, tax year 2023 to 2024" + ], + "period": 2025, + "notes": [ + "Published count times published mean, uprated with HMRC Income Tax liabilities statistics", + "Rescaled to sum to its published higher-level control" + ], + "localAreaFileOnly": true + }, + { + "key": "local|high_value_homes|value_threshold", + "scope": "local", + "label": "Owner-occupied homes above high-value thresholds", + "breakdown": "Value threshold", + "geoLevels": [ + "localAuthority" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 350, + "otherCount": 0, + "nations": [], + "targetCount": 350, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "Microcosm derivation from open data (HM Land Registry Price Paid, UK HPI, ONS NSPL, VOA council tax stock, Census 2021 tenure)", + "sourceTables": [ + "HM Land Registry Price Paid Data, UK HPI and ONS NSPL, Microcosm derivation" + ], + "period": 2025, + "notes": [], + "localAreaFileOnly": true + }, + { + "key": "local|age|age_band", + "scope": "local", + "label": "Population", + "breakdown": "Age", + "geoLevels": [ + "constituency", + "localAuthority" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 5200, + "localAuthorityCount": 2872, + "otherCount": 0, + "nations": [], + "targetCount": 8072, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "ONS, National Records of Scotland, NISRA", + "sourceTables": [ + "MYE01T013 Mid-year population estimates by Assembly Area (2024), five year age bands and sex", + "NRS revised mid-2011 to mid-2024 single-year age/sex population, UKPC24, 2022 data-zone method", + "Population estimates - local authority based by five year age band, United Kingdom, mid-2025", + "Population estimates - small area (2021 based) by single year of age, England and Wales - Westminster Parliamentary Constituencies (July 2024), mid-2024" + ], + "period": 2025, + "notes": [ + "Published small-area and local-authority estimates summed to ten-year bands", + "Rescaled to sum to its published higher-level control" + ], + "localAreaFileOnly": true + }, + { + "key": "local|rent/private_rent_brma|", + "scope": "local", + "label": "Private rents paid", + "breakdown": null, + "geoLevels": [ + "other" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 0, + "otherCount": 18, + "nations": [], + "targetCount": 18, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "gbp", + "source": "ONS", + "sourceTables": [ + "Price Index of Private Rents, UK: monthly price statistics, July 2026 edition (data to June 2026)" + ], + "period": 2025, + "notes": [ + "12 times the ONS Price Index of Private Rents mean monthly rent for 2025, times private-renter households" + ], + "localAreaFileOnly": true + }, + { + "key": "local|rent/private_rent|", + "scope": "local", + "label": "Private rents paid", + "breakdown": null, + "geoLevels": [ + "localAuthority" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 0, + "localAuthorityCount": 316, + "otherCount": 0, + "nations": [], + "targetCount": 316, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "gbp", + "source": "ONS", + "sourceTables": [ + "Price Index of Private Rents, UK: monthly price statistics, July 2026 edition (data to June 2026)" + ], + "period": 2025, + "notes": [ + "12 times the ONS Price Index of Private Rents mean monthly rent for 2025, times private-renter households" + ], + "localAreaFileOnly": true + }, + { + "key": "local|uc_households|", + "scope": "local", + "label": "Universal Credit households", + "breakdown": null, + "geoLevels": [ + "constituency", + "localAuthority" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 632, + "localAuthorityCount": 348, + "otherCount": 0, + "nations": [], + "targetCount": 980, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "DWP", + "sourceTables": [ + "DWP Stat-Xplore UC households by constituency and Payment Indicator, January 2025 to May 2026", + "DWP Stat-Xplore UC households by local authority and Payment Indicator, January 2025 to May 2026" + ], + "period": 2025, + "notes": [ + "Average of the published 2025 months, paid claims only", + "Rescaled to sum to its published higher-level control" + ], + "localAreaFileOnly": true + }, + { + "key": "local|uc_households_by_children|number_of_children", + "scope": "local", + "label": "Universal Credit households", + "breakdown": "Number of children", + "geoLevels": [ + "constituency" + ], + "nationalCount": 0, + "gbCount": 0, + "englandAndWalesCount": 0, + "nationCount": 0, + "regionCount": 0, + "constituencyCount": 2528, + "localAuthorityCount": 0, + "otherCount": 0, + "nations": [], + "targetCount": 2528, + "nationalValue": null, + "nationalValueGeography": null, + "unit": "count", + "source": "DWP", + "sourceTables": [ + "DWP Stat-Xplore UC households by constituency, number of children and Payment Indicator, January 2025 to May 2026" + ], + "period": 2025, + "notes": [ + "Average of the published 2025 months, paid claims only", + "Rescaled to sum to its published higher-level control" + ], + "localAreaFileOnly": true + } + ] +} From 2cfe93400965ff335cabacff1c1f419256e6b8bd Mon Sep 17 00:00:00 2001 From: Maria Juaristi Date: Tue, 27 Oct 2026 11:56:35 +0000 Subject: [PATCH 02/13] List the UK calibration targets on the calibration page The UK page said calibration targets were available for the US only. It now renders src/data/ukCalibrationTargets.json with the US page's table, search and level badges, using UK levels: UK, GB, England and Wales, nation, region, constituency, local authority and Scottish BRMA. It shows target values only. CalibrationPage.tsx changes only its non-US branch, so the US page is unchanged. Co-Authored-By: Claude Opus 5.5 --- src/views/data/CalibrationPage.tsx | 11 +- src/views/data/UkCalibrationPage.tsx | 397 +++++++++++++++++++++++++++ 2 files changed, 399 insertions(+), 9 deletions(-) create mode 100644 src/views/data/UkCalibrationPage.tsx diff --git a/src/views/data/CalibrationPage.tsx b/src/views/data/CalibrationPage.tsx index 50899d5..c9d4a7a 100644 --- a/src/views/data/CalibrationPage.tsx +++ b/src/views/data/CalibrationPage.tsx @@ -4,6 +4,7 @@ import usCalibrationTargets from '../../data/calibrationTargets.json'; import PageHeader from '../../components/layout/PageHeader'; import SearchInput from '../../components/layout/SearchInput'; import type { Country } from '../../hooks/useCountry'; +import UkCalibrationPage from './UkCalibrationPage'; interface CalibrationRow { variable: string; @@ -71,15 +72,7 @@ export default function CalibrationPage({ country }: { country: Country }) { ); if (country !== 'us') { - return ( -
- -
- ); + return ; } return ( diff --git a/src/views/data/UkCalibrationPage.tsx b/src/views/data/UkCalibrationPage.tsx new file mode 100644 index 0000000..506edf5 --- /dev/null +++ b/src/views/data/UkCalibrationPage.tsx @@ -0,0 +1,397 @@ +import { useMemo, useState } from 'react'; +import { colors, typography, spacing } from '../../designTokens'; +import ukCalibrationTargets from '../../data/ukCalibrationTargets.json'; +import PageHeader from '../../components/layout/PageHeader'; +import SearchInput from '../../components/layout/SearchInput'; + +export type UkLevel = + | 'uk' + | 'gb' + | 'englandAndWales' + | 'nation' + | 'region' + | 'constituency' + | 'localAuthority' + | 'other'; + +export interface UkCalibrationRow { + key: string; + scope: 'national' | 'local'; + label: string; + breakdown: string | null; + geoLevels: UkLevel[]; + nationalCount: number; + gbCount: number; + englandAndWalesCount: number; + nationCount: number; + regionCount: number; + constituencyCount: number; + localAuthorityCount: number; + otherCount: number; + nations: string[]; + targetCount: number; + nationalValue: number | null; + nationalValueGeography: string | null; + unit: 'gbp' | 'count' | 'kwh'; + source: string; + sourceTables: string[]; + period: number | null; + notes: string[]; + localAreaFileOnly: boolean; +} + +interface UkRelease { + buildId: string; + dataset: string; + households: number; + targets: number; +} + +export interface UkCalibrationData { + policyengineVersion: string; + modelVersion: string; + calibrationYear: number; + releases: { national: UkRelease; localArea: UkRelease }; + totalTargets: { national: number; localArea: number; union: number }; + levelCounts: Record<'national' | 'localArea' | 'union', Record>; + rows: UkCalibrationRow[]; +} + +export const ukCalibration = ukCalibrationTargets as UkCalibrationData; + +const LEVELS: UkLevel[] = [ + 'uk', + 'gb', + 'englandAndWales', + 'nation', + 'region', + 'constituency', + 'localAuthority', + 'other', +]; + +const COUNT_FIELDS: Record = { + uk: 'nationalCount', + gb: 'gbCount', + englandAndWales: 'englandAndWalesCount', + nation: 'nationCount', + region: 'regionCount', + constituency: 'constituencyCount', + localAuthority: 'localAuthorityCount', + other: 'otherCount', +}; + +export const levelLabels: Record = { + uk: 'UK', + gb: 'GB', + englandAndWales: 'England and Wales', + nation: 'Nation', + region: 'Region', + constituency: 'Constituency', + localAuthority: 'Local authority', + other: 'Scottish BRMA', +}; + +// The US page's national / state / district colours, by analogous level. +const levelColors: Record = { + uk: { bg: '#dbeafe', text: '#1e40af' }, + gb: { bg: '#dbeafe', text: '#1e40af' }, + englandAndWales: { bg: '#dbeafe', text: '#1e40af' }, + nation: { bg: '#dcfce7', text: '#166534' }, + region: { bg: '#dcfce7', text: '#166534' }, + constituency: { bg: '#fef3c7', text: '#92400e' }, + localAuthority: { bg: '#fef3c7', text: '#92400e' }, + other: { bg: '#fef3c7', text: '#92400e' }, +}; + +export function levelCount(row: UkCalibrationRow, level: UkLevel): number { + return row[COUNT_FIELDS[level]] as number; +} + +export function levelBadge(row: UkCalibrationRow, level: UkLevel): string { + const count = levelCount(row, level); + // A row whose nation targets are all one nation's names that nation. + const name = level === 'nation' && row.nations.length === 1 ? row.nations[0] : levelLabels[level]; + return `${name}${count > 1 ? ` (${count.toLocaleString('en-GB')})` : ''}`; +} + +export function formatUkValue(value: number | null, unit: UkCalibrationRow['unit']): string { + if (value === null || value === undefined) return '—'; + const abs = Math.abs(value); + if (unit === 'gbp') { + if (abs >= 1e12) return `£${(value / 1e12).toFixed(2)}tn`; + if (abs >= 1e9) return `£${(value / 1e9).toFixed(1)}bn`; + if (abs >= 1e6) return `£${(value / 1e6).toFixed(1)}m`; + return `£${Math.round(value).toLocaleString('en-GB')}`; + } + if (unit === 'kwh') { + if (abs >= 1e9) return `${(value / 1e9).toFixed(1)} TWh`; + if (abs >= 1e6) return `${(value / 1e6).toFixed(1)} GWh`; + return `${Math.round(value).toLocaleString('en-GB')} kWh`; + } + return Math.round(value).toLocaleString('en-GB'); +} + +const n = (x: number) => x.toLocaleString('en-GB'); + +/** The header sentence, from the extracted release counts. */ +export function describeUkTargets(data: UkCalibrationData): string { + const { national, localArea, union } = data.totalTargets; + const dense = data.levelCounts.localArea; + const localCells = dense.constituency + dense.localAuthority + dense.other; + const denseNational = localArea - localCells; + const lead = + `Microcosm UK, the dataset PolicyEngine’s UK model runs on, is reweighted so its weighted totals match ` + + `published statistics for calendar ${data.calibrationYear}. The national file is calibrated to ` + + `${n(national)} targets for the UK, Great Britain, England and Wales, the four nations and the English regions.`; + if (union !== localArea) { + return `${lead} The local-area file is calibrated to ${n(localArea)} targets, ${n(localCells)} of them for constituencies, local authorities and Scotland’s Broad Rental Market Areas.`; + } + const extra = denseNational - national; + return ( + `${lead} The local-area file is calibrated to all ${n(localArea)} targets: the national ones` + + `${extra > 0 ? `, ${n(extra)} more above constituency level,` : ''} and ` + + `${n(localCells)} cells for constituencies, local authorities and Scotland’s Broad Rental Market Areas.` + ); +} + +const prose = { + fontSize: typography.fontSize.base, + color: colors.text.secondary, + lineHeight: 1.7, + marginBottom: spacing.xl, + maxWidth: '720px', +}; + +const cell = { + padding: `${spacing.sm} ${spacing.lg}`, + verticalAlign: 'top' as const, +}; + +export default function UkCalibrationPage() { + const [search, setSearch] = useState(''); + const data = ukCalibration; + const allRows = data.rows; + + const filtered = useMemo(() => { + if (!search) return allRows; + const q = search.toLowerCase(); + return allRows.filter((r) => + [ + r.label, + r.breakdown ?? '', + r.source, + ...r.sourceTables, + ...r.notes, + ...r.nations, + ...r.geoLevels.map((l) => levelLabels[l]), + ].some((s) => s.toLowerCase().includes(q)), + ); + }, [allRows, search]); + + const { national, localArea } = data.releases; + + return ( +
+ + +
+ The targets of the two Microcosm UK releases that version {data.policyengineVersion} of the policyengine + Python package serves with policyengine-uk {data.modelVersion}: the national file{' '} + {national.buildId} ({n(national.households)}{' '} + households) and the local-area file{' '} + {localArea.buildId} ({n(localArea.households)}{' '} + households). +
+ +

+ Every value below is a published official statistic or Microcosm’s transformation of published figures + (uprating, calendar-year windows of fiscal-year figures, published ratios, or rescaling to a published + higher-level total); each row’s notes say which. Rows whose source reads “Microcosm derivation from open + data” are built from open data such as HM Land Registry price-paid records. Rows that list only + constituencies, local authorities or Scottish Broad Rental Market Areas (BRMAs), + and rows marked “local-area file only”, calibrate the local-area file alone; a few cells of other rows are + also calibrated in the local-area file only. How closely each file meets its targets is measured on licensed + survey microdata, so those figures are available to UK Data Service licence holders only. +

+ + + +

+ {filtered.length} target group{filtered.length !== 1 ? 's' : ''} shown + {search ? ` (filtered from ${allRows.length})` : ''} +

+ + {filtered.length === 0 ? ( +

No calibration targets found.

+ ) : ( + // contain: inline-size keeps the table's width from widening the page + // on phones; the table scrolls inside this box instead. +
+ + + + {[ + { label: 'Target', width: '30%' }, + { label: 'Breakdown', width: '18%' }, + { label: 'Geographic levels', width: '22%' }, + { label: 'Value', width: '13%' }, + { label: 'Source', width: '17%' }, + ].map((col) => ( + + ))} + + + + {filtered.map((r, i) => ( + + + + + + + + ))} + +
+ {col.label} +
+
+ {r.label} +
+ {r.scope === 'national' && r.localAreaFileOnly && ( +
+ Local-area file only +
+ )} + {r.notes.length > 0 && ( +
+ {r.notes.join(' · ')} +
+ )} +
+ {r.breakdown ?? '—'} + +
+ {LEVELS.filter((l) => r.geoLevels.includes(l)).map((level) => ( + + {levelBadge(r, level)} + + ))} +
+
+ {formatUkValue(r.nationalValue, r.unit)} + {r.nationalValue !== null && r.nationalValueGeography && r.nationalValueGeography !== 'United Kingdom' && ( +
+ {r.nationalValueGeography} +
+ )} +
+ {r.source} +
+
+ )} +
+ ); +} From 462b34a4bb6655dd11ca717947feaa53b76b9aa8 Mon Sep 17 00:00:00 2001 From: Maria Juaristi Date: Tue, 27 Oct 2026 11:56:35 +0000 Subject: [PATCH 03/13] Describe the Microcosm UK pipeline The UK pipeline page described the Enhanced FRS build (FRS 2023-24, WAS round 7, constituency and local-authority weight matrices). It now follows the Microcosm UK build at microcosm 90ff43f (policyengine-uk 2.162.8): the 49-stage spine, then the national file and the 150,000-household local-area file, their calibration and gates, publication, and how policyengine.py serves national and area results. Steps new at 90ff43f are marked as such. DataPipeline.tsx gets the UK intro and branch labels, and a UK-only ScrollBox so the wider UK flow and the stage tables scroll inside their box on phones. The US renders as before. Co-Authored-By: Claude Opus 5.5 --- src/components/data/DataPipeline.tsx | 30 +- src/data/ukPipelineStages.ts | 460 ++++++++++++++++----------- 2 files changed, 299 insertions(+), 191 deletions(-) diff --git a/src/components/data/DataPipeline.tsx b/src/components/data/DataPipeline.tsx index 73d54c1..68a3308 100644 --- a/src/components/data/DataPipeline.tsx +++ b/src/components/data/DataPipeline.tsx @@ -37,6 +37,19 @@ const iconMap: Record{children}; + return ( +
+ {minWidth ?
{children}
: children} +
+ ); +} + function StageButton({ s, isActive, onClick, borderRadiusLeft, borderRadiusRight, marginLeft }: { s: PipelineStage; isActive: boolean; @@ -124,11 +137,16 @@ export default function DataPipeline({ country = 'us' }: { country?: Country }) }; const introText = country === 'uk' - ? 'PolicyEngine constructs its representative household dataset through a multi-stage pipeline, starting from the Family Resources Survey and producing two weight matrices: one for 650 parliamentary constituencies and one for 360 local authorities.' + ? 'PolicyEngine’s UK model runs on Microcosm UK, built by Microcosm, PolicyEngine’s data build system; since version 6.10.0 of the policyengine Python package it has replaced the Enhanced FRS as the certified UK dataset. A 49-stage spine turns Family Resources Survey 2024-25 households, plus synthetic copies that carry high incomes, home values and capital gains, into one calibration-ready file. Two files are calibrated from it: a national file, reweighted to UK, nation and English region targets and certified as policyengine.py’s UK default, and a local-area file of exactly 150,000 households placed in small areas and calibrated to constituency and local-authority targets as well. UK and nation results come from the national file; constituency and local-authority results come from the local-area file, with its own weights. This page follows Microcosm commit 90ff43f (policyengine-uk 2.162.8); steps marked “new at 90ff43f” are not in the release pair policyengine.py 6.11.1 serves.' : 'PolicyEngine constructs its representative household dataset through a 14-step pipeline. Public survey data is merged, stratified, and cloned to 10 geographic variants per household. Each clone is simulated through PolicyEngine US with stochastic take-up, then calibrated via L0-regularized optimization against administrative targets at the national, state, and congressional district levels simultaneously, producing 488 geographically representative datasets.'; - const nationalLabel = country === 'uk' ? 'Constituency (national)' : 'National'; - const localLabel = country === 'uk' ? 'Local authority' : 'Geography-specific'; + const nationalLabel = country === 'uk' ? 'National file' : 'National'; + const localLabel = country === 'uk' ? 'Local-area file' : 'Geography-specific'; + const scrollBoxes = country === 'uk'; + const flowMinWidth = Math.max( + sharedStages.length * 110, + 2 * Math.max(nationalStages.length, localStages.length, localSub2Stages.length, localSub4Stages.length) * 110 + 24, + ); return (
@@ -136,6 +154,7 @@ export default function DataPipeline({ country = 'us' }: { country?: Country }) {introText}

+ {/* Pipeline flow - branching layout */}
{/* Shared linear portion */} @@ -347,6 +366,7 @@ export default function DataPipeline({ country = 'us' }: { country?: Country })
)}
+ {/* Stage detail */} @@ -630,6 +650,7 @@ export default function DataPipeline({ country = 'us' }: { country?: Country }) className="tw:overflow-hidden" >
+ @@ -654,6 +675,7 @@ export default function DataPipeline({ country = 'us' }: { country?: Country }) ))}
+
)} @@ -694,6 +716,7 @@ export default function DataPipeline({ country = 'us' }: { country?: Country }) className="tw:overflow-hidden" >
+ @@ -718,6 +741,7 @@ export default function DataPipeline({ country = 'us' }: { country?: Country }) ))}
+
)} diff --git a/src/data/ukPipelineStages.ts b/src/data/ukPipelineStages.ts index c3a578c..0d845fe 100644 --- a/src/data/ukPipelineStages.ts +++ b/src/data/ukPipelineStages.ts @@ -1,247 +1,331 @@ import type { PipelineStage } from './pipelineStages'; +// UK pipeline: the Microcosm UK build at PolicyEngine/microcosm 90ff43f +// (policyengine-uk 2.162.8). The stage list, methods and parameters follow +// the build code at that commit (packages/microcosm-build/src/microcosm/build/ +// uk/source_stages.json, uk/gates.json and uk_runtime/*_doctrine.py); the +// serving rules follow policyengine.py's bundle manifest and UK regions. +// Steps marked NEW are not in the release pair that policyengine.py 6.11.1 +// serves (built at microcosm 67ee2ddf3 with policyengine-uk 2.151.0). +// No figure here comes from a build: they are code parameters, counts of +// stages, gates and areas, and statistics their publishers state. +const NEW = '(new at 90ff43f)'; + export const ukPipelineStages: PipelineStage[] = [ { - id: 'frs', + id: 'frs-base', branch: 'shared', - title: 'Family Resources Survey', - subtitle: 'Base microdata', - description: 'The Family Resources Survey (FRS) 2023-24 provides the baseline microdata, covering income, benefits, housing, and demographics for ~20,000 UK households. Variables are mapped to PolicyEngine\'s system, and benefit take-up rates are stochastically assigned.', + title: 'FRS base', + subtitle: 'Family Resources Survey 2024-25', + description: 'Every record starts as a Family Resources Survey household for 2024-25, with its benefit units and people. Households keep their own reported incomes, benefits and housing, and start from the FRS grossing weight. Only the 2024-25 survey is used; years are not pooled. The first 13 stages of the spine map the survey to policyengine-uk’s inputs and draw the attributes the survey does not record.', icon: 'chart-bar', - inputSize: '~20,000 households', - outputSize: '~20,000 households', - dataSources: ['DWP Family Resources Survey 2023-24'], + inputSize: 'FRS 2024-25 households', + dataSources: ['DWP Family Resources Survey 2024-25 (UK Data Service SN 9563)'], + details: [ + 'The 16,299 households are DWP’s published achieved sample for 2024-25; the Scottish Government puts Scotland’s at about 1,900', + 'Starting weight: the FRS grossing weight (GROSS4); weekly amounts are annualised at 52 weeks', + 'The only geography the survey records is the FRS region (12 values)', + 'Ages top-coded at 80 are redrawn from ONS populations aged 80 to 84, 85 to 89 and 90 and over', + 'A keyed draw is a deterministic random draw keyed on the record’s identity, with a declared seed, so a rebuild draws the same values', + 'Every published record keeps a lineage key naming its FRS source household and how it was copied', + ], + imputations: [ + { name: 'Missing council tax bills', source: 'FRS 2024-25', method: 'Cell means' }, + { name: 'Ages 80 and over', source: 'ONS mid-year population estimates', method: 'Redrawn within 80 to 84, 85 to 89 and 90+' }, + { name: 'Take-up of Child Benefit, Pension Credit, Universal Credit, Tax-Free Childcare and the childcare entitlements', source: 'Microcosm take-up contract (published rates)', method: 'Keyed draw' }, + { name: 'Marriage Allowance, Scottish Child Payment, Tax-Free Childcare routing', source: 'Published rates', method: 'Keyed draw' }, + { name: `Apprentice flag ${NEW}`, source: 'DfE apprenticeships 2024/25', method: 'Keyed draw: employees aged 21 to 24 until the flagged weight matches England’s rate of apprentices per head aged 19 to 24, applied UK-wide' }, + { name: 'TV licence, first-time buyer, property purchase', source: 'Published rates', method: 'Keyed draw' }, + { name: 'Broad Rental Market Area', source: 'Census private-rented households', method: 'Drawn within region and LHA category' }, + ], + }, + { + id: 'high-incomes', + branch: 'shared', + title: 'High incomes', + subtitle: 'SPI copies and band donors', + description: 'Few survey households have very high incomes, so the spine adds copies of FRS households whose incomes come from HMRC’s Survey of Personal Incomes Public Use Tape, and whole-household donors for HMRC’s top income bands. The FRS households keep their own reported incomes. The copies’ incomes come from the tape; HMRC’s published income tables are calibration targets and do not shape them.', + icon: 'building-bank', + inputSize: 'FRS households', + outputSize: '+10,000 SPI copies, band donors', + dataSources: ['HMRC SPI Public Use Tape 2022-23 (SN 9422)', 'HMRC Income Tax liabilities statistics', 'ONS ASHE 2024'], details: [ - 'Demographics: age, gender, marital status, employment status, education, region (12 UK regions)', - '18+ reported benefit types: child benefit, universal credit, PIP, DLA, ESA, JSA, state pension, pension credit, housing benefit, carer\'s allowance, and more', - 'Income: employment, self-employment, private pension, savings interest, dividends, property, maintenance', - 'Housing: rent, council tax (with imputation for ~75% missing), mortgage interest/capital, tenure type, accommodation type', - 'Stochastic take-up: child benefit, UC, pension credit, marriage allowance, Scottish Child Payment, tax-free childcare, extended/universal/targeted childcare', - 'Grossing weights from FRS GROSS4 field', + 'SPI channel: 10,000 copies of FRS households (uniform draw, seed 42) start at zero weight; within each region they take 50% of the weight of households with nobody at or over State Pension age and 20% of the weight of the others', + 'Band donors: whole-household copies for the HMRC Income Tax liabilities Table 2.5 bands from £200,000 (£200k to £500k, £500k to £1m, £1m to £2m, £2m and over); each band seats max(120, ⌈published taxpayers ÷ 250⌉) donors at equal weight, summing to its published taxpayer count, with the weight taken from the donor’s region', + 'Income stage 1: a quantile regression forest on a weighted resample of the tape draws 21 income items at the quantile of each copy’s FRS twin, given age, sex, region, main income source, whether there is pay or trading income, and State Pension age', + 'Tape incomes are rebased from 2022 to 2024 with each variable’s policyengine-uk uprating index; income stage 2 fills the items the tape lacks from a forest trained on FRS households', + 'Annual income spells describe the year before interview, so HMRC’s annual targets read annual income', + 'Draws are keyed on record ids, and imputed values stay within the donors’ observed range', ], imputations: [ - { name: 'Council tax (missing ~75%)', source: 'FRS 2023-24', method: 'Imputed from council tax band and regional rates' }, - { name: 'Benefit take-up (child benefit)', source: 'HMRC/DWP', method: 'Stochastic assignment at reported take-up rates' }, - { name: 'Benefit take-up (UC, pension credit)', source: 'DWP', method: 'Stochastic assignment at reported rates' }, - { name: 'Benefit take-up (tax-free childcare)', source: 'HMRC', method: 'Stochastic assignment at reported rate' }, - { name: 'Benefit take-up (childcare entitlements)', source: 'DfE', method: 'Stochastic assignment: extended, universal, targeted childcare' }, - { name: 'Property purchase', source: 'HMRC transaction data', method: '3.85% stochastic assignment' }, - { name: 'Private school attendance', source: 'ISC Census 2024', method: 'Stochastic assignment by region' }, - { name: 'TV ownership and licence evasion', source: 'TV Licensing data', method: 'Stochastic assignment' }, + { name: 'Employer pension contributions', source: 'ONS ASHE 2024 pension tables', method: 'Rate drawn by scheme type and sector' }, + { name: '21 income items of the SPI copies', source: 'SPI Public Use Tape 2022-23', method: 'Quantile regression forest at the FRS twin’s quantile' }, + { name: 'Items the tape does not carry', source: 'FRS 2024-25', method: 'Quantile regression forest' }, + { name: 'Housing of the SPI copies', source: 'FRS 2024-25', method: 'Classifiers and a quantile regression forest trained on FRS households' }, ], }, { - id: 'wealth', + id: 'wealth-property', branch: 'shared', - title: 'Wealth imputation', - subtitle: 'WAS → FRS', - description: 'Wealth and vehicle ownership variables are imputed from the Wealth and Assets Survey (WAS) Round 7 onto FRS households using quantile random forests. This adds property, financial, and pension wealth needed for capital tax calculations, and vehicle ownership needed for fuel consumption bridging.', + title: 'Wealth and property', + subtitle: 'WAS, Land Registry, HMRC', + description: 'Household wealth comes from the ONS Wealth and Assets Survey, round 8, by a chain of quantile regression forests. Home values are then re-levelled to published house prices, and landlords’ costs and property purchases are aligned to HMRC statistics.', icon: 'coin', - inputSize: '~20,000 households', - outputSize: '~20,000 households', - dataSources: ['Wealth and Assets Survey Round 7 (2018–20, released March 2022)'], + dataSources: ['ONS Wealth and Assets Survey round 8 (SN 7215)', 'HM Land Registry Price Paid', 'UK HPI', 'HMRC Property Rental Income Statistics', 'HMRC Annual Stamp Tax Statistics'], details: [ - '11 predictors: household net income, adults, children, pension/employment/self-employment/capital income, bedrooms, council tax, renting, region', - 'Northern Ireland households mapped to Wales (WAS does not sample NI)', - 'Must run before consumption imputation (num_vehicles needed as predictor)', + 'WAS round 8 covers April 2020 to March 2022 in Great Britain; Northern Ireland households are predicted with the Wales pattern', + 'Wealth is not index-uprated; home values are re-levelled by the stages below, and on the local-area file by area', + 'Owner home values are rescaled to calendar-2024 regional UK HPI means (Northern Ireland is not rescaled)', + 'Owner households at or above their region’s weighted 95th percentile of home value are split into 6 copies, conserving household weight', + 'England and Wales owners take their home value from their region’s Land Registry price-paid distribution at the household’s weighted rank', ], imputations: [ - { name: 'Owned land', source: 'WAS Round 7', method: 'Quantile random forest' }, - { name: 'Property wealth', source: 'WAS Round 7', method: 'Quantile random forest' }, - { name: 'Corporate wealth (pensions + shares + ISAs)', source: 'WAS Round 7', method: 'Quantile random forest' }, - { name: 'Gross/net financial wealth', source: 'WAS Round 7', method: 'Quantile random forest' }, - { name: 'Main residence value', source: 'WAS Round 7', method: 'Quantile random forest' }, - { name: 'Other residential property', source: 'WAS Round 7', method: 'Quantile random forest' }, - { name: 'Non-residential property', source: 'WAS Round 7', method: 'Quantile random forest' }, - { name: 'Savings', source: 'WAS Round 7', method: 'Quantile random forest' }, - { name: 'Number of vehicles', source: 'WAS Round 7', method: 'Quantile random forest' }, + { name: 'Wealth: property, financial, pension and other components', source: 'WAS round 8', method: 'Quantile regression forest chain' }, + { name: 'Lifetime ISA', source: 'WAS round 8', method: 'Drawn from WAS' }, + { name: 'Home value, England and Wales owners', source: 'HM Land Registry Price Paid, UK HPI', method: 'Regional distribution at the household’s weighted rank' }, + { name: 'Landlords’ finance costs', source: 'SPI Public Use Tape', method: 'Drawn from the tape' }, + { name: 'Property income receipts', source: 'HMRC Property Rental Income Statistics 2024-25', method: 'Aligned to published bands' }, + { name: 'Property purchases', source: 'HMRC Annual Stamp Tax Statistics 2024-25', method: 'Hazard draws matched to published transactions and values' }, ], }, { - id: 'consumption', + id: 'spending-travel', branch: 'shared', - title: 'Consumption imputation', - subtitle: 'LCFS → FRS', - description: 'Spending patterns are imputed from the Living Costs and Food Survey (LCFS) onto FRS households, enabling VAT, fuel duty, and indirect tax modeling. A two-step bridge via WAS vehicle ownership corrects LCFS fuel underreporting.', + title: 'Spending and travel', + subtitle: 'LCFS, ETB, NTS', + description: 'Spending comes from the ONS Living Costs and Food Survey 2024-25 by a chain of quantile regression forests over 25 spending targets, then is aligned to published totals. The Effects of Taxes and Benefits file supplies the VAT share and public services used, and the National Travel Survey bus use.', icon: 'shopping-cart', - inputSize: '~20,000 households', - outputSize: '~20,000 households', - dataSources: ['Living Costs and Food Survey 2021-22', 'NTS 2024 (vehicle ownership)'], + dataSources: ['ONS Living Costs and Food Survey 2024-25 (SN 9603)', 'ONS Effects of Taxes and Benefits (SN 8856)', 'DfT National Travel Survey (SN 5340)'], details: [ - '12 COICOP categories + petrol/diesel + domestic energy = 14 consumption variables', - '8 predictors: adult/child status, region, employment/self-employment/pension income, household net income, has_fuel_consumption', - 'Two-step fuel bridge: WAS vehicle ownership (78%) corrects LCFS diary undercount (58%)', - 'LCFS data uprated from 2021 to 2024 using CPI from OBR, with separate 1.3x fuel uprating', + 'Spending by COICOP division, road fuel, domestic energy and bus fares from LCFS 2024-25 (2nd edition)', + 'Domestic energy is raked to DESNZ NEED 2024 and priced with Quarterly Energy Prices for calendar 2024; road fuel is levelled to ONS Consumer Trends (COICOP 07.2.2)', + `Alcohol, tobacco and gambling duty inputs: LCFS shares, excise quantities levelled to HMRC and gambling participation topped up to the Gambling Survey for Great Britain 2025 ${NEW}`, + 'Full-rate VAT spending share and in-kind education, rail and bus subsidy from ETB (donor year FYE 2024); NHS spending is allocated per person by age and sex', + 'Bus use, trips and concessionary eligibility from the NTS (19th edition), fitted on survey years 2022 to 2024 for England residents', + 'LCFS amounts are uprated to 2024 with CPI, DESNZ fuel prices and NEED energy; ETB amounts with OBR earnings, CPI and ORR and DfT ratios', ], imputations: [ - { name: 'Food and non-alcoholic beverages', source: 'LCFS 2021-22', method: 'Quantile random forest' }, - { name: 'Alcohol and tobacco', source: 'LCFS 2021-22', method: 'Quantile random forest' }, - { name: 'Clothing and footwear', source: 'LCFS 2021-22', method: 'Quantile random forest' }, - { name: 'Housing, water, and electricity', source: 'LCFS 2021-22', method: 'Quantile random forest' }, - { name: 'Household furnishings', source: 'LCFS 2021-22', method: 'Quantile random forest' }, - { name: 'Health', source: 'LCFS 2021-22', method: 'Quantile random forest' }, - { name: 'Transport', source: 'LCFS 2021-22', method: 'Quantile random forest' }, - { name: 'Communication', source: 'LCFS 2021-22', method: 'Quantile random forest' }, - { name: 'Recreation', source: 'LCFS 2021-22', method: 'Quantile random forest' }, - { name: 'Education', source: 'LCFS 2021-22', method: 'Quantile random forest' }, - { name: 'Restaurants and hotels', source: 'LCFS 2021-22', method: 'Quantile random forest' }, - { name: 'Miscellaneous goods and services', source: 'LCFS 2021-22', method: 'Quantile random forest' }, - { name: 'Petrol and diesel spending', source: 'LCFS 2021-22', method: 'Quantile random forest (fuel-bridged)' }, - { name: 'Domestic energy consumption', source: 'LCFS 2021-22', method: 'Quantile random forest' }, - ], - }, - { - id: 'vat-services', + { name: 'Spending: 25 targets by COICOP division, road fuel, domestic energy and bus fares', source: 'LCFS 2024-25', method: 'Quantile regression forest chain' }, + { name: 'Full-rate VAT spending share', source: 'ETB, donor year FYE 2024', method: 'Drawn from donors' }, + { name: 'Education, rail and bus subsidy in kind', source: 'ETB, donor year FYE 2024', method: 'Drawn from donors' }, + { name: 'Local bus use and concessionary eligibility', source: 'NTS, survey years 2022 to 2024', method: 'Fitted on England residents' }, + ], + }, + { + id: 'benefits-take-up', branch: 'shared', - title: 'VAT and public services', - subtitle: 'ETB + NHS data', - description: 'VAT expenditure rates and public service usage (NHS, education, transport subsidies) are imputed from the Effects of Taxes and Benefits survey and NHS administrative data.', + title: 'Benefits and take-up', + subtitle: 'Coherence and take-up', + description: 'Reported benefits are made coherent with the drawn incomes and capital. policyengine-uk then computes each benefit unit’s entitlements, and take-up is drawn so that caseloads match DWP and HMRC statistics.', icon: 'heart-pulse', - inputSize: '~20,000 households', - outputSize: '~20,000 households', - dataSources: ['Effects of Taxes and Benefits survey (2020)', 'NHS consumption by age/gender', 'NTS 2024'], + dataSources: ['DWP Stat-Xplore', 'DWP Move to UC statistics', 'DWP income-related benefit take-up estimates', 'HMRC Child Benefit statistics'], details: [ - 'VAT: full-rate expenditure share imputed via QRF (assumes 3% at reduced rate per OBR)', - 'NHS: deterministic assignment of per-person spending by age-gender cell, scaled to £202bn 2025/26 budget', - 'NHS visit types: A&E, admitted patient, outpatient (6 variables)', - 'Education: DfE spending by primary/secondary/further education', - 'Transport: rail and bus subsidy spending from ETB', + 'Universal Credit reporters are redrawn, and benefits made coherent with SPI-drawn incomes and with Universal Credit capital rules', + 'Universal Credit: engine awards, then take-up drawn to DWP Stat-Xplore households by family type and award band', + 'Take-up flags must land within ±0.05 of their contract rates, a release-blocking check', ], imputations: [ - { name: 'Full-rate VAT expenditure rate', source: 'ETB 2020', method: 'Quantile random forest' }, - { name: 'NHS A&E/admitted/outpatient visits and spending', source: 'NHS data', method: 'Deterministic by age-gender cell' }, - { name: 'Education spending (DfE)', source: 'ETB 2020', method: 'Quantile random forest' }, - { name: 'Rail and bus subsidy', source: 'ETB 2020', method: 'Quantile random forest' }, + { name: 'Universal Credit take-up', source: 'DWP Stat-Xplore households by family type and award band', method: 'Keyed draw on engine awards' }, + { name: 'Legacy benefit closure take-up', source: 'DWP Move to UC statistics', method: 'Keyed draw' }, + { name: 'Pension Credit take-up', source: 'DWP take-up estimates, FYE 2024', method: 'Keyed draw' }, + { name: 'Child Benefit take-up', source: 'HMRC claim rates', method: 'Keyed draw' }, + { name: 'Carer’s Allowance take-up (Carer Support Payment in Scotland)', source: 'Take-up contract rate', method: 'Keyed draw' }, + { name: 'Universal Credit deductions', source: 'DWP deductions statistics', method: 'Drawn attributes' }, ], }, { - id: 'spi-income', + id: 'gains-pensions', branch: 'shared', - title: 'SPI income imputation', - subtitle: 'HMRC → FRS (stacked)', - description: 'Income variables are imputed from the HMRC Survey of Personal Incomes (SPI) to enrich the high-income tail. The FRS is stacked: original records keep their income, and a subsampled zero-weight copy (10,000 households) receives SPI-imputed incomes. Calibration later activates the most informative records.', - icon: 'building-bank', - inputSize: '~20,000 households + 100,000 SPI records', - outputSize: '~30,000 households (stacked)', - dataSources: ['HMRC Survey of Personal Incomes 2020-21'], + title: 'Gains, pensions, loans', + subtitle: 'CGT, salary sacrifice, SLC', + description: 'Capital gains are placed on households through copies and anchored to HMRC Capital Gains Tax statistics. Salary sacrifice, student loans, the childcare copies and private school attendance complete the spine’s 49 stages.', + icon: 'trending-up', + dataSources: ['HMRC Capital Gains Tax statistics', 'HMRC and OBR salary-sacrifice figures', 'SLC student loan forecasts', 'Advani and Summers (2020)'], details: [ - 'Original FRS: only dividend_income imputed from SPI', - 'Zero-weight copy (10,000 households): all 6 income variables imputed', - 'Stacked: original + zero-weight records (calibration decides which to activate)', - '3 predictors: age, gender, region', - 'Rent and mortgage adjusted proportionally to income changes', + 'Capital gains: a support split, an incidence clone at two halves of the weight, gains drawn to HMRC Capital Gains Tax Table 3 amounts for 2024-25 and raked to Tables 5 and 6, an incidence anchor that moves weight only, a residential split and asset types; every split conserves household weight', + 'Who has gains follows Advani and Summers (2020), CAGE working paper 465', + 'Salary sacrifice: a quantile regression forest on the FRS respondents asked about it, aligned to HMRC and OBR figures, bonus sacrifice included', + 'Student loans: plan cohorts topped up to Student Loans Company stocks for England (forecasts Table 6a)', + 'Private school attendance is policyengine-uk’s own imputation, stored in the file', ], imputations: [ - { name: 'Employment income', source: 'SPI 2020-21', method: 'Quantile random forest (zero-weight copy)' }, - { name: 'Self-employment income', source: 'SPI 2020-21', method: 'Quantile random forest (zero-weight copy)' }, - { name: 'Savings interest income', source: 'SPI 2020-21', method: 'Quantile random forest (zero-weight copy)' }, - { name: 'Dividend income', source: 'SPI 2020-21', method: 'Quantile random forest (both copies)' }, - { name: 'Private pension income', source: 'SPI 2020-21', method: 'Quantile random forest (zero-weight copy)' }, - { name: 'Property income', source: 'SPI 2020-21', method: 'Quantile random forest (zero-weight copy)' }, + { name: 'Capital gains and their asset types', source: 'HMRC Capital Gains Tax statistics, Tables 3 to 8', method: 'Incidence copies, raked to published tables' }, + { name: 'Salary-sacrificed pension contributions', source: 'FRS 2024-25 respondents asked; HMRC, OBR', method: 'Quantile regression forest, then aligned' }, + { name: 'Student loan plans', source: 'SLC forecasts Table 6a', method: 'Cohorts topped up to published stocks' }, + { name: 'Childcare copies’ take-up', source: 'Take-up contract', method: 'Keyed draw on the copies’ own benefit units' }, + { name: 'Private school attendance', source: 'policyengine-uk', method: 'The engine’s imputation, stored' }, ], }, { - id: 'capital-gains', + id: 'spine', branch: 'shared', - title: 'Capital gains', - subtitle: 'Advani-Summers distribution', - description: 'Capital gains are imputed using percentile distributions from Advani & Summers research, split by income band. The dataset is doubled: each household appears as a "has gains" and "no gains" copy, with PyTorch-optimised weight splitting matching the income-band-specific percentage with gains.', - icon: 'trending-up', - inputSize: '~30,000 households', - outputSize: '~60,000 households (doubled)', - dataSources: ['Advani & Summers capital gains percentile distributions'], + title: 'Spine', + subtitle: '49 stages, 47 checks', + description: 'The 49 stages produce one calibration-ready spine. One spine serves both files of a release pair: the two are separate solves that share the spine and the pinned target feed, not weights.', + icon: 'checkbox', + inputSize: '49 stages', + outputSize: 'One spine', + dataSources: ['PolicyEngine/chronicle (pinned target feed)'], details: [ - 'Dataset duplicated: each household gets "has CG" and "no CG" versions', - 'PyTorch Adam optimiser with sigmoid blend factors splits each household\'s weight', - '100 optimisation epochs matching income-band-specific gain percentages', - 'CG amounts assigned via univariate splines fit to percentile distributions (p05-p95)', + '47 spine checks cover stage health, conservation of household weight, donor support, coherence, published stocks and category values', + 'Each check gets exactly one outcome; checks run in batches that stop the build at each phase boundary, and every report is signed', + 'Targets and the published facts the spine reads come from one pinned commit of PolicyEngine/chronicle, a ledger of publishers’ aggregate statistics with no microdata; a build refuses a feed whose digests differ', + 'Stored values are at the 2024 basis; each release is a single-year file, and later years use policyengine-uk’s uprating', ], }, { - id: 'misc-imputations', - branch: 'shared', - title: 'Other imputations', - subtitle: 'Salary sacrifice, student loans', - description: 'Remaining imputations cover salary sacrifice pension contributions (from FRS respondents asked about SALSAC) and student loan plan assignment based on age and reported repayments.', - icon: 'school', - inputSize: '~60,000 households', - outputSize: '~60,000 households', - dataSources: ['FRS 2023-24 (SALSAC field)', 'Student loan plan rules'], - details: [ - 'Salary sacrifice: QRF trained on ~4,000 FRS jobs with SALSAC response, imputed to ~13,000 not asked', - 'Student loan plan: rule-based from age and repayments (Plan 1 before 2012, Plan 2 2012-2023, Plan 5 2023+)', - 'Uprating from 2023 to 2025 using OBR economic assumptions for all variables', + id: 'national-calibration', + branch: 'national', + title: 'National calibration', + subtitle: 'UK, nations, regions', + description: 'The spine’s households, with no copies and no small areas, are reweighted so their weighted totals match the national target register for calendar 2025: the UK, Great Britain, England and Wales, the four nations and the English regions.', + icon: 'scale', + inputSize: 'Spine households', + outputSize: 'National weights', + details: [ + 'Adam on log-weights, which keeps weights positive: 1,500 epochs, learning rate 0.02, seed 0', + 'The loss is the weighted mean over targets of min(|relative error|, 10), with relative error on the scale max(|target|, 1)', + 'Target weights (family_equal): each target family gets an equal share, split evenly within the family', + 'No weight may exceed 10 times its starting weight; the household total is free; there is no L0 or L2 penalty', + 'Each household is measured at 2025 through policyengine-uk’s projection of its stored inputs; fiscal-year facts bind to calendar 2025 as 3/12 of one year plus 9/12 of the next', + 'Lower-grain targets are rescaled to sum to their published higher-grain controls; only target values move, never weights', + 'Some measures are held out of the fit and reported as diagnostics, such as OBR VAT, ORR rail support and employer National Insurance', ], - imputations: [ - { name: 'Pension contributions via salary sacrifice', source: 'FRS 2023-24', method: 'Quantile random forest from age, employment income' }, - { name: 'Student loan plan type', source: 'Rules', method: 'Deterministic from age and repayment status' }, + calibrationTargets: [ + { variable: 'Incomes, Income Tax and Capital Gains Tax', target: 'UK, nations, regions; by income band', source: 'HMRC' }, + { variable: 'Benefit caseloads and spending', target: 'GB, nations, regions', source: 'DWP, DfC (NI), OBR' }, + { variable: 'Receipts and spending forecasts', target: 'UK, GB, England and Wales', source: 'OBR' }, + { variable: 'Population and households', target: 'UK, nations, regions', source: 'ONS, NRS, NISRA, Welsh Government' }, + { variable: 'Council Tax dwelling stock', target: 'English regions, Scotland, Wales', source: 'MHCLG, Scottish Government, Welsh Government' }, + { variable: 'Transport, education, childcare and student loans', target: 'UK, nations, regions', source: 'DfT, DfE, SLC, ISC, NI bodies' }, ], + link: { label: 'View calibration targets', path: '/data/calibration' }, }, { - id: 'constituency-calibration', + id: 'national-release', branch: 'national', isFinalDataset: true, - title: 'Constituency calibration', - subtitle: '650 × N weight matrix', - description: 'Unlike the US L0 approach, the UK uses a weight matrix: a single 650 × N matrix where each constituency gets its own set of household weights, optimised jointly with national targets using gradient descent. Column sums give the national weights. This single dataset serves both national and constituency-level analysis.', - icon: 'checkbox', - inputSize: '~60,000 households (uprated)', - outputSize: '650 × ~60,000 weight matrix', - dataSources: ['ONS mid-year population estimates', 'HMRC SPI by constituency', 'DWP Stat-Xplore (UC)', 'OBR', 'HMRC', 'DWP', 'VOA'], - details: [ - 'PyTorch Adam optimiser (lr=0.1, 512 epochs), not L0 regularisation', - 'Country mask: households only get weight in constituencies of the same country (England/Wales/Scotland/NI)', - 'Country-aware initialisation: initial weight ÷ number of areas in household\'s country', - '5% weight dropout for regularisation', - 'Symmetric relative loss: min(((1+x)/(1+y)-1)², ((1+y)/(1+x)-1)²)', - 'Joint optimisation of constituency-level and national targets simultaneously', - 'Column sums = national weights (no separate national calibration needed)', - 'Boundary mapping: 2010-boundary constituency targets transformed to 2024 boundaries via population overlap matrix', + title: 'National release', + subtitle: '19-gate cut, certified', + description: 'The national file, microcosm_uk_2024_25.h5, must pass a 19-gate release cut and beat the pinned Enhanced FRS on their common target surface before it is published and certified as policyengine.py’s UK default.', + icon: 'building-community', + outputSize: 'microcosm_uk_2024_25', + details: [ + 'Seven calibration-seam gates check target fit, CGT projection entrants, weight ratio, weight effective sample size, zero-weight strata, administrative totals and reference coverage', + 'Each national target’s |relative error| must be at most 0.25 unless a reviewed, signed register entry lifts it; the effective sample size must be at least 1% of records', + 'The 19 release-cut gates cover input coverage, target and export surfaces, parity of weighted input totals with a pinned Enhanced FRS 2024-25 reference, take-up, support, category values and a weights audit', + 'release_certification.json combines the signed, digest-pinned spine, seam and release-cut reports; whether the file can ship is recomputed from those parts', + 'Published by immutable cut tag (microcosm-uk-2024-25-national-