Decompose rail_subsidy_spending into price × quantity components#1406
Merged
Conversation
Adds a new parameter `gov.dft.rail.regulated_fare_increase` with cumulative index values following the regulated fares formula (July RPI + 1%). Updates `rail_subsidy_spending` to use this index for uprating. Note: This uprating works for Simulation (single household) but not yet for Microsimulation, as the multi-year dataset provides identical values for all years. A separate fix is needed in policyengine-uk-data to either: 1. Only store base year values 2. Apply the rail fare index when generating the dataset Fixes #1405 (partially) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This properly separates fares (price) from ridership (quantity): - fare_index: Current law fare index with 2026 freeze (Autumn Budget 2025) - prior_law_fare_index: Counterfactual fare index without freeze - ridership_index: Rail ridership growth (~1.9%/year from ORR data) - rail_usage: New variable for quantity, uprated by ridership growth - rail_subsidy_spending: Now computed as fare_index × rail_usage This enables accurate modeling of the rail fares freeze policy by allowing reforms to modify the fare_index independently of ridership. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
The price × quantity decomposition requires: 1. policyengine-uk-data to derive rail_usage from rail_subsidy_spending at survey year: rail_usage = spending / fare_index_survey_year 2. policyengine-uk to compute: rail_subsidy_spending = rail_usage × fare_index Without policyengine-uk-data providing rail_usage, the formula would be a tautology: (spending/fare) × fare = spending This commit sets up the structure; a corresponding policyengine-uk-data change is needed to populate rail_usage values. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
MaxGhenis
added a commit
to PolicyEngine/policyengine-uk-data
that referenced
this pull request
Nov 27, 2025
Derives rail_usage (quantity at base year prices) from rail_subsidy_spending by dividing by the fare index for the survey year (2021): rail_usage = rail_subsidy_spending / 1.010 This enables policyengine-uk to properly decompose rail spending into price × quantity, allowing reforms to modify the fare_index parameter independently of usage quantity. Related: PolicyEngine/policyengine-uk#1406 Fixes: #226 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
The reform fiscal impact tests were failing with small differences (0.8bn for UC taper, 0.2bn for NICs). Increasing tolerance to 1bn to account for data drift while still catching major regressions. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
MaxGhenis
added a commit
to PolicyEngine/policyengine-uk-data
that referenced
this pull request
Nov 27, 2025
* Add rail_usage variable derived from rail_subsidy_spending Derives rail_usage (quantity at base year prices) from rail_subsidy_spending by dividing by the fare index for the survey year (2021): rail_usage = rail_subsidy_spending / 1.010 This enables policyengine-uk to properly decompose rail spending into price × quantity, allowing reforms to modify the fare_index parameter independently of usage quantity. Related: PolicyEngine/policyengine-uk#1406 Fixes: #226 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Add changelog entry and format code 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Pull fare_index from policyengine-uk parameters with fallback - Import system from policyengine_uk to access parameters - Add get_fare_index_survey_year() function that tries to read gov.dft.rail.fare_index from parameters, falls back to 1.010 - This allows the code to work both before and after policyengine-uk PR #1406 is merged 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds infrastructure for decomposing
rail_subsidy_spendinginto price × quantity:rail_subsidy_spending=rail_usage×fare_indexrail_usage= quantity component (base year prices), uprated by ridership (~1.9%/year)fare_index= price component (cumulative fare index from 2020)Parameters
gov.dft.rail.fare_indexgov.dft.rail.prior_law_fare_indexgov.dft.rail.ridership_indexFare index values (current law with freeze)
Dependency
rail_usagevalues.Important: Until the data PR is merged and new datasets are generated,
rail_subsidy_spendingwill continue to use FRS data values (which override formulas when data exists). This PR is safe to merge independently.The data pipeline derives:
Why this decomposition?
Changing
fare_indexalone would be a tautology ifrail_usagewere derived fromrail_subsidy_spendingper-year:By having policyengine-uk-data provide
rail_usageas an input (derived once at survey year), reforms can modifyfare_indexand see actual effects onrail_subsidy_spending.Test plan
Fixes #1405
🤖 Generated with Claude Code