Skip to content

Commit 6986821

Browse files
Improve uprating growthfactors (#1218)
* Remove duplicate docs deployment * Just run mac * Uprating improvements * Add catch * Enable entity table downloads * Re-add VAT to gov tax?
1 parent 8722207 commit 6986821

28 files changed

Lines changed: 570 additions & 394 deletions

changelog_entry.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
- bump: minor
2+
changes:
3+
added:
4+
- Documentation on growth factors.
5+
- Cleaned up non-standard uprating factors for wealth variables.
6+
- Added triple lock uprating detail and reform switches.
7+
- Added ability to download entity datasets from HuggingFace.

docs/book/assumptions/growthfactors.ipynb

Lines changed: 0 additions & 181 deletions
This file was deleted.
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Economic assumptions
2+
3+
We project economic variables using year-over-year growth rates stored in `parameters/gov/economic_assumptions/yoy_growth.yaml`. We generate index values from these rates to update household variables. We source all values from the OBR's Economic and Fiscal Outlook (March 2025) unless we specify otherwise.
4+
5+
## Consumer price index
6+
7+
We use the OBR's CPI projections to drive most benefit and consumption variables.
8+
9+
| Fiscal year | 2022 | 2023 | 2024 | 2025 | 2026 | 2027 | 2028 | 2029 |
10+
|-------------|------|------|------|------|------|------|------|------|
11+
| Growth rate | 10.0% | 5.7% | 2.3% | 3.2% | 1.9% | 2.0% | 2.0% | 2.0% |
12+
13+
We apply CPI to these variables:
14+
- All reported benefits– `attendance_allowance_reported`, `carers_allowance_reported`, `child_benefit_reported`, `child_tax_credit_reported`, `dla_m_reported`, `dla_sc_reported`, `esa_contrib_reported`, `esa_income_reported`, `housing_benefit_reported`, `iidb_reported`, `incapacity_benefit_reported`, `income_support_reported`, `jsa_contrib_reported`, `jsa_income_reported`, `maternity_allowance_reported`, `pension_credit_reported`, `pip_dl_reported`, `pip_m_reported`, `sda_reported`, `state_pension_reported`, `universal_credit_reported`, `winter_fuel_allowance_reported`, `working_tax_credit_reported`
15+
- All consumption categories– `alcohol_and_tobacco_consumption`, `clothing_and_footwear_consumption`, `communication_consumption`, `domestic_energy_consumption`, `education_consumption`, `food_and_non_alcoholic_beverages_consumption`, `health_consumption`, `household_furnishings_consumption`, `housing_water_and_electricity_consumption`, `miscellaneous_consumption`, `recreation_consumption`, `restaurants_and_hotels_consumption`, `transport_consumption`
16+
- Other variables– `afcs_reported`, `bsp_reported`, `childcare_expenses`, `diesel_spending`, `free_school_fruit_veg`, `free_school_meals`, `free_school_milk`, `maintenance_expenses`, `petrol_spending`, `statutory_maternity_pay`, `statutory_paternity_pay`, `statutory_sick_pay`, `state_pension`
17+
18+
## Average earnings
19+
20+
We apply the OBR's wage growth forecasts to employment-related variables.
21+
22+
| Fiscal year | 2022 | 2023 | 2024 | 2025 | 2026 | 2027 | 2028 | 2029 |
23+
|-------------|------|------|------|------|------|------|------|------|
24+
| Growth rate | 6.4% | 6.9% | 4.7% | 3.7% | 2.2% | 2.1% | 2.3% | 2.5% |
25+
26+
We use average earnings for these variables: `employee_pension_contributions`, `employer_pension_contributions`, `employment_income`, `employment_income_before_lsr`, `personal_pension_contributions`, `student_loan_repayments`
27+
28+
## Per capita GDP
29+
30+
We derive these rates from OBR GDP growth and ONS population projections.
31+
32+
| Fiscal year | 2022 | 2023 | 2024 | 2025 | 2026 | 2027 | 2028 | 2029 |
33+
|-------------|------|------|------|------|------|------|------|------|
34+
| Growth rate | 9.2% | 5.0% | 3.8% | 2.8% | 2.8% | 3.1% | 3.3% | 3.3% |
35+
36+
We use per capita GDP for these variables: `capital_gains`, `capital_gains_before_response`, `corporate_wealth`, `dividend_income`, `gross_financial_wealth`, `lump_sum_income`, `main_residence_value`, `maintenance_income`, `miscellaneous_income`, `mortgage_capital_repayment`, `net_financial_wealth`, `non_residential_property_value`, `other_investment_income`, `other_residential_property_value`, `owned_land`, `pension_income`, `private_transfer_income`, `property_income`, `savings`, `savings_interest_income`, `sublet_income`
37+
38+
## Council tax
39+
40+
We use the OBR's Council Tax receipts and projections.
41+
42+
| Fiscal year | 2022 | 2023 | 2024 | 2025 | 2026 | 2027 | 2028 | 2029 |
43+
|-------------|------|------|------|------|------|------|------|------|
44+
| Growth rate | 5.3% | 5.6% | 6.4% | 4.6% | 4.5% | 4.6% | 4.5% | 4.5% |
45+
46+
We apply this to: `council_tax`
47+
48+
## Mortgage interest
49+
50+
We use the OBR's mortgage interest rate index growth rates.
51+
52+
| Fiscal year | 2022 | 2023 | 2024 | 2025 | 2026 | 2027 | 2028 | 2029 |
53+
|-------------|------|------|------|------|------|------|------|------|
54+
| Growth rate | 26.2% | 48.5% | 22.1% | 13.6% | 12.6% | 8.2% | 4.2% | 4.7% |
55+
56+
We apply this to: `mortgage_interest_repayment`
57+
58+
## Private pension index
59+
60+
We use RPI year-on-year change from the previous year, capped at 5%.
61+
62+
| Fiscal year | 2022 | 2023 | 2024 | 2025 | 2026 | 2027 | 2028 | 2029 |
63+
|-------------|------|------|------|------|------|------|------|------|
64+
| Growth rate | 5.0% | 5.0% | 5.0% | 4.7% | 3.7% | 2.2% | 2.1% | 2.3% |
65+
66+
We apply this to: `private_pension_income`
67+
68+
## Rent
69+
70+
We use the OBR's rental growth projections.
71+
72+
| Fiscal year | 2022 | 2023 | 2024 | 2025 | 2026 | 2027 | 2028 | 2029 |
73+
|-------------|------|------|------|------|------|------|------|------|
74+
| Growth rate | 4.0% | 6.3% | 7.4% | 5.7% | 3.6% | 2.7% | 2.3% | 2.4% |
75+
76+
We apply this to: `rent`
77+
78+
## Per capita mixed income
79+
80+
We derive these rates from OBR mixed income growth and ONS population projections.
81+
82+
| Fiscal year | 2022 | 2023 | 2024 | 2025 | 2026 | 2027 | 2028 | 2029 |
83+
|-------------|------|------|------|------|------|------|------|------|
84+
| Growth rate | 6.3% | 2.4% | 4.8% | 4.7% | 3.1% | 3.1% | 3.6% | 3.8% |
85+
86+
We apply this to: `self_employment_income`
87+
88+
## Population
89+
90+
We use ONS population projections.
91+
92+
| Fiscal year | 2022 | 2023 | 2024 | 2025 | 2026 | 2027 | 2028 | 2029 |
93+
|-------------|------|------|------|------|------|------|------|------|
94+
| Growth rate | 0.3% | 1.4% | 1.0% | 1.1% | 0.7% | 0.8% | 0.4% | 0.5% |
95+
96+
We apply this to: `household_weight`

policyengine_uk/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,9 @@
1313
from pathlib import Path
1414
import os
1515
from policyengine_core.taxbenefitsystems import TaxBenefitSystem
16+
from policyengine_uk.data.economic_assumptions import (
17+
BASELINE_GROWFACTORS,
18+
apply_growth_factors,
19+
)
1620

1721
REPO = Path(__file__).parent

policyengine_uk/data/__init__.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1 @@
11
from policyengine_uk.data.dataset_schema import UKDataset
2-
from policyengine_uk.data.economic_assumptions import (
3-
BASELINE_GROWFACTORS,
4-
apply_growth_factors,
5-
)

policyengine_uk/data/dataset_schema.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,39 @@
11
import pandas as pd
2-
from policyengine_uk import Microsimulation
2+
from typing import TYPE_CHECKING
3+
4+
if TYPE_CHECKING:
5+
from policyengine_uk import Microsimulation
6+
7+
from pathlib import Path
8+
import h5py
39

410

511
class UKDataset:
612
person: pd.DataFrame
713
benunit: pd.DataFrame
814
household: pd.DataFrame
915

16+
@staticmethod
17+
def validate_file_path(file_path: str):
18+
if not file_path.endswith(".h5"):
19+
raise ValueError("File path must end with '.h5' for UKDataset.")
20+
if not Path(file_path).exists():
21+
raise FileNotFoundError(f"File not found: {file_path}")
22+
23+
# Check if the file contains time_period, person, benunit, and household datasets
24+
with h5py.File(file_path, "r") as f:
25+
required_datasets = [
26+
"time_period",
27+
"person",
28+
"benunit",
29+
"household",
30+
]
31+
for dataset in required_datasets:
32+
if dataset not in f:
33+
raise ValueError(
34+
f"Dataset '{dataset}' not found in the file: {file_path}"
35+
)
36+
1037
def __init__(
1138
self,
1239
file_path: str = None,
@@ -16,6 +43,7 @@ def __init__(
1643
fiscal_year: int = 2025,
1744
):
1845
if file_path is not None:
46+
self.validate_file_path(file_path)
1947
with pd.HDFStore(file_path) as f:
2048
self.person = f["person"]
2149
self.benunit = f["benunit"]
@@ -59,7 +87,9 @@ def copy(self):
5987
)
6088

6189
@staticmethod
62-
def from_simulation(simulation: Microsimulation, fiscal_year: int = 2025):
90+
def from_simulation(
91+
simulation: "Microsimulation", fiscal_year: int = 2025
92+
):
6393
entity_dfs = {}
6494

6595
for entity in ["person", "benunit", "household"]:

policyengine_uk/data/economic_assumptions.py

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from pathlib import Path
33
from policyengine_uk.data.dataset_schema import UKDataset
44

5-
START_YEAR = 2020
5+
START_YEAR = 2022
66
END_YEAR = 2029
77

88

@@ -13,40 +13,29 @@ def create_policyengine_uprating_factors_table(print_diff=True):
1313

1414
variable_names = []
1515
years = []
16-
index_values = []
16+
yoy_values = []
1717

1818
parameter_by_variable = {}
1919

2020
for variable in system.variables.values():
2121
if variable.uprating is not None:
22-
parameter = system.parameters.get_child(variable.uprating)
22+
parameter = system.parameters.get_child(
23+
variable.uprating.replace("indices", "yoy_growth")
24+
)
2325
parameter_by_variable[variable.name] = parameter.name
24-
start_value = parameter(START_YEAR)
2526
for year in range(START_YEAR, END_YEAR + 1):
2627
variable_names.append(variable.name)
27-
years.append(str(year)) # Convert to string here
28-
growth = parameter(year) / start_value
29-
index_values.append(round(growth, 3))
28+
years.append(str(year))
29+
yoy_values.append(round(parameter(year), 3))
3030

3131
df["Variable"] = variable_names
3232
df["Year"] = years
33-
df["Value"] = index_values
33+
df["Value"] = yoy_values
3434

3535
# Convert to there is a column for each year
3636
df = df.pivot(index="Variable", columns="Year", values="Value")
3737
df = df.sort_values("Variable")
3838

39-
# Create a table with growth factors by year
40-
41-
df_growth = df.copy()
42-
for year in range(END_YEAR, START_YEAR, -1):
43-
year_str = str(year)
44-
prev_year_str = str(year - 1)
45-
df_growth[year_str] = round(
46-
df_growth[year_str] / df_growth[prev_year_str] - 1, 3
47-
)
48-
df_growth[str(START_YEAR)] = 0
49-
5039
file_path = Path(__file__).parent / "uprating_growth_factors.csv"
5140

5241
# Read old CSV if it exists
@@ -57,16 +46,16 @@ def create_policyengine_uprating_factors_table(print_diff=True):
5746
old_df.columns = old_df.columns.astype(str)
5847

5948
# Prepare new dataframe
60-
df_growth["Parameter"] = df.index.map(parameter_by_variable)
61-
df_growth = df_growth[
49+
df["Parameter"] = df.index.map(parameter_by_variable)
50+
df = df[
6251
["Parameter"] + [str(year) for year in range(START_YEAR, END_YEAR + 1)]
6352
]
6453

6554
# Print diff if old CSV existed and print_diff is True
6655
if old_df is not None and print_diff:
67-
print_csv_diff(old_df, df_growth)
56+
print_csv_diff(old_df, df)
6857
# Save new CSV
69-
df_growth.to_csv(file_path)
58+
df.to_csv(file_path)
7059

7160
return pd.read_csv(file_path)
7261

0 commit comments

Comments
 (0)