policyengine 1.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. policyengine/__init__.py +1 -0
  2. policyengine/constants.py +20 -0
  3. policyengine/outputs/household/comparison/net_income_change.py +4 -0
  4. policyengine/outputs/household/single/net_income.py +2 -0
  5. policyengine/outputs/macro/comparison/budget.py +44 -0
  6. policyengine/outputs/macro/comparison/decile/income.py +51 -0
  7. policyengine/outputs/macro/comparison/decile/wealth.py +52 -0
  8. policyengine/outputs/macro/comparison/detailed_budget.py +28 -0
  9. policyengine/outputs/macro/comparison/inequality.py +34 -0
  10. policyengine/outputs/macro/comparison/labor_supply_response.py +109 -0
  11. policyengine/outputs/macro/comparison/local_areas/parliamentary_constituencies/data.py +27 -0
  12. policyengine/outputs/macro/comparison/local_areas/parliamentary_constituencies/heatmap.py +136 -0
  13. policyengine/outputs/macro/comparison/poverty/age.py +81 -0
  14. policyengine/outputs/macro/comparison/poverty/gender.py +65 -0
  15. policyengine/outputs/macro/comparison/poverty/race.py +55 -0
  16. policyengine/outputs/macro/comparison/winners/income_decile.py +71 -0
  17. policyengine/outputs/macro/comparison/winners/wealth_decile.py +71 -0
  18. policyengine/outputs/macro/single/gov/balance.py +18 -0
  19. policyengine/outputs/macro/single/gov/local_areas/parliamentary_constituencies.py +47 -0
  20. policyengine/outputs/macro/single/gov/programs.py +35 -0
  21. policyengine/outputs/macro/single/household/demographics.py +24 -0
  22. policyengine/outputs/macro/single/household/finance.py +65 -0
  23. policyengine/outputs/macro/single/household/inequality.py +32 -0
  24. policyengine/outputs/macro/single/household/labor_supply.py +62 -0
  25. policyengine/simulation.py +256 -0
  26. policyengine/utils/charts.py +125 -0
  27. policyengine/utils/huggingface.py +18 -0
  28. policyengine-1.0.1.dist-info/LICENSE +661 -0
  29. policyengine-1.0.1.dist-info/METADATA +686 -0
  30. policyengine-1.0.1.dist-info/RECORD +32 -0
  31. policyengine-1.0.1.dist-info/WHEEL +5 -0
  32. policyengine-1.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1 @@
