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
11 changes: 8 additions & 3 deletions policyengine_uk_data/datasets/disability_benefits.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down Expand Up @@ -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


Expand Down
48 changes: 38 additions & 10 deletions policyengine_uk_data/tests/test_disability_benefits.py
Original file line number Diff line number Diff line change
Expand Up @@ -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),
],
}
)
Expand All @@ -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
Expand Down