Skip to content

Commit

Permalink
Merge pull request #18 from masterismail/deepPovertyImpact
Browse files Browse the repository at this point in the history
added Deep poverty impact
  • Loading branch information
nikhilwoodruff authored Jul 9, 2024
2 parents fbe6efd + 4f8890e commit 1e764c7
Show file tree
Hide file tree
Showing 10 changed files with 327 additions and 0 deletions.
19 changes: 19 additions & 0 deletions policyengine/economic_impact/economic_impact.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@
FemalePoverty as RegularFemalePoverty,
AllPoverty as RegularGenderAllPoverty
)
from .poverty_impact.deep_poverty.by_age.by_age import (
ChildPoverty as DeepChildPoverty,
AdultPoverty as DeepAdultPoverty,
SeniorPoverty as DeepSeniorPoverty,
AllPoverty as DeepAgeAllPoverty
)
from .poverty_impact.deep_poverty.by_gender.by_gender import (
MalePoverty as DeepMalePoverty,
FemalePoverty as DeepFemalePoverty,
AllPoverty as DeepGenderAllPoverty
)
from typing import Dict

class EconomicImpact:
Expand Down Expand Up @@ -54,6 +65,14 @@ def __init__(self, reform: dict, country: str) -> None:
"poverty/regular/male": RegularMalePoverty(self.baseline, self.reformed),
"poverty/regular/female": RegularFemalePoverty(self.baseline, self.reformed),
"poverty/regular/gender/all": RegularGenderAllPoverty(self.baseline, self.reformed),
"poverty/deep/child": DeepChildPoverty(self.baseline, self.reformed),
"poverty/deep/adult": DeepAdultPoverty(self.baseline, self.reformed),
"poverty/deep/senior": DeepSeniorPoverty(self.baseline, self.reformed),
"poverty/deep/age/all": DeepAgeAllPoverty(self.baseline, self.reformed),
"poverty/deep/male": DeepMalePoverty(self.baseline, self.reformed),
"poverty/deep/female": DeepFemalePoverty(self.baseline, self.reformed),
"poverty/deep/gender/all": DeepGenderAllPoverty(self.baseline, self.reformed),

}

def _get_simulation_class(self) -> type:
Expand Down
Empty file.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
from policyengine.economic_impact.base_metric_calculator import BaseMetricCalculator
from policyengine_uk import Microsimulation

class ChildPoverty(BaseMetricCalculator):
def __init__(self, baseline: Microsimulation, reformed: Microsimulation, default_period: int = 2024) -> None:
super().__init__(baseline, reformed, default_period)
self.baseline = baseline
self.reformed = reformed

def calculate(self):
age = self.baseline.calculate("age")

baseline_poverty = self.baseline.calculate("in_deep_poverty", map_to="person")
reform_poverty = self.reformed.calculate("in_deep_poverty", map_to="person")

baseline = float(baseline_poverty[age < 18].mean())
reform = float(reform_poverty[age < 18].mean())
change = ((reform - baseline) / baseline) * 100

return {
"baseline": round(baseline*100,2),
"reform": round(reform*100,2),
"change": round(change,1)
}

class AdultPoverty(BaseMetricCalculator):
def __init__(self, baseline: Microsimulation, reformed: Microsimulation, default_period: int = 2024) -> None:
super().__init__(baseline, reformed, default_period)
self.baseline = baseline
self.reformed = reformed

def calculate(self):
age = self.baseline.calculate("age")

baseline_poverty = self.baseline.calculate("in_deep_poverty", map_to="person")
reform_poverty = self.reformed.calculate("in_deep_poverty", map_to="person")

baseline = float(baseline_poverty[(age >= 18) & (age < 65)].mean())
reform = float(reform_poverty[(age >= 18) & (age < 65)].mean())
change = ((reform - baseline) / baseline) * 100

return {
"baseline": round(baseline*100,2),
"reform": round(reform*100,2),
"change": round(change,1)
}

class SeniorPoverty(BaseMetricCalculator):
def __init__(self, baseline: Microsimulation, reformed: Microsimulation, default_period: int = 2024) -> None:
super().__init__(baseline, reformed, default_period)
self.baseline = baseline
self.reformed = reformed