1
+ from .simulation import Simulation
@@ -0,0 +1,20 @@
1
+ """Mainly simulation options and parameters."""
2
+
3
+ # Datasets
4
+
5
+ ENHANCED_FRS = "hf://policyengine/policyengine-uk-data/enhanced_frs_2022_23.h5"
6
+ FRS = "hf://policyengine/policyengine-uk-data/frs_2022_23.h5"
7
+
8
+ ENHANCED_CPS = "hf://policyengine/policyengine-us-data/enhanced_cps_2024.h5"
9
+ CPS = "hf://policyengine/policyengine-us-data/cps_2023.h5"
10
+ POOLED_CPS = "hf://policyengine/policyengine-us-data/pooled_3_year_cps_2023.h5"
11
+
12
+ DATASETS = {
13
+ "uk": {"frs": FRS, "enhanced_frs": ENHANCED_FRS},
14
+ "us": {"cps": CPS, "enhanced_cps": ENHANCED_CPS, "pooled_cps": POOLED_CPS},
15
+ }
16
+
17
+ DEFAULT_DATASETS = {
18
+ "uk": ENHANCED_FRS,
19
+ "us": CPS,
20
+ }
@@ -0,0 +1,4 @@
1
+ def net_income_change(simulation):
2
+ baseline_net_income = simulation.calculate("household/baseline/net_income")
3
+ reform_net_income = simulation.calculate("household/reform/net_income")
4
+ return reform_net_income - baseline_net_income
@@ -0,0 +1,2 @@
1
+ def net_income(simulation):
2
+ return simulation.selected.calculate("household_net_income").sum()
@@ -0,0 +1,44 @@
1
+ from policyengine import Simulation
2
+
3
+
4
+ def budget(simulation: Simulation):
5
+ """Calculate the budgetary impact of the given simulation.
6
+
7
+ Args:
8
+ simulation (Simulation): The simulation for which the revenue impact is to be calculated.
9
+
10
+ Returns:
11
+ dict: A dictionary containing the budgetary impact details with the following keys:
12
+ - budgetary_impact (float): The overall budgetary impact.
13
+ - tax_revenue_impact (float): The impact on tax revenue.
14
+ - state_tax_revenue_impact (float): The impact on state tax revenue.
15
+ - benefit_spending_impact (float): The impact on benefit spending.
16
+ - households (int): The number of households.
17
+ - baseline_net_income (float): The total net income in the baseline scenario.
18
+ """
19
+ baseline = simulation.calculate("macro/baseline")
20
+ reform = simulation.calculate("macro/reform")
21
+
22
+ tax_revenue_impact = (
23
+ reform["gov"]["balance"]["total_tax"]
24
+ - baseline["gov"]["balance"]["total_tax"]
25
+ )
26
+ state_tax_revenue_impact = (
27
+ reform["gov"]["balance"]["total_state_tax"]
28
+ - baseline["gov"]["balance"]["total_state_tax"]
29
+ )
30
+ benefit_spending_impact = (
31
+ reform["gov"]["balance"]["total_spending"]
32
+ - baseline["gov"]["balance"]["total_spending"]
33
+ )
34
+ budgetary_impact = tax_revenue_impact - benefit_spending_impact
35
+ households = sum(baseline["household"]["demographics"]["household_weight"])
36
+ baseline_net_income = baseline["household"]["finance"]["total_net_income"]
37
+ return dict(
38
+ budgetary_impact=budgetary_impact,
39
+ tax_revenue_impact=tax_revenue_impact,
40
+ state_tax_revenue_impact=state_tax_revenue_impact,
41
+ benefit_spending_impact=benefit_spending_impact,
42
+ households=households,
43
+ baseline_net_income=baseline_net_income,
44
+ )
@@ -0,0 +1,51 @@
1
+ from policyengine import Simulation
2
+ from microdf import MicroSeries
3
+
4
+
5
+ def income(simulation: Simulation):
6
+ """Calculate the impact of the reform on income deciles.
7
+
8
+ Args:
9
+ simulation (Simulation): The simulation for which the impact is to be calculated.
10
+
11
+ Returns:
12
+ dict: A dictionary containing the impact details with the following keys:
13
+ - relative (dict): A dictionary with keys representing deciles and values as relative income changes.
14
+ - average (dict): A dictionary with keys representing deciles and values as average income changes.
15
+ """
16
+ baseline = simulation.calculate("macro/baseline")
17
+ reform = simulation.calculate("macro/reform")
18
+
19
+ baseline_income = MicroSeries(
20
+ baseline["household"]["finance"]["household_net_income"],
21
+ weights=baseline["household"]["demographics"]["household_weight"],
22
+ )
23
+ reform_income = MicroSeries(
24
+ reform["household"]["finance"]["household_net_income"],
25
+ weights=baseline_income.weights,
26
+ )
27
+
28
+ # Filter out negative decile values
29
+ decile = MicroSeries(
30
+ baseline["household"]["finance"]["household_income_decile"]
31
+ )
32
+ baseline_income_filtered = baseline_income[decile >= 0]
33
+ reform_income_filtered = reform_income[decile >= 0]
34
+
35
+ income_change = reform_income_filtered - baseline_income_filtered
36
+ rel_income_change_by_decile = (
37
+ income_change.groupby(decile).sum()
38
+ / baseline_income_filtered.groupby(decile).sum()
39
+ )
40
+
41
+ avg_income_change_by_decile = (
42
+ income_change.groupby(decile).sum()
43
+ / baseline_income_filtered.groupby(decile).count()
44
+ )
45
+ rel_decile_dict = rel_income_change_by_decile.to_dict()
46
+ avg_decile_dict = avg_income_change_by_decile.to_dict()
47
+ result = dict(
48
+ relative={int(k): v for k, v in rel_decile_dict.items()},
49
+ average={int(k): v for k, v in avg_decile_dict.items()},
50
+ )
51
+ return result
@@ -0,0 +1,52 @@
1
+ from policyengine import Simulation
2
+ from microdf import MicroSeries
3
+
4
+
5
+ def wealth(simulation: Simulation):
6
+ """Calculate the impact of the reform on wealth deciles.
7
+
8
+ Args:
9
+ simulation (Simulation): The simulation for which the impact is to be calculated.
10
+
11
+ Returns:
12
+ dict: A dictionary containing the impact details with the following keys:
13
+ - relative (dict): A dictionary with keys representing deciles and values as relative income changes.
14
+ - average (dict): A dictionary with keys representing deciles and values as average income changes.
15
+ """
16
+ if simulation.country != "uk":
17
+ return {}
18
+
19
+ baseline = simulation.calculate("macro/baseline")
20
+ reform = simulation.calculate("macro/reform")
21
+
22
+ baseline_income = MicroSeries(
23
+ baseline["household"]["finance"]["household_net_income"],
24
+ weights=baseline["household"]["demographics"]["household_weight"],
25
+ )
26
+ reform_income = MicroSeries(
27
+ reform["household"]["finance"]["household_net_income"],
28
+ weights=baseline_income.weights,
29
+ )
30
+
31
+ # Filter out negative decile values
32
+ decile = MicroSeries(baseline["household"]["finance"]["wealth_decile"])
33
+ baseline_income_filtered = baseline_income[decile >= 0]
34
+ reform_income_filtered = reform_income[decile >= 0]
35
+
36
+ income_change = reform_income_filtered - baseline_income_filtered
37
+ rel_income_change_by_decile = (
38
+ income_change.groupby(decile).sum()
39
+ / baseline_income_filtered.groupby(decile).sum()
40
+ )
41
+
42
+ avg_income_change_by_decile = (
43
+ income_change.groupby(decile).sum()
44
+ / baseline_income_filtered.groupby(decile).count()
45
+ )
46
+ rel_decile_dict = rel_income_change_by_decile.to_dict()
47
+ avg_decile_dict = avg_income_change_by_decile.to_dict()
48
+ result = dict(
49
+ relative={int(k): v for k, v in rel_decile_dict.items()},
50
+ average={int(k): v for k, v in avg_decile_dict.items()},
51
+ )
52
+ return result
@@ -0,0 +1,28 @@
1
+ from policyengine import Simulation
2
+
3
+
4
+ def detailed_budget(simulation: Simulation):
5
+ """Calculate the detailed budgetary impact of the given simulation.
6
+
7
+ Args:
8
+ simulation (Simulation): The simulation for which the budgetary impact is to be calculated.
9
+
10
+ Returns:
11
+ dict: A dictionary containing the detailed budgetary impact for each program with the following keys:
12
+ - baseline (float): The baseline budgetary impact of the program.
13
+ - reform (float): The reform budgetary impact of the program.
14
+ - difference (float): The difference between the reform and baseline budgetary impacts.
15
+ """
16
+ baseline = simulation.calculate("macro/baseline")
17
+ reform = simulation.calculate("macro/reform")
18
+ result = {}
19
+ if simulation.country == "uk":
20
+ for program in baseline["gov"]["programs"]:
21
+ # baseline[programs][program] = total budgetary impact of program
22
+ result[program] = dict(
23
+ baseline=baseline["gov"]["programs"][program],
24
+ reform=reform["gov"]["programs"][program],
25
+ difference=reform["gov"]["programs"][program]
26
+ - baseline["gov"]["programs"][program],
27
+ )
28
+ return result
@@ -0,0 +1,34 @@
1
+ from policyengine import Simulation
2
+
3
+
4
+ def inequality(simulation: Simulation):
5
+ """Calculate the impact of the reform on inequality.
6
+
7
+ Args:
8
+ simulation (Simulation): The simulation for which the impact is to be calculated.
9
+
10
+ Returns:
11
+ dict: A dictionary containing the inequality impact details with the following keys:
12
+ - gini (dict): A dictionary with baseline and reform Gini coefficients.
13
+ - top_10_pct_share (dict): A dictionary with baseline and reform top 10% income share.
14
+ - top_1_pct_share (dict): A dictionary with baseline and reform top 1% income share.
15
+ """
16
+ baseline = simulation.calculate("macro/baseline")["household"][
17
+ "inequality"
18
+ ]
19
+ reform = simulation.calculate("macro/reform")["household"]["inequality"]
20
+
21
+ return dict(
22
+ gini=dict(
23
+ baseline=baseline["gini"],
24
+ reform=reform["gini"],
25
+ ),
26
+ top_10_pct_share=dict(
27
+ baseline=baseline["top_10_percent_share"],
28
+ reform=reform["top_10_percent_share"],
29
+ ),
30
+ top_1_pct_share=dict(
31
+ baseline=baseline["top_1_percent_share"],
32
+ reform=reform["top_1_percent_share"],
33
+ ),
34
+ )
@@ -0,0 +1,109 @@
1
+ from policyengine import Simulation
2
+ from microdf import MicroSeries
3
+ import numpy as np
4
+
5
+
6
+ def labor_supply_response(simulation: Simulation) -> dict:
7
+ baseline = simulation.calculate("macro/baseline")
8
+ reform = simulation.calculate("macro/reform")
9
+
10
+ substitution_lsr = (
11
+ reform["household"]["labor_supply"]["substitution_lsr"]
12
+ - baseline["household"]["labor_supply"]["substitution_lsr"]
13
+ )
14
+ income_lsr = (
15
+ reform["household"]["labor_supply"]["income_lsr"]
16
+ - baseline["household"]["labor_supply"]["income_lsr"]
17
+ )
18
+ total_change = substitution_lsr + income_lsr
19
+ revenue_change = (
20
+ reform["household"]["labor_supply"]["budgetary_impact_lsr"]
21
+ - baseline["household"]["labor_supply"]["budgetary_impact_lsr"]
22
+ )
23
+
24
+ substitution_lsr_hh = np.array(
25
+ reform["household"]["labor_supply"]["substitution_lsr_hh"]
26
+ ) - np.array(baseline["household"]["labor_supply"]["substitution_lsr_hh"])
27
+ income_lsr_hh = np.array(
28
+ reform["household"]["labor_supply"]["income_lsr_hh"]
29
+ ) - np.array(baseline["household"]["labor_supply"]["income_lsr_hh"])
30
+ decile = np.array(
31
+ baseline["household"]["finance"]["household_income_decile"]
32
+ )
33
+ household_weight = baseline["household"]["demographics"][
34
+ "household_weight"
35
+ ]
36
+
37
+ total_lsr_hh = substitution_lsr_hh + income_lsr_hh
38
+
39
+ emp_income = MicroSeries(
40
+ baseline["household"]["finance"]["employment_income_hh"],
41
+ weights=household_weight,
42
+ )
43
+ self_emp_income = MicroSeries(
44
+ baseline["household"]["finance"]["self_employment_income_hh"],
45
+ weights=household_weight,
46
+ )
47
+ earnings = emp_income + self_emp_income
48
+ original_earnings = earnings - total_lsr_hh
49
+ substitution_lsr_hh = MicroSeries(
50
+ substitution_lsr_hh, weights=household_weight
51
+ )
52
+ income_lsr_hh = MicroSeries(income_lsr_hh, weights=household_weight)
53
+
54
+ decile_avg = dict(
55
+ income=income_lsr_hh.groupby(decile).mean().to_dict(),
56
+ substitution=substitution_lsr_hh.groupby(decile).mean().to_dict(),
57
+ )
58
+ decile_rel = dict(
59
+ income=(
60
+ income_lsr_hh.groupby(decile).sum()
61
+ / original_earnings.groupby(decile).sum()
62
+ ).to_dict(),
63
+ substitution=(
64
+ substitution_lsr_hh.groupby(decile).sum()
65
+ / original_earnings.groupby(decile).sum()
66
+ ).to_dict(),
67
+ )
68
+
69
+ relative_lsr = dict(
70
+ income=(income_lsr_hh.sum() / original_earnings.sum()),
71
+ substitution=(substitution_lsr_hh.sum() / original_earnings.sum()),
72
+ )
73
+
74
+ decile_rel["income"] = {
75
+ int(k): v for k, v in decile_rel["income"].items() if k > 0
76
+ }
77
+ decile_rel["substitution"] = {
78
+ int(k): v for k, v in decile_rel["substitution"].items() if k > 0
79
+ }
80
+
81
+ hours = dict(
82
+ baseline=baseline["household"]["labor_supply"]["weekly_hours"],
83
+ reform=reform["household"]["labor_supply"]["weekly_hours"],
84
+ change=reform["household"]["labor_supply"]["weekly_hours"]
85
+ - baseline["household"]["labor_supply"]["weekly_hours"],
86
+ income_effect=reform["household"]["labor_supply"][
87
+ "weekly_hours_income_effect"
88
+ ]
89
+ - baseline["household"]["labor_supply"]["weekly_hours_income_effect"],
90
+ substitution_effect=reform["household"]["labor_supply"][
91
+ "weekly_hours_substitution_effect"
92
+ ]
93
+ - baseline["household"]["labor_supply"][
94
+ "weekly_hours_substitution_effect"
95
+ ],
96
+ )
97
+
98
+ return dict(
99
+ substitution_lsr=substitution_lsr,
100
+ income_lsr=income_lsr,
101
+ relative_lsr=relative_lsr,
102
+ total_change=total_change,
103
+ revenue_change=revenue_change,
104
+ decile=dict(
105
+ average=decile_avg,
106
+ relative=decile_rel,
107
+ ),
108
+ hours=hours,
109
+ )
@@ -0,0 +1,27 @@
1
+ from policyengine import Simulation
2
+
3
+
4
+ def data(simulation: Simulation) -> dict:
5
+ if not simulation.options.get("include_constituencies"):
6
+ return {}
7
+
8
+ constituency_baseline = simulation.calculate(
9
+ "macro/baseline/gov/local_areas/parliamentary_constituencies"
10
+ )
11
+ constituency_reform = simulation.calculate(
12
+ "macro/reform/gov/local_areas/parliamentary_constituencies"
13
+ )
14
+
15
+ result = {}
16
+
17
+ for constituency in constituency_baseline:
18
+ result[constituency] = {}
19
+ for key in constituency_baseline[constituency]:
20
+ result[constituency][key] = {
21
+ "change": constituency_reform[constituency][key]
22
+ - constituency_baseline[constituency][key],
23
+ "baseline": constituency_baseline[constituency][key],
24
+ "reform": constituency_reform[constituency][key],
25
+ }
26
+
27
+ return result
@@ -0,0 +1,136 @@
1
+ from policyengine import Simulation
2
+ import pandas as pd
3
+ from policyengine.utils.huggingface import download
4
+ import plotly.express as px
5
+ from policyengine.utils.charts import *
6
+
7
+
8
+ def heatmap(
9
+ simulation: Simulation,
10
+ variable: str = None,
11
+ aggregator: str = None,
12
+ relative: bool = None,
13
+ ) -> dict:
14
+ if not simulation.options.get("include_constituencies"):
15
+ return {}
16
+
17
+ options = {}
18
+
19
+ if variable is not None:
20
+ options["variables"] = [variable]
21
+ if aggregator is not None:
22
+ options["aggregator"] = aggregator
23
+
24
+ constituency_baseline = simulation.calculate(
25
+ "macro/baseline/gov/local_areas/parliamentary_constituencies",
26
+ **options,
27
+ )
28
+ constituency_reform = simulation.calculate(
29
+ "macro/reform/gov/local_areas/parliamentary_constituencies", **options
30
+ )
31
+
32
+ result = {}
33
+
34
+ constituency_names_file_path = download(
35
+ repo="policyengine/policyengine-uk-data",
36
+ repo_filename="constituencies_2024.csv",
37
+ local_folder=None,
38
+ version=None,
39
+ )
40
+ constituency_names = pd.read_csv(constituency_names_file_path)
41
+ hex_map_locations = pd.read_csv(
42
+ "/Users/nikhilwoodruff/uk-local-area-calibration/policyengine_uk_local_areas/hex_map/hex_map_2024.csv"
43
+ ).set_index("code")
44
+
45
+ if variable is None:
46
+ variable = "household_net_income"
47
+ if relative is None:
48
+ relative = True
49
+
50
+ for constituency in constituency_baseline:
51
+ if relative:
52
+ result[constituency] = (
53
+ constituency_reform[constituency][variable]
54
+ / constituency_baseline[constituency][variable]
55
+ - 1
56
+ )
57
+ else:
58
+ result[constituency] = (
59
+ constituency_reform[constituency][variable]
60
+ - constituency_baseline[constituency][variable]
61
+ )
62
+
63
+ constituency_names["x"] = hex_map_locations.loc[
64
+ constituency_names["code"]
65
+ ]["x"].values
66
+ constituency_names["y"] = hex_map_locations.loc[
67
+ constituency_names["code"]
68
+ ]["y"].values
69
+ x_range = constituency_names["x"].max() - constituency_names["x"].min()
70
+ y_range = constituency_names["y"].max() - constituency_names["y"].min()
71
+ # Expand x range to preserve aspect ratio
72
+ expanded_lower_x_range = -(y_range - x_range) / 2
73
+ expanded_upper_x_range = x_range - expanded_lower_x_range
74
+ constituency_names.x = (
75
+ constituency_names.x - (constituency_names.y % 2 == 0) * 0.5
76
+ )
77
+ constituency_names["Relative change"] = (
78
+ pd.Series(list(result.values()), index=list(result.keys()))
79
+ .loc[constituency_names["name"]]
80
+ .values
81
+ )
82
+
83
+ label = simulation.baseline.tax_benefit_system.variables[variable].label
84
+
85
+ fig = px.scatter(
86
+ constituency_names,
87
+ x="x",
88
+ y="y",
89
+ color="Relative change",
90
+ hover_name="name",
91
+ title=f"{'Relative change' if relative else 'Change'} in {label} by parliamentary constituency",
92
+ )
93
+
94
+ format_fig(fig)
95
+
96
+ # Show hexagons on scatter points
97
+
98
+ fig.update_traces(
99
+ marker=dict(
100
+ symbol="hexagon", line=dict(width=0, color="lightgray"), size=15
101
+ )
102
+ )
103
+ fig.update_layout(
104
+ xaxis_tickvals=[],
105
+ xaxis_title="",
106
+ yaxis_tickvals=[],
107
+ yaxis_title="",
108
+ xaxis_range=[expanded_lower_x_range, expanded_upper_x_range],
109
+ yaxis_range=[
110
+ constituency_names["y"].min(),
111
+ constituency_names["y"].max(),
112
+ ],
113
+ ).update_traces(marker_size=10).update_layout(
114
+ xaxis_range=[30, 85], yaxis_range=[-50, 2]
115
+ )
116
+
117
+ x_min = fig.data[0]["marker"]["color"].min()
118
+ x_max = fig.data[0]["marker"]["color"].max()
119
+ max_abs = max(abs(x_min), abs(x_max))
120
+
121
+ fig.update_layout(
122
+ coloraxis=dict(
123
+ cmin=-max_abs,
124
+ cmax=max_abs,
125
+ colorscale=[
126
+ [0, DARK_GRAY],
127
+ [0.5, "lightgray"],
128
+ [1, BLUE],
129
+ ],
130
+ colorbar=dict(
131
+ tickformat=".0%" if relative else ",.0f",
132
+ ),
133
+ )
134
+ )
135
+
136
+ return fig
@@ -0,0 +1,81 @@
1
+ from policyengine import Simulation
2
+ from microdf import MicroSeries
3
+
4
+
5
+ def age(simulation: Simulation):
6
+ """Calculate the impact of the reform on poverty by age.
7
+
8
+ Args:
9
+ simulation (Simulation): The simulation for which the impact is to be calculated.
10
+
11
+ Returns:
12
+ dict: A dictionary containing the poverty and deep poverty impact details with the following keys:
13
+ - poverty (dict): A dictionary with keys representing age groups and values as dictionaries with baseline and reform poverty rates.
14
+ - deep_poverty (dict): A dictionary with keys representing age groups and values as dictionaries with baseline and reform deep poverty rates.
15
+ """
16
+ baseline = simulation.calculate("macro/baseline")["household"]["finance"]
17
+ reform = simulation.calculate("macro/reform")["household"]["finance"]
18
+ baseline_demographics = simulation.calculate("macro/baseline")[
19
+ "household"
20
+ ]["demographics"]
21
+
22
+ baseline_poverty = MicroSeries(
23
+ baseline["person_in_poverty"],
24
+ weights=baseline_demographics["person_weight"],
25
+ )
26
+ baseline_deep_poverty = MicroSeries(
27
+ baseline["person_in_deep_poverty"],
28
+ weights=baseline_demographics["person_weight"],
29
+ )
30
+ reform_poverty = MicroSeries(
31
+ reform["person_in_poverty"], weights=baseline_poverty.weights
32
+ )
33
+ reform_deep_poverty = MicroSeries(
34
+ reform["person_in_deep_poverty"], weights=baseline_poverty.weights
35
+ )
36
+ age = MicroSeries(baseline_demographics["age"])
37
+
38
+ poverty = dict(
39
+ child=dict(
40
+ baseline=float(baseline_poverty[age < 18].mean()),
41
+ reform=float(reform_poverty[age < 18].mean()),
42
+ ),
43
+ adult=dict(
44
+ baseline=float(baseline_poverty[(age >= 18) & (age < 65)].mean()),
45
+ reform=float(reform_poverty[(age >= 18) & (age < 65)].mean()),
46
+ ),
47
+ senior=dict(
48
+ baseline=float(baseline_poverty[age >= 65].mean()),
49
+ reform=float(reform_poverty[age >= 65].mean()),
50
+ ),
51
+ all=dict(
52
+ baseline=float(baseline_poverty.mean()),
53
+ reform=float(reform_poverty.mean()),
54
+ ),
55
+ )
56
+
57
+ deep_poverty = dict(
58
+ child=dict(
59
+ baseline=float(baseline_deep_poverty[age < 18].mean()),
60
+ reform=float(reform_deep_poverty[age < 18].mean()),
61
+ ),
62
+ adult=dict(
63
+ baseline=float(
64
+ baseline_deep_poverty[(age >= 18) & (age < 65)].mean()
65
+ ),
66
+ reform=float(reform_deep_poverty[(age >= 18) & (age < 65)].mean()),
67
+ ),
68
+ senior=dict(
69
+ baseline=float(baseline_deep_poverty[age >= 65].mean()),
70
+ reform=float(reform_deep_poverty[age >= 65].mean()),
71
+ ),
72
+ all=dict(
73
+ baseline=float(baseline_deep_poverty.mean()),
74
+ reform=float(reform_deep_poverty.mean()),
75
+ ),
76
+ )
77
+
78
+ return dict(
79
+ poverty=poverty,
80
+ deep_poverty=deep_poverty,
81
+ )
@@ -0,0 +1,65 @@
1
+ from policyengine import Simulation
2
+ from microdf import MicroSeries
3
+
4
+
5
+ def gender(simulation: Simulation):
6
+ """Calculate the impact of the reform on poverty by gender.
7
+
8
+ Args:
9
+ simulation (Simulation): The simulation for which the impact is to be calculated.
10
+
11
+ Returns:
12
+ dict: A dictionary containing the poverty and deep poverty impact details with the following keys:
13
+ - poverty (dict): A dictionary with keys representing genders and values as dictionaries with baseline and reform poverty rates.
14
+ - deep_poverty (dict): A dictionary with keys representing genders and values as dictionaries with baseline and reform deep poverty rates.
15
+ """
16
+ baseline = simulation.calculate("macro/baseline")["household"]["finance"]
17
+ reform = simulation.calculate("macro/reform")["household"]["finance"]
18
+ baseline_demographics = simulation.calculate("macro/baseline")[
19
+ "household"
20
+ ]["demographics"]
21
+
22
+ if baseline_demographics["is_male"] is None:
23
+ return {}
24
+ baseline_poverty = MicroSeries(
25
+ baseline["person_in_poverty"],
26
+ weights=baseline_demographics["person_weight"],
27
+ )
28
+ baseline_deep_poverty = MicroSeries(
29
+ baseline["person_in_deep_poverty"],
30
+ weights=baseline_demographics["person_weight"],
31
+ )
32
+ reform_poverty = MicroSeries(
33
+ reform["person_in_poverty"], weights=baseline_poverty.weights
34
+ )
35
+ reform_deep_poverty = MicroSeries(
36
+ reform["person_in_deep_poverty"], weights=baseline_poverty.weights
37
+ )
38
+ is_male = MicroSeries(baseline_demographics["is_male"])
39
+
40
+ poverty = dict(
41
+ male=dict(
42
+ baseline=float(baseline_poverty[is_male].mean()),
43
+ reform=float(reform_poverty[is_male].mean()),
44
+ ),
45
+ female=dict(
46
+ baseline=float(baseline_poverty[~is_male].mean()),
47
+ reform=float(reform_poverty[~is_male].mean()),
48
+ ),
49
+ )
50
+
51
+ deep_poverty = dict(
52
+ male=dict(
53
+ baseline=float(baseline_deep_poverty[is_male].mean()),
54
+ reform=float(reform_deep_poverty[is_male].mean()),
55
+ ),
56
+ female=dict(
57
+ baseline=float(baseline_deep_poverty[~is_male].mean()),
58
+ reform=float(reform_deep_poverty[~is_male].mean()),
59
+ ),
60
+ )
61
+
62
+ return dict(
63
+ poverty=poverty,
64
+ deep_poverty=deep_poverty,
65
+ )