We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
There was an error while loading. Please reload this page.
2 parents 21ecae6 + 1b0a4fd commit a24b284Copy full SHA for a24b284
5 files changed
changelog_entry.yaml
@@ -0,0 +1,4 @@
1
+- bump: minor
2
+ changes:
3
+ added:
4
+ - Add pension_contributions_via_salary_sacrifice variable from FRS SPNAMT field
policyengine_uk_data/datasets/frs.py
@@ -631,6 +631,20 @@ def determine_education_level(fted_val, typeed2_val, age_val):
631
pe_person["employer_pension_contributions"] = (
632
pe_person["employee_pension_contributions"] * 3
633
) # Rough estimate based on aggregates.
634
+ # Salary sacrifice pension contributions from FRS Job table (SPNAMT field)
635
+ # SPNAMT represents employer pension contributions made via salary sacrifice
636
+ # arrangements where employees forego salary in exchange for increased pension
637
+ # contributions. This is separate from regular employee pension contributions
638
+ # (deduc1) and provides tax advantages for both employer and employee.
639
+ # Uses same pattern as employee_pension_contributions (deduc1) without outlier
640
+ # clipping, as job-level data is generally cleaner than pension provider data.
641
+ # Source: https://datacatalogue.ukdataservice.ac.uk/datasets/dataset/630d4a8d-ba6a-82b3-f33d-c713c66efcb3
642
+ # Note: Values are annualized from weekly amounts reported in the survey.
643
+ pe_person["pension_contributions_via_salary_sacrifice"] = np.maximum(
644
+ 0,
645
+ sum_to_entity(job.spnamt.fillna(0), job.person_id, person.person_id)
646
+ * WEEKS_IN_YEAR,
647
+ )
648
649
pe_household["housing_service_charges"] = (
650
pd.DataFrame(
policyengine_uk_data/storage/uprating_factors.csv
@@ -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
21
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
22
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
23
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
24
+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
25
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
26
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
27
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
policyengine_uk_data/storage/uprating_growth_factors.csv
@@ -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
policyengine_uk_data/tests/test_pension_contributions_via_salary_sacrifice.py
@@ -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
5
6
+
7
+ # Basic validation: all values should be non-negative
8
+ assert (
9
+ values >= 0
10
+ ).all(), "Salary sacrifice pension contributions must be non-negative"
11
12
+ # Should have some non-zero values (not everyone uses salary sacrifice, but some do)
13
+ total = values.sum()
14
15
+ total > 0
16
+ ), f"Expected some salary sacrifice contributions, got {total}"
17
18
+ # Reasonableness check: total should be less than total employment income
19
+ # This is a very loose check just to catch major issues
20
+ employment_income = baseline.calculate("employment_income", period=2025)
+ total_employment = employment_income.sum()
+ total < total_employment
+ ), f"Salary sacrifice contributions ({total/1e9:.1f}B) cannot exceed total employment income ({total_employment/1e9:.1f}B)"
0 commit comments