def calculate(self):
age = self.baseline.calculate("age")

baseline_poverty = self.baseline.calculate("in_deep_poverty", map_to="person")
reform_poverty = self.reformed.calculate("in_deep_poverty", map_to="person")

baseline = float(baseline_poverty[age >= 65].mean())
reform = float(reform_poverty[age >= 65].mean())
change = ((reform - baseline) / baseline) * 100

return {
"baseline": round(baseline*100,2),
"reform": round(reform*100,2),
"change": round(change,1)
}

class AllPoverty(BaseMetricCalculator):
def __init__(self, baseline: Microsimulation, reformed: Microsimulation, default_period: int = 2024) -> None:
super().__init__(baseline, reformed, default_period)
self.baseline = baseline
self.reformed = reformed

def calculate(self):

baseline_poverty = self.baseline.calculate("in_deep_poverty", map_to="person")
reform_poverty = self.reformed.calculate("in_deep_poverty", map_to="person")

baseline = float(baseline_poverty.mean())
reform = float(reform_poverty.mean())
change = ((reform - baseline) / baseline) * 100

return {
"baseline": round(baseline*100,2),
"reform": round(reform*100,2),
"change": round(change,1)
}
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from policyengine.economic_impact.inequality_impact.inequality_impact import BaseMetricCalculator
from policyengine_uk import Microsimulation

class MalePoverty(BaseMetricCalculator):
def __init__(self, baseline: Microsimulation, reformed: Microsimulation, default_period: int = 2024) -> None:
super().__init__(baseline, reformed, default_period)
self.baseline = baseline
self.reformed = reformed

def calculate(self):
is_male = self.baseline.calculate("is_male")

baseline_poverty = self.baseline.calculate("in_deep_poverty", map_to="person")
reform_poverty = self.reformed.calculate("in_deep_poverty", map_to="person")

baseline = float(baseline_poverty[is_male].mean())
reform = float(reform_poverty[is_male].mean())
change = ((reform - baseline) / baseline) * 100

return {
"baseline": round(baseline*100,2),
"reform": round(reform*100,2),
"change": round(change,1)
}

class FemalePoverty(BaseMetricCalculator):
def __init__(self, baseline: Microsimulation, reformed: Microsimulation, default_period: int = 2024) -> None:
super().__init__(baseline, reformed, default_period)
self.baseline = baseline
self.reformed = reformed

def calculate(self):
is_male = self.baseline.calculate("is_male")

baseline_poverty = self.baseline.calculate("in_deep_poverty", map_to="person")
reform_poverty = self.reformed.calculate("in_deep_poverty", map_to="person")

baseline = float(baseline_poverty[~is_male].mean())
reform = float(reform_poverty[~is_male].mean())
change = ((reform - baseline) / baseline) * 100

return {
"baseline": round(baseline*100,2),
"reform": round(reform*100,2),
"change": round(change,1)
}

class AllPoverty(BaseMetricCalculator):
def __init__(self, baseline: Microsimulation, reformed: Microsimulation, default_period: int = 2024) -> None:
super().__init__(baseline, reformed, default_period)
self.baseline = baseline
self.reformed = reformed

def calculate(self):

baseline_poverty = self.baseline.calculate("in_deep_poverty", map_to="person")
reform_poverty = self.reformed.calculate("in_deep_poverty", map_to="person")

baseline = float(baseline_poverty.mean())
reform = float(reform_poverty.mean())
change = ((reform - baseline) / baseline) * 100

