Skip to content

Commit

Permalink
Merge pull request #19 from masterismail/BudgetaryImpactOverall
Browse files Browse the repository at this point in the history
[1] - added the metrics of Budgetary impact by program.
  • Loading branch information
nikhilwoodruff authored Jul 16, 2024
2 parents 3e7b672 + 6d13d76 commit 791a9ab
Show file tree
Hide file tree
Showing 6 changed files with 369 additions and 0 deletions.
Empty file.
192 changes: 192 additions & 0 deletions policyengine/economic_impact/budgetary_impact/by_program/by_program.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
from policyengine.economic_impact.base_metric_calculator import BaseMetricCalculator
from policyengine_uk import Microsimulation

class IncomeTax(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 = self.baseline.calculate("income_tax", map_to="household").sum()
reform = self.reformed.calculate("income_tax", map_to="household").sum()

change = ((reform - baseline) / baseline) * 100

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

class NationalInsurance(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 = self.baseline.calculate("national_insurance", map_to="household").sum()
reform = self.reformed.calculate("national_insurance", map_to="household").sum()

change = ((reform - baseline) / baseline) * 100

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

class Vat(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 = self.baseline.calculate("vat", map_to="household").sum()
reform = self.reformed.calculate("vat", map_to="household").sum()

change = ((reform - baseline) / baseline) * 100

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

class CouncilTax(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 = self.baseline.calculate("council_tax", map_to="household").sum()
reform = self.reformed.calculate("council_tax", map_to="household").sum()

change = ((reform - baseline) / baseline) * 100

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

class FuelDuty(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 = self.baseline.calculate("fuel_duty", map_to="household").sum()
reform = self.reformed.calculate("fuel_duty", map_to="household").sum()

change = ((reform - baseline) / baseline) * 100

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

class TaxCredits(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 = self.baseline.calculate("tax_credits", map_to="household").sum() * -1
reform = self.reformed.calculate("tax_credits", map_to="household").sum() * -1

change = ((reform - baseline) / baseline) * 100

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

class UniversalCredit(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 = self.baseline.calculate("universal_credit", map_to="household").sum() * -1
reform = self.reformed.calculate("universal_credit", map_to="household").sum() * -1

change = ((reform - baseline) / baseline) * 100

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

class ChildBenefit(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 = self.baseline.calculate("child_benefit", map_to="household").sum() * -1
reform = self.reformed.calculate("child_benefit", map_to="household").sum() * -1

change = ((reform - baseline) / baseline) * 100

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

class StatePension(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 = self.baseline.calculate("state_pension", map_to="household").sum() * -1
reform = self.reformed.calculate("state_pension", map_to="household").sum() * -1

change = ((reform - baseline) / baseline) * 100

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

class PensionCredit(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 = self.baseline.calculate("pension_credit", map_to="household").sum() * -1
reform = self.reformed.calculate("pension_credit", map_to="household").sum() * -1

change = ((reform - baseline) / baseline) * 100

return {
"baseline": round(baseline,2),
"reform": round(reform,2),
"change": round(change,1)
}
Empty file.
24 changes: 24 additions & 0 deletions policyengine/economic_impact/economic_impact.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,20 @@
FemalePoverty as DeepFemalePoverty,
AllPoverty as DeepGenderAllPoverty
)

from .budgetary_impact.by_program.by_program import (
IncomeTax,
NationalInsurance,
Vat,
CouncilTax,
FuelDuty,
TaxCredits,
UniversalCredit,
ChildBenefit,
StatePension,
PensionCredit
)

from typing import Dict

class EconomicImpact:
Expand Down Expand Up @@ -55,6 +69,16 @@ def __init__(self, reform: dict, country: str) -> None:

# Set up metric calculators
self.metric_calculators: Dict[str, object] = {
"budgetary/by_program/income_tax" : IncomeTax(self.baseline, self.reformed),
"budgetary/by_program/national_insurance" : NationalInsurance(self.baseline, self.reformed),
"budgetary/by_program/vat" : Vat(self.baseline, self.reformed),
"budgetary/by_program/council_tax" : CouncilTax(self.baseline, self.reformed),
"budgetary/by_program/fuel_duty" : FuelDuty(self.baseline, self.reformed),
"budgetary/by_program/tax_credits" : TaxCredits(self.baseline, self.reformed),
"budgetary/by_program/universal_credits" : UniversalCredit(self.baseline, self.reformed),
"budgetary/by_program/child_benefits" : ChildBenefit(self.baseline, self.reformed),
"budgetary/by_program/state_pension" : StatePension(self.baseline, self.reformed),
"budgetary/by_program/pension_credit" : PensionCredit(self.baseline, self.reformed),
"inequality/gini": GiniCalculator(self.baseline, self.reformed),
"inequality/top_1_pct_share": Top1PctShareCalculator(self.baseline, self.reformed),
"inequality/top_10_pct_share": Top10PctShareCalculator(self.baseline, self.reformed),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# by_program
- test_income_tax:
reform:
gov.hmrc.income_tax.rates.uk[0].rate:
"2024-01-01.2100-12-31": 0.55
country: uk
expected:
baseline: 291090070166.62
reform: 496242053771.2
change: 70.5

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

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

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

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

- test_tax_credits:
reform:
gov.hmrc.income_tax.rates.uk[0].rate:
"2024-01-01.2100-12-31": 0.55
country: uk
expected:
baseline: -208150256.01
reform: -308166663.98
change: 48.1

- test_universal_credits:
reform:
gov.hmrc.income_tax.rates.uk[0].rate:
"2024-01-01.2100-12-31": 0.55
country: uk
expected:
baseline: -72209672284.1
reform: -73780445681.08
change: 2.2

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

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

- test_pension_credit:
reform:
gov.hmrc.income_tax.rates.uk[0].rate:
"2024-01-01.2100-12-31": 0.55
country: uk
expected:
baseline: -2000983943.05
reform: -2181135212.4
change: 9.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
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/budgetary_impact/by_program/by_program.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 'income_tax' in test_name:
result = economic_impact.calculate("budgetary/by_program/income_tax")
elif 'national_insurance' in test_name:
result = economic_impact.calculate("budgetary/by_program/national_insurance")
elif 'vat' in test_name:
result = economic_impact.calculate("budgetary/by_program/vat")
elif 'council_tax' in test_name:
result = economic_impact.calculate("budgetary/by_program/council_tax")
elif 'fuel_duty' in test_name:
result = economic_impact.calculate("budgetary/by_program/fuel_duty")
elif 'tax_credits' in test_name:
result = economic_impact.calculate("budgetary/by_program/tax_credits")
elif 'universal_credits' in test_name:
result = economic_impact.calculate("budgetary/by_program/universal_credits")
elif 'child_benefits' in test_name:
result = economic_impact.calculate("budgetary/by_program/child_benefits")
elif 'state_pension' in test_name:
result = economic_impact.calculate("budgetary/by_program/state_pension")
elif 'pension_credit' in test_name:
result = economic_impact.calculate("budgetary/by_program/pension_credit")
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 791a9ab

Please sign in to comment.