Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions changelog_entry.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- bump: minor
changes:
added:
- Add pension_contributions_via_salary_sacrifice variable from FRS SPNAMT field
14 changes: 14 additions & 0 deletions policyengine_uk_data/datasets/frs.py
Original file line number Diff line number Diff line change
Expand Up @@ -631,6 +631,20 @@ def determine_education_level(fted_val, typeed2_val, age_val):
pe_person["employer_pension_contributions"] = (
pe_person["employee_pension_contributions"] * 3
) # Rough estimate based on aggregates.
# Salary sacrifice pension contributions from FRS Job table (SPNAMT field)
# SPNAMT represents employer pension contributions made via salary sacrifice
# arrangements where employees forego salary in exchange for increased pension
# contributions. This is separate from regular employee pension contributions
# (deduc1) and provides tax advantages for both employer and employee.
# Uses same pattern as employee_pension_contributions (deduc1) without outlier
# clipping, as job-level data is generally cleaner than pension provider data.
# Source: https://datacatalogue.ukdataservice.ac.uk/datasets/dataset/630d4a8d-ba6a-82b3-f33d-c713c66efcb3
# Note: Values are annualized from weekly amounts reported in the survey.
pe_person["pension_contributions_via_salary_sacrifice"] = np.maximum(
0,
sum_to_entity(job.spnamt.fillna(0), job.person_id, person.person_id)
* WEEKS_IN_YEAR,
)

pe_household["housing_service_charges"] = (
pd.DataFrame(
Expand Down
1 change: 1 addition & 0 deletions policyengine_uk_data/storage/uprating_factors.csv
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ domestic_energy_consumption,1.0,1.04,1.144,1.209,1.237,1.277,1.301,1.327,1.353,1
education_consumption,1.0,1.04,1.144,1.209,1.237,1.277,1.301,1.327,1.353,1.38,1.38,1.38,1.38,1.38,1.38
employee_pension_contributions,1.0,1.059,1.127,1.205,1.261,1.308,1.337,1.365,1.396,1.431,1.431,1.431,1.431,1.431,1.431
employer_pension_contributions,1.0,1.059,1.127,1.205,1.261,1.308,1.337,1.365,1.396,1.431,1.431,1.431,1.431,1.431,1.431
pension_contributions_via_salary_sacrifice,1.0,1.059,1.127,1.205,1.261,1.308,1.337,1.365,1.396,1.431,1.431,1.431,1.431,1.431,1.431
employment_income,1.0,1.059,1.127,1.205,1.261,1.308,1.337,1.365,1.396,1.431,1.431,1.431,1.431,1.431,1.431
employment_income_before_lsr,1.0,1.059,1.127,1.205,1.261,1.308,1.337,1.365,1.396,1.431,1.431,1.431,1.431,1.431,1.431
esa_contrib_reported,1.0,1.04,1.144,1.209,1.237,1.277,1.301,1.327,1.353,1.38,1.38,1.38,1.38,1.38,1.38
Expand Down
1 change: 1 addition & 0 deletions policyengine_uk_data/storage/uprating_growth_factors.csv
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ domestic_energy_consumption,0,0.04,0.1,0.057,0.023,0.032,0.019,0.02,0.02,0.02,0.
education_consumption,0,0.04,0.1,0.057,0.023,0.032,0.019,0.02,0.02,0.02,0.0,0.0,0.0,0.0,0.0
employee_pension_contributions,0,0.059,0.064,0.069,0.046,0.037,0.022,0.021,0.023,0.025,0.0,0.0,0.0,0.0,0.0
employer_pension_contributions,0,0.059,0.064,0.069,0.046,0.037,0.022,0.021,0.023,0.025,0.0,0.0,0.0,0.0,0.0
pension_contributions_via_salary_sacrifice,0,0.059,0.064,0.069,0.046,0.037,0.022,0.021,0.023,0.025,0.0,0.0,0.0,0.0,0.0
employment_income,0,0.059,0.064,0.069,0.046,0.037,0.022,0.021,0.023,0.025,0.0,0.0,0.0,0.0,0.0
employment_income_before_lsr,0,0.059,0.064,0.069,0.046,0.037,0.022,0.021,0.023,0.025,0.0,0.0,0.0,0.0,0.0
esa_contrib_reported,0,0.04,0.1,0.057,0.023,0.032,0.019,0.02,0.02,0.02,0.0,0.0,0.0,0.0,0.0
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
def test_pension_contributions_via_salary_sacrifice(baseline):
"""Test that pension_contributions_via_salary_sacrifice loads and has reasonable values."""
values = baseline.calculate(
"pension_contributions_via_salary_sacrifice", period=2025
)

# Basic validation: all values should be non-negative
assert (
values >= 0
).all(), "Salary sacrifice pension contributions must be non-negative"

# Should have some non-zero values (not everyone uses salary sacrifice, but some do)
total = values.sum()
assert (
total > 0
), f"Expected some salary sacrifice contributions, got {total}"

# Reasonableness check: total should be less than total employment income
# This is a very loose check just to catch major issues
employment_income = baseline.calculate("employment_income", period=2025)
total_employment = employment_income.sum()
assert (
total < total_employment
), f"Salary sacrifice contributions ({total/1e9:.1f}B) cannot exceed total employment income ({total_employment/1e9:.1f}B)"