return {
"baseline": round(baseline*100,2),
"reform": round(reform*100,2),
"change": round(change,1)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Regular poverty by age
- test_child_poverty:
reform:
gov.hmrc.income_tax.rates.uk[0].rate:
"2024-01-01.2100-12-31": 0.55
country: uk
expected:
baseline: 2.44
reform: 2.45
change: 0.7

- test_adult_poverty:
reform:
gov.hmrc.income_tax.rates.uk[0].rate:
"2024-01-01.2100-12-31": 0.55
country: uk
expected:
baseline: 2.6
reform: 2.7
change: 3.9

- test_senior_poverty:
reform:
gov.hmrc.income_tax.rates.uk[0].rate:
"2024-01-01.2100-12-31": 0.55
country: uk
expected:
baseline: 1.76
reform: 1.76
change: 0.5

- test_all_poverty:
reform:
gov.hmrc.income_tax.rates.uk[0].rate:
"2024-01-01.2100-12-31": 0.55
country: uk
expected:
baseline: 2.41
reform: 2.47
change: 2.7
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import pytest
import yaml
import os
from policyengine import EconomicImpact

def assert_dict_approx_equal(actual, expected, tolerance=1e-4):
for key in expected:
assert abs(actual[key] - expected[key]) < tolerance, f"Key {key}: expected {expected[key]}, got {actual[key]}"


yaml_file_path = "policyengine/tests/economic_impact/poverty_impact/deep_poverty/by_age/by_age.yaml"

# Check if the file exists
if not os.path.exists(yaml_file_path):
raise FileNotFoundError(f"The YAML file does not exist at: {yaml_file_path}")

with open(yaml_file_path, 'r') as file:
test_cases = yaml.safe_load(file)

@pytest.mark.parametrize("test_case", test_cases)
def test_economic_impact(test_case):
test_name = list(test_case.keys())[0]
test_data = test_case[test_name]

economic_impact = EconomicImpact(test_data['reform'], test_data['country'])

if 'child' in test_name:
result = economic_impact.calculate("poverty/deep/child")
elif 'adult' in test_name:
result = economic_impact.calculate("poverty/deep/adult")
elif 'senior' in test_name:
result = economic_impact.calculate("poverty/deep/senior")
elif 'all' in test_name:
result = economic_impact.calculate("poverty/deep/age/all")
else:
pytest.fail(f"Unknown test case: {test_name}")

assert_dict_approx_equal(result, test_data['expected'])

if __name__ == "__main__":
pytest.main([__file__])
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Regular poverty by age
- male_poverty_test:
reform:
gov.hmrc.income_tax.rates.uk[0].rate:
"2024-01-01.2100-12-31": 0.55
country: uk
expected:
baseline: 2.66
reform: 2.73
change: 2.5

- female_poverty_test:
reform:
gov.hmrc.income_tax.rates.uk[0].rate:
"2024-01-01.2100-12-31": 0.55
country: uk
expected:
baseline: 2.16
reform: 2.23
change: 2.9

- all_poverty_test:
reform:
gov.hmrc.income_tax.rates.uk[0].rate:
"2024-01-01.2100-12-31": 0.55
country: uk
expected:
baseline: 2.41
reform: 2.47
change: 2.7
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import pytest
import yaml
import os
from policyengine import EconomicImpact

def assert_dict_approx_equal(actual, expected, tolerance=1e-4):
for key in expected:
assert abs(actual[key] - expected[key]) < tolerance, f"Key {key}: expected {expected[key]}, got {actual[key]}"


yaml_file_path = "policyengine/tests/economic_impact/poverty_impact/deep_poverty/by_age/by_age.yaml"

# Check if the file exists
if not os.path.exists(yaml_file_path):
raise FileNotFoundError(f"The YAML file does not exist at: {yaml_file_path}")

with open(yaml_file_path, 'r') as file:
test_cases = yaml.safe_load(file)

@pytest.mark.parametrize("test_case", test_cases)
def test_economic_impact(test_case):
test_name = list(test_case.keys())[0]
test_data = test_case[test_name]

economic_impact = EconomicImpact(test_data['reform'], test_data['country'])

if 'child' in test_name:
result = economic_impact.calculate("poverty/deep/child")
elif 'adult' in test_name:
result = economic_impact.calculate("poverty/deep/adult")
elif 'senior' in test_name:
result = economic_impact.calculate("poverty/deep/senior")
elif 'all' in test_name:
result = economic_impact.calculate("poverty/deep/age/all")
else:
pytest.fail(f"Unknown test case: {test_name}")

assert_dict_approx_equal(result, test_data['expected'])

if __name__ == "__main__":
pytest.main([__file__])

0 comments on commit 1e764c7

Please sign in to comment.