Skip to content

Commit

Permalink
Merge pull request #26 from masterismail/LabourSupplyImpact-Overall
Browse files Browse the repository at this point in the history
[1] - Labour supply impact overall
  • Loading branch information
masterismail authored Aug 20, 2024
2 parents 8714658 + 4b334cc commit 3a222cc
Show file tree
Hide file tree
Showing 23 changed files with 1,060 additions and 11 deletions.
21 changes: 10 additions & 11 deletions policyengine/economic_impact/budgetary_impact/overall/overall.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,22 @@ def __init__(self, baseline: Microsimulation, reformed: Microsimulation, default
self.reformed = reformed

def calculate(self):

baseline_total_tax = self.baseline.calculate("household_tax").sum()
reformed_total_tax = self.reformed.calculate("household_tax").sum()

tax_revenue_impact = reformed_total_tax - baseline_total_tax

baseline_total_benefits = self.baseline.calculate("household_benefits").sum()
reformed_total_benefits = self.reformed.calculate("household_benefits").sum()


benefit_spending_impact = reformed_total_benefits - baseline_total_benefits

budgetary_impact = tax_revenue_impact - benefit_spending_impact



return {
"budgetary_impact" : round(budgetary_impact,2)
}
Expand All @@ -35,15 +35,15 @@ def __init__(self, baseline: Microsimulation, reformed: Microsimulation, default
self.reformed = reformed

def calculate(self):

baseline_total_benefits = self.baseline.calculate("household_benefits").sum()
reformed_total_benefits = self.reformed.calculate("household_benefits").sum()


benefit_spending_impact = reformed_total_benefits - baseline_total_benefits




return {
"baseline_total_benefits": round(baseline_total_benefits,2),
"reformed_total_benefits": round(reformed_total_benefits,2),
Expand All @@ -57,15 +57,14 @@ def __init__(self, baseline: Microsimulation, reformed: Microsimulation, default
self.reformed = reformed

def calculate(self):

baseline_total_tax = self.baseline.calculate("household_tax").sum()
reformed_total_tax = self.reformed.calculate("household_tax").sum()

tax_revenue_impact = reformed_total_tax - baseline_total_tax

tax_revenue_impact = reformed_total_tax - baseline_total_tax

return {
"baseline_total_tax": round(baseline_total_tax,2),
"reformed_total_tax": round(reformed_total_tax,2),
"tax_revenue_impact": round(tax_revenue_impact,2)
}
}
34 changes: 34 additions & 0 deletions policyengine/economic_impact/economic_impact.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,28 @@
TaxRevenueImpact
)


from .labour_supply_impact.earnings.overall.relative.relative import IncomeLSR , SubstitutionLSR , NetLSRChange

from .labour_supply_impact.earnings.overall.absolute.absolute import (
IncomeLSR as AbsoluteIncomeLSR,
SubstitutionLSR as AbsoluteSubstitutionLSR,
NetLSRChange as AbsoluteNetLSRChange
)

from .labour_supply_impact.earnings.by_decile.relative.substitution_effect.substitutional_effect import SubstitutionEffect
from .labour_supply_impact.earnings.by_decile.relative.income_effect.income_effect import IncomeEffect
from .labour_supply_impact.earnings.by_decile.relative.total.total import Total

from .labour_supply_impact.earnings.by_decile.absolute.substitution_effect.substitution_effect import SubstitutionEffect as AbsoluteSubstutionEffect
from .labour_supply_impact.earnings.by_decile.absolute.income_effect.income_effect import IncomeEffect as AbsoluteIncomeEffect
from .labour_supply_impact.earnings.by_decile.absolute.total.total import Total as AbsoluteTotal


from .winners_and_losers.by_income_decile.by_income_decile import ByIncomeDecile
from .winners_and_losers.by_wealth_decile.by_wealth_decile import ByWealthDecile


from typing import Dict

class EconomicImpact:
Expand Down Expand Up @@ -121,6 +140,20 @@ def __init__(self, reform: dict, country: str) -> None:
"poverty/deep/female": DeepFemalePoverty(self.baseline, self.reformed),
"poverty/deep/gender/all": DeepGenderAllPoverty(self.baseline, self.reformed),

"labour_supply_impact/earnings/overall/relative/IncomeLSR" : IncomeLSR(self.baseline,self.reformed),
"labour_supply_impact/earnings/overall/relative/SubstitutionLSR" : SubstitutionLSR(self.baseline,self.reformed),
"labour_supply_impact/earnings/overall/relative/NetLSRChange" : NetLSRChange(self.baseline,self.reformed),
"labour_supply_impact/earnings/overall/absolute/IncomeLSR" : AbsoluteIncomeLSR(self.baseline,self.reformed),
"labour_supply_impact/earnings/overall/absolute/SubstitutionLSR" : AbsoluteSubstitutionLSR(self.baseline,self.reformed),
"labour_supply_impact/earnings/overall/absolute/NetLSRChange" : AbsoluteNetLSRChange(self.baseline,self.reformed),
"labour_supply_impact/earnings/by_decile/relative/IncomeEffect" : IncomeEffect(self.baseline,self.reformed),
"labour_supply_impact/earnings/by_decile/relative/SubstitutionEffect" : SubstitutionEffect(self.baseline,self.reformed),
"labour_supply_impact/earnings/by_decile/relative/Total" : Total(self.baseline,self.reformed),
"labour_supply_impact/earnings/by_decile/absolute/income_effect" : AbsoluteIncomeEffect(self.baseline,self.reformed),
"labour_supply_impact/earnings/by_decile/absolute/substitution_effect" : AbsoluteSubstutionEffect(self.baseline,self.reformed),
"labour_supply_impact/earnings/by_decile/absolute/total" : AbsoluteTotal(self.baseline,self.reformed),


"distributional/by_income/average": AverageByIncome(self.baseline, self.reformed),
"distributional/by_income/relative": RelativeByIncome(self.baseline, self.reformed),
"distributional/by_wealth/average": AverageByWealth(self.baseline, self.reformed),
Expand All @@ -131,6 +164,7 @@ def __init__(self, reform: dict, country: str) -> None:

"winners_and_losers/by_income_decile": ByIncomeDecile(self.baseline, self.reformed),
"winners_and_losers/by_wealth_decile": ByWealthDecile(self.baseline, self.reformed),

}

def _get_simulation_class(self) -> type:
Expand Down
Empty file.
Empty file.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
from policyengine.economic_impact.base_metric_calculator import BaseMetricCalculator
from policyengine_uk import Microsimulation
from policyengine_core.reforms import Reform
from microdf import MicroSeries
import numpy as np
from typing import Dict, Union


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

def calculate(self):
# Calculate household weight
household_weight = self.baseline.calculate("household_weight")

# Calculate household count people
household_count_people_baseline = self.baseline.calculate("household_count_people")
household_count_people_reformed = self.reformed.calculate("household_count_people")

# Initialize income LSR and substitution LSR
income_lsr_hh_baseline = np.zeros_like(household_count_people_baseline, dtype=float)
income_lsr_hh_reformed = np.zeros_like(household_count_people_reformed, dtype=float)

substitution_lsr_hh_baseline = np.zeros_like(household_count_people_baseline, dtype=float)
substitution_lsr_hh_reformed = np.zeros_like(household_count_people_reformed, dtype=float)

# Check if behavioral response exists and update income LSR
if "employment_income_behavioral_response" in self.baseline.tax_benefit_system.variables:
if np.any(self.baseline.calculate("employment_income_behavioral_response") != 0):
income_elasticity_baseline = self.baseline.calculate("income_elasticity_lsr", map_to="household")
income_lsr_hh_baseline = income_elasticity_baseline.astype(float)

if "employment_income_behavioral_response" in self.reformed.tax_benefit_system.variables:
if np.any(self.reformed.calculate("employment_income_behavioral_response") != 0):
income_elasticity_reformed = self.reformed.calculate("income_elasticity_lsr", map_to="household")
income_lsr_hh_reformed = income_elasticity_reformed.astype(float)

if "employment_income_behavioral_response" in self.baseline.tax_benefit_system.variables:
if np.any(self.baseline.calculate("employment_income_behavioral_response") != 0):
substitution_lsr_hh_baseline = (
self.baseline.calculate("substitution_elasticity_lsr", map_to="household")
.astype(float)
)

if "employment_income_behavioral_response" in self.reformed.tax_benefit_system.variables:
if np.any(self.reformed.calculate("employment_income_behavioral_response") != 0):
substitution_lsr_hh_reformed = (
self.reformed.calculate("substitution_elasticity_lsr", map_to="household")
.astype(float)
)

# Calculate differences
substitution_lsr_hh = np.array(substitution_lsr_hh_reformed) - np.array(substitution_lsr_hh_baseline)
income_lsr_hh = np.array(income_lsr_hh_reformed) - np.array(income_lsr_hh_baseline)

# Convert to MicroSeries
income_lsr_hh = MicroSeries(income_lsr_hh, weights=household_weight)
substitution_lsr_hh = MicroSeries(substitution_lsr_hh, weights=household_weight)

decile = np.array(self.baseline.calculate("household_income_decile"))
total_lsr_hh = substitution_lsr_hh + income_lsr_hh

# Calculate earnings
emp_income = MicroSeries(
self.baseline.calculate("employment_income", map_to="household").astype(float),
weights=household_weight
)

self_emp_income = MicroSeries(
self.baseline.calculate("self_employment_income", map_to="household").astype(float),
weights=household_weight
)

earnings = emp_income + self_emp_income
original_earnings = earnings - total_lsr_hh

# Calculate decile relative and averages
decile_rel = dict(
income=(
income_lsr_hh.groupby(decile).sum() / original_earnings.groupby(decile).sum()
).to_dict(),
)

decile_avg = dict(
income=income_lsr_hh.groupby(decile).mean().to_dict()
)

decile_rel["income"] = {int(k): round(v * 100,2) for k, v in decile_rel["income"].items() if k > 0}

return {
"decile_avg": decile_avg
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
from policyengine.economic_impact.base_metric_calculator import BaseMetricCalculator
from policyengine_uk import Microsimulation
from policyengine_core.reforms import Reform
from microdf import MicroSeries
import numpy as np
from typing import Dict, Union

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

def calculate(self):
# Calculate household weight
household_weight = self.baseline.calculate("household_weight")

# Calculate household count people
household_count_people_baseline = self.baseline.calculate("household_count_people")
household_count_people_reformed = self.reformed.calculate("household_count_people")

# Initialize income LSR and substitution LSR
income_lsr_hh_baseline = np.zeros_like(household_count_people_baseline, dtype=float)
income_lsr_hh_reformed = np.zeros_like(household_count_people_reformed, dtype=float)

substitution_lsr_hh_baseline = np.zeros_like(household_count_people_baseline, dtype=float)
substitution_lsr_hh_reformed = np.zeros_like(household_count_people_reformed, dtype=float)

# Check if behavioral response exists and update income LSR
if "employment_income_behavioral_response" in self.baseline.tax_benefit_system.variables:
if np.any(self.baseline.calculate("employment_income_behavioral_response") != 0):
income_elasticity_baseline = self.baseline.calculate("income_elasticity_lsr", map_to="household")
income_lsr_hh_baseline = income_elasticity_baseline.astype(float)

if "employment_income_behavioral_response" in self.reformed.tax_benefit_system.variables:
if np.any(self.reformed.calculate("employment_income_behavioral_response") != 0):
income_elasticity_reformed = self.reformed.calculate("income_elasticity_lsr", map_to="household")
income_lsr_hh_reformed = income_elasticity_reformed.astype(float)

if "employment_income_behavioral_response" in self.baseline.tax_benefit_system.variables:
if np.any(self.baseline.calculate("employment_income_behavioral_response") != 0):
substitution_lsr_hh_baseline = (
self.baseline.calculate("substitution_elasticity_lsr", map_to="household")
.astype(float)
)

if "employment_income_behavioral_response" in self.reformed.tax_benefit_system.variables:
if np.any(self.reformed.calculate("employment_income_behavioral_response") != 0):
substitution_lsr_hh_reformed = (
self.reformed.calculate("substitution_elasticity_lsr", map_to="household")
.astype(float)
)

# Calculate differences
substitution_lsr_hh = np.array(substitution_lsr_hh_reformed) - np.array(substitution_lsr_hh_baseline)
income_lsr_hh = np.array(income_lsr_hh_reformed) - np.array(income_lsr_hh_baseline)

# Convert to MicroSeries
income_lsr_hh = MicroSeries(income_lsr_hh, weights=household_weight)
substitution_lsr_hh = MicroSeries(substitution_lsr_hh, weights=household_weight)

decile = np.array(self.baseline.calculate("household_income_decile"))
total_lsr_hh = substitution_lsr_hh + income_lsr_hh

# Calculate earnings
emp_income = MicroSeries(
self.baseline.calculate("employment_income", map_to="household").astype(float),
weights=household_weight
)

self_emp_income = MicroSeries(
self.baseline.calculate("self_employment_income", map_to="household").astype(float),
weights=household_weight
)

earnings = emp_income + self_emp_income
original_earnings = earnings - total_lsr_hh

# Calculate decile relative and averages
decile_rel = dict(
income=(
income_lsr_hh.groupby(decile).sum() / original_earnings.groupby(decile).sum()
).to_dict(),
)

decile_avg = dict(
substitution=substitution_lsr_hh.groupby(decile).mean().to_dict(),
)

decile_rel["income"] = {int(k): round(v * 100,2) for k, v in decile_rel["income"].items() if k > 0}

return {
"decile_avg": decile_avg
}
Loading

0 comments on commit 3a222cc

Please sign in to comment.