diff --git a/policyengine_uk_data/datasets/disability_benefits.py b/policyengine_uk_data/datasets/disability_benefits.py index db6e9c93..00af2619 100644 --- a/policyengine_uk_data/datasets/disability_benefits.py +++ b/policyengine_uk_data/datasets/disability_benefits.py @@ -47,7 +47,7 @@ "esa_income_reported", ) -SAFETY_MARGIN = 0.1 +CATEGORY_THRESHOLD_WEEKLY_TOLERANCE = 1.0 SURVEY_REPORTED_AMOUNT_WEEKS_IN_YEAR = 365.25 / 7 @@ -88,9 +88,14 @@ def _category_from_reported_amount( weekly_amount = weekly_amount.to_numpy(dtype=float) / MODEL_WEEKS_IN_YEAR category = np.full(len(weekly_amount), "NONE", dtype=object) for category_name, weekly_rate in thresholds: - category[weekly_amount >= float(weekly_rate) * (1 - SAFETY_MARGIN)] = ( - category_name + # FRS benefit amounts are weekly survey responses annualised upstream; + # allow GBP 1/week of rounding noise without discounting rates by a + # percentage that can promote people into higher award categories. + threshold = max( + 0.0, + float(weekly_rate) - CATEGORY_THRESHOLD_WEEKLY_TOLERANCE, ) + category[weekly_amount >= threshold] = category_name return category diff --git a/policyengine_uk_data/tests/test_disability_benefits.py b/policyengine_uk_data/tests/test_disability_benefits.py index f355aff2..1c21950c 100644 --- a/policyengine_uk_data/tests/test_disability_benefits.py +++ b/policyengine_uk_data/tests/test_disability_benefits.py @@ -16,32 +16,36 @@ def test_reported_amounts_map_to_disability_categories(): year = 2025 dwp = CountryTaxBenefitSystem().parameters(year).baseline.gov.dwp + + def annual_near_threshold(weekly_rate): + return (weekly_rate - 0.5) * WEEKS_IN_YEAR + person = pd.DataFrame( { "attendance_allowance_reported": [ 0, - dwp.attendance_allowance.lower * WEEKS_IN_YEAR * 0.91, - dwp.attendance_allowance.higher * WEEKS_IN_YEAR * 0.91, + annual_near_threshold(dwp.attendance_allowance.lower), + annual_near_threshold(dwp.attendance_allowance.higher), ], "dla_sc_reported": [ 0, - dwp.dla.self_care.lower * WEEKS_IN_YEAR * 0.91, - dwp.dla.self_care.middle * WEEKS_IN_YEAR * 0.91, + annual_near_threshold(dwp.dla.self_care.lower), + annual_near_threshold(dwp.dla.self_care.middle), ], "dla_m_reported": [ 0, - dwp.dla.mobility.lower * WEEKS_IN_YEAR * 0.91, - dwp.dla.mobility.higher * WEEKS_IN_YEAR * 0.91, + annual_near_threshold(dwp.dla.mobility.lower), + annual_near_threshold(dwp.dla.mobility.higher), ], "pip_m_reported": [ 0, - dwp.pip.mobility.standard * WEEKS_IN_YEAR * 0.91, - dwp.pip.mobility.enhanced * WEEKS_IN_YEAR * 0.91, + annual_near_threshold(dwp.pip.mobility.standard), + annual_near_threshold(dwp.pip.mobility.enhanced), ], "pip_dl_reported": [ 0, - dwp.pip.daily_living.standard * WEEKS_IN_YEAR * 0.91, - dwp.pip.daily_living.enhanced * WEEKS_IN_YEAR * 0.91, + annual_near_threshold(dwp.pip.daily_living.standard), + annual_near_threshold(dwp.pip.daily_living.enhanced), ], } ) @@ -55,6 +59,30 @@ def test_reported_amounts_map_to_disability_categories(): assert result["pip_dl_category"].tolist() == ["NONE", "STANDARD", "ENHANCED"] +def test_reported_amounts_do_not_use_percentage_category_margin(): + year = 2025 + dwp = CountryTaxBenefitSystem().parameters(year).baseline.gov.dwp + person = pd.DataFrame( + { + "attendance_allowance_reported": [ + dwp.attendance_allowance.higher * WEEKS_IN_YEAR * 0.91, + ], + "pip_dl_reported": [ + dwp.pip.daily_living.standard * WEEKS_IN_YEAR * 0.91, + ], + "pip_m_reported": [ + dwp.pip.mobility.enhanced * WEEKS_IN_YEAR * 0.91, + ], + } + ) + + result = add_disability_benefit_categories_from_reported_amounts(person, year) + + assert result["aa_category"].tolist() == ["LOWER"] + assert result["pip_dl_category"].tolist() == ["NONE"] + assert result["pip_m_category"].tolist() == ["STANDARD"] + + def test_reported_amounts_recompute_disability_flags(): year = 2025 dwp = CountryTaxBenefitSystem().parameters(year).gov.dwp