Skip to content

Commit 3a222cc

Browse files
authored
Merge pull request #26 from masterismail/LabourSupplyImpact-Overall
[1] - Labour supply impact overall
2 parents 8714658 + 4b334cc commit 3a222cc

File tree

23 files changed

+1060
-11
lines changed

23 files changed

+1060
-11
lines changed

policyengine/economic_impact/budgetary_impact/overall/overall.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,22 @@ def __init__(self, baseline: Microsimulation, reformed: Microsimulation, default
88
self.reformed = reformed
99

1010
def calculate(self):
11-
11+
1212
baseline_total_tax = self.baseline.calculate("household_tax").sum()
1313
reformed_total_tax = self.reformed.calculate("household_tax").sum()
14-
14+
1515
tax_revenue_impact = reformed_total_tax - baseline_total_tax
1616

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

20-
20+
2121
benefit_spending_impact = reformed_total_benefits - baseline_total_benefits
2222

2323
budgetary_impact = tax_revenue_impact - benefit_spending_impact
2424

2525

26-
26+
2727
return {
2828
"budgetary_impact" : round(budgetary_impact,2)
2929
}
@@ -35,15 +35,15 @@ def __init__(self, baseline: Microsimulation, reformed: Microsimulation, default
3535
self.reformed = reformed
3636

3737
def calculate(self):
38-
38+
3939
baseline_total_benefits = self.baseline.calculate("household_benefits").sum()
4040
reformed_total_benefits = self.reformed.calculate("household_benefits").sum()
4141

42-
42+
4343
benefit_spending_impact = reformed_total_benefits - baseline_total_benefits
4444

45-
4645

46+
4747
return {
4848
"baseline_total_benefits": round(baseline_total_benefits,2),
4949
"reformed_total_benefits": round(reformed_total_benefits,2),
@@ -57,15 +57,14 @@ def __init__(self, baseline: Microsimulation, reformed: Microsimulation, default
5757
self.reformed = reformed
5858

5959
def calculate(self):
60-
60+
6161
baseline_total_tax = self.baseline.calculate("household_tax").sum()
6262
reformed_total_tax = self.reformed.calculate("household_tax").sum()
63-
64-
tax_revenue_impact = reformed_total_tax - baseline_total_tax
6563

64+
tax_revenue_impact = reformed_total_tax - baseline_total_tax
6665

6766
return {
6867
"baseline_total_tax": round(baseline_total_tax,2),
6968
"reformed_total_tax": round(reformed_total_tax,2),
7069
"tax_revenue_impact": round(tax_revenue_impact,2)
71-
}
70+
}

policyengine/economic_impact/economic_impact.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,28 @@
5454
TaxRevenueImpact
5555
)
5656

57+
58+
from .labour_supply_impact.earnings.overall.relative.relative import IncomeLSR , SubstitutionLSR , NetLSRChange
59+
60+
from .labour_supply_impact.earnings.overall.absolute.absolute import (
61+
IncomeLSR as AbsoluteIncomeLSR,
62+
SubstitutionLSR as AbsoluteSubstitutionLSR,
63+
NetLSRChange as AbsoluteNetLSRChange
64+
)
65+
66+
from .labour_supply_impact.earnings.by_decile.relative.substitution_effect.substitutional_effect import SubstitutionEffect
67+
from .labour_supply_impact.earnings.by_decile.relative.income_effect.income_effect import IncomeEffect
68+
from .labour_supply_impact.earnings.by_decile.relative.total.total import Total
69+
70+
from .labour_supply_impact.earnings.by_decile.absolute.substitution_effect.substitution_effect import SubstitutionEffect as AbsoluteSubstutionEffect
71+
from .labour_supply_impact.earnings.by_decile.absolute.income_effect.income_effect import IncomeEffect as AbsoluteIncomeEffect
72+
from .labour_supply_impact.earnings.by_decile.absolute.total.total import Total as AbsoluteTotal
73+
74+
5775
from .winners_and_losers.by_income_decile.by_income_decile import ByIncomeDecile
5876
from .winners_and_losers.by_wealth_decile.by_wealth_decile import ByWealthDecile
5977

78+
6079
from typing import Dict
6180

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

143+
"labour_supply_impact/earnings/overall/relative/IncomeLSR" : IncomeLSR(self.baseline,self.reformed),
144+
"labour_supply_impact/earnings/overall/relative/SubstitutionLSR" : SubstitutionLSR(self.baseline,self.reformed),
145+
"labour_supply_impact/earnings/overall/relative/NetLSRChange" : NetLSRChange(self.baseline,self.reformed),
146+
"labour_supply_impact/earnings/overall/absolute/IncomeLSR" : AbsoluteIncomeLSR(self.baseline,self.reformed),
147+
"labour_supply_impact/earnings/overall/absolute/SubstitutionLSR" : AbsoluteSubstitutionLSR(self.baseline,self.reformed),
148+
"labour_supply_impact/earnings/overall/absolute/NetLSRChange" : AbsoluteNetLSRChange(self.baseline,self.reformed),
149+
"labour_supply_impact/earnings/by_decile/relative/IncomeEffect" : IncomeEffect(self.baseline,self.reformed),
150+
"labour_supply_impact/earnings/by_decile/relative/SubstitutionEffect" : SubstitutionEffect(self.baseline,self.reformed),
151+
"labour_supply_impact/earnings/by_decile/relative/Total" : Total(self.baseline,self.reformed),
152+
"labour_supply_impact/earnings/by_decile/absolute/income_effect" : AbsoluteIncomeEffect(self.baseline,self.reformed),
153+
"labour_supply_impact/earnings/by_decile/absolute/substitution_effect" : AbsoluteSubstutionEffect(self.baseline,self.reformed),
154+
"labour_supply_impact/earnings/by_decile/absolute/total" : AbsoluteTotal(self.baseline,self.reformed),
155+
156+
124157
"distributional/by_income/average": AverageByIncome(self.baseline, self.reformed),
125158
"distributional/by_income/relative": RelativeByIncome(self.baseline, self.reformed),
126159
"distributional/by_wealth/average": AverageByWealth(self.baseline, self.reformed),
@@ -131,6 +164,7 @@ def __init__(self, reform: dict, country: str) -> None:
131164

132165
"winners_and_losers/by_income_decile": ByIncomeDecile(self.baseline, self.reformed),
133166
"winners_and_losers/by_wealth_decile": ByWealthDecile(self.baseline, self.reformed),
167+
134168
}
135169

136170
def _get_simulation_class(self) -> type:

policyengine/economic_impact/labour_supply_impact/__init__.py

Whitespace-only changes.

policyengine/economic_impact/labour_supply_impact/earnings/by_decile/__init__.py

Whitespace-only changes.

policyengine/economic_impact/labour_supply_impact/earnings/by_decile/absolute/__init__.py

Whitespace-only changes.

policyengine/economic_impact/labour_supply_impact/earnings/by_decile/absolute/income_effect/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
from policyengine.economic_impact.base_metric_calculator import BaseMetricCalculator
2+
from policyengine_uk import Microsimulation
3+
from policyengine_core.reforms import Reform
4+
from microdf import MicroSeries
5+
import numpy as np
6+
from typing import Dict, Union
7+
8+
9+
class IncomeEffect(BaseMetricCalculator):
10+
def __init__(self, baseline: Microsimulation, reformed: Microsimulation, default_period: int = 2024) -> None:
11+
super().__init__(baseline, reformed, default_period)
12+
13+
def calculate(self):
14+
# Calculate household weight
15+
household_weight = self.baseline.calculate("household_weight")
16+
17+
# Calculate household count people
18+
household_count_people_baseline = self.baseline.calculate("household_count_people")
19+
household_count_people_reformed = self.reformed.calculate("household_count_people")
20+
21+
# Initialize income LSR and substitution LSR
22+
income_lsr_hh_baseline = np.zeros_like(household_count_people_baseline, dtype=float)
23+
income_lsr_hh_reformed = np.zeros_like(household_count_people_reformed, dtype=float)
24+
25+
substitution_lsr_hh_baseline = np.zeros_like(household_count_people_baseline, dtype=float)
26+
substitution_lsr_hh_reformed = np.zeros_like(household_count_people_reformed, dtype=float)
27+
28+
# Check if behavioral response exists and update income LSR
29+
if "employment_income_behavioral_response" in self.baseline.tax_benefit_system.variables:
30+
if np.any(self.baseline.calculate("employment_income_behavioral_response") != 0):
31+
income_elasticity_baseline = self.baseline.calculate("income_elasticity_lsr", map_to="household")
32+
income_lsr_hh_baseline = income_elasticity_baseline.astype(float)
33+
34+
if "employment_income_behavioral_response" in self.reformed.tax_benefit_system.variables:
35+
if np.any(self.reformed.calculate("employment_income_behavioral_response") != 0):
36+
income_elasticity_reformed = self.reformed.calculate("income_elasticity_lsr", map_to="household")
37+
income_lsr_hh_reformed = income_elasticity_reformed.astype(float)
38+
39+
if "employment_income_behavioral_response" in self.baseline.tax_benefit_system.variables:
40+
if np.any(self.baseline.calculate("employment_income_behavioral_response") != 0):
41+
substitution_lsr_hh_baseline = (
42+
self.baseline.calculate("substitution_elasticity_lsr", map_to="household")
43+
.astype(float)
44+
)
45+
46+
if "employment_income_behavioral_response" in self.reformed.tax_benefit_system.variables:
47+
if np.any(self.reformed.calculate("employment_income_behavioral_response") != 0):
48+
substitution_lsr_hh_reformed = (
49+
self.reformed.calculate("substitution_elasticity_lsr", map_to="household")
50+
.astype(float)
51+
)
52+
53+
# Calculate differences
54+
substitution_lsr_hh = np.array(substitution_lsr_hh_reformed) - np.array(substitution_lsr_hh_baseline)
55+
income_lsr_hh = np.array(income_lsr_hh_reformed) - np.array(income_lsr_hh_baseline)
56+
57+
# Convert to MicroSeries
58+
income_lsr_hh = MicroSeries(income_lsr_hh, weights=household_weight)
59+
substitution_lsr_hh = MicroSeries(substitution_lsr_hh, weights=household_weight)
60+
61+
decile = np.array(self.baseline.calculate("household_income_decile"))
62+
total_lsr_hh = substitution_lsr_hh + income_lsr_hh
63+
64+
# Calculate earnings
65+
emp_income = MicroSeries(
66+
self.baseline.calculate("employment_income", map_to="household").astype(float),
67+
weights=household_weight
68+
)
69+
70+
self_emp_income = MicroSeries(
71+
self.baseline.calculate("self_employment_income", map_to="household").astype(float),
72+
weights=household_weight
73+
)
74+
75+
earnings = emp_income + self_emp_income
76+
original_earnings = earnings - total_lsr_hh
77+
78+
# Calculate decile relative and averages
79+
decile_rel = dict(
80+
income=(
81+
income_lsr_hh.groupby(decile).sum() / original_earnings.groupby(decile).sum()
82+
).to_dict(),
83+
)
84+
85+
decile_avg = dict(
86+
income=income_lsr_hh.groupby(decile).mean().to_dict()
87+
)
88+
89+
decile_rel["income"] = {int(k): round(v * 100,2) for k, v in decile_rel["income"].items() if k > 0}
90+
91+
return {
92+
"decile_avg": decile_avg
93+
}

policyengine/economic_impact/labour_supply_impact/earnings/by_decile/absolute/substitution_effect/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
from policyengine.economic_impact.base_metric_calculator import BaseMetricCalculator
2+
from policyengine_uk import Microsimulation
3+
from policyengine_core.reforms import Reform
4+
from microdf import MicroSeries
5+
import numpy as np
6+
from typing import Dict, Union
7+
8+
class SubstitutionEffect(BaseMetricCalculator):
9+
def __init__(self, baseline: Microsimulation, reformed: Microsimulation, default_period: int = 2024) -> None:
10+
super().__init__(baseline, reformed, default_period)
11+
12+
def calculate(self):
13+
# Calculate household weight
14+
household_weight = self.baseline.calculate("household_weight")
15+
16+
# Calculate household count people
17+
household_count_people_baseline = self.baseline.calculate("household_count_people")
18+
household_count_people_reformed = self.reformed.calculate("household_count_people")
19+
20+
# Initialize income LSR and substitution LSR
21+
income_lsr_hh_baseline = np.zeros_like(household_count_people_baseline, dtype=float)
22+
income_lsr_hh_reformed = np.zeros_like(household_count_people_reformed, dtype=float)
23+
24+
substitution_lsr_hh_baseline = np.zeros_like(household_count_people_baseline, dtype=float)
25+
substitution_lsr_hh_reformed = np.zeros_like(household_count_people_reformed, dtype=float)
26+
27+
# Check if behavioral response exists and update income LSR
28+
if "employment_income_behavioral_response" in self.baseline.tax_benefit_system.variables:
29+
if np.any(self.baseline.calculate("employment_income_behavioral_response") != 0):
30+
income_elasticity_baseline = self.baseline.calculate("income_elasticity_lsr", map_to="household")
31+
income_lsr_hh_baseline = income_elasticity_baseline.astype(float)
32+
33+
if "employment_income_behavioral_response" in self.reformed.tax_benefit_system.variables:
34+
if np.any(self.reformed.calculate("employment_income_behavioral_response") != 0):
35+
income_elasticity_reformed = self.reformed.calculate("income_elasticity_lsr", map_to="household")
36+
income_lsr_hh_reformed = income_elasticity_reformed.astype(float)
37+
38+
if "employment_income_behavioral_response" in self.baseline.tax_benefit_system.variables:
39+
if np.any(self.baseline.calculate("employment_income_behavioral_response") != 0):
40+
substitution_lsr_hh_baseline = (
41+
self.baseline.calculate("substitution_elasticity_lsr", map_to="household")
42+
.astype(float)
43+
)
44+
45+
if "employment_income_behavioral_response" in self.reformed.tax_benefit_system.variables:
46+
if np.any(self.reformed.calculate("employment_income_behavioral_response") != 0):
47+
substitution_lsr_hh_reformed = (
48+
self.reformed.calculate("substitution_elasticity_lsr", map_to="household")
49+
.astype(float)
50+
)
51+
52+
# Calculate differences
53+
substitution_lsr_hh = np.array(substitution_lsr_hh_reformed) - np.array(substitution_lsr_hh_baseline)
54+
income_lsr_hh = np.array(income_lsr_hh_reformed) - np.array(income_lsr_hh_baseline)
55+
56+
# Convert to MicroSeries
57+
income_lsr_hh = MicroSeries(income_lsr_hh, weights=household_weight)
58+
substitution_lsr_hh = MicroSeries(substitution_lsr_hh, weights=household_weight)
59+
60+
decile = np.array(self.baseline.calculate("household_income_decile"))
61+
total_lsr_hh = substitution_lsr_hh + income_lsr_hh
62+
63+
# Calculate earnings
64+
emp_income = MicroSeries(
65+
self.baseline.calculate("employment_income", map_to="household").astype(float),
66+
weights=household_weight
67+
)
68+
69+
self_emp_income = MicroSeries(
70+
self.baseline.calculate("self_employment_income", map_to="household").astype(float),
71+
weights=household_weight
72+
)
73+
74+
earnings = emp_income + self_emp_income
75+
original_earnings = earnings - total_lsr_hh
76+
77+
# Calculate decile relative and averages
78+
decile_rel = dict(
79+
income=(
80+
income_lsr_hh.groupby(decile).sum() / original_earnings.groupby(decile).sum()
81+
).to_dict(),
82+
)
83+
84+
decile_avg = dict(
85+
substitution=substitution_lsr_hh.groupby(decile).mean().to_dict(),
86+
)
87+
88+
decile_rel["income"] = {int(k): round(v * 100,2) for k, v in decile_rel["income"].items() if k > 0}
89+
90+
return {
91+
"decile_avg": decile_avg
92+
}

policyengine/economic_impact/labour_supply_impact/earnings/by_decile/absolute/total/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)