Skip to content

Commit b65e698

Browse files
Add budget charts and data
1 parent 53d9251 commit b65e698

File tree

10 files changed

+259
-224
lines changed

10 files changed

+259
-224
lines changed

policyengine/outputs/macro/comparison/budget.py

Lines changed: 0 additions & 44 deletions
This file was deleted.
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
from policyengine import Simulation
2+
import plotly.graph_objects as go
3+
import plotly.express as px
4+
import pandas as pd
5+
from policyengine.utils.charts import *
6+
7+
8+
def general(simulation: Simulation, chart: bool = False):
9+
"""Calculate the budgetary impact of the given simulation.
10+
11+
Args:
12+
simulation (Simulation): The simulation for which the revenue impact is to be calculated.
13+
14+
Returns:
15+
dict: A dictionary containing the budgetary impact details with the following keys:
16+
- budgetary_impact (float): The overall budgetary impact.
17+
- tax_revenue_impact (float): The impact on tax revenue.
18+
- state_tax_revenue_impact (float): The impact on state tax revenue.
19+
- benefit_spending_impact (float): The impact on benefit spending.
20+
- households (int): The number of households.
21+
- baseline_net_income (float): The total net income in the baseline scenario.
22+
"""
23+
baseline = simulation.calculate("macro/baseline")
24+
reform = simulation.calculate("macro/reform")
25+
26+
tax_revenue_impact = (
27+
reform["gov"]["balance"]["total_tax"]
28+
- baseline["gov"]["balance"]["total_tax"]
29+
)
30+
state_tax_revenue_impact = (
31+
reform["gov"]["balance"]["total_state_tax"]
32+
- baseline["gov"]["balance"]["total_state_tax"]
33+
)
34+
benefit_spending_impact = (
35+
reform["gov"]["balance"]["total_spending"]
36+
- baseline["gov"]["balance"]["total_spending"]
37+
)
38+
budgetary_impact = tax_revenue_impact - benefit_spending_impact
39+
households = sum(baseline["household"]["demographics"]["household_weight"])
40+
baseline_net_income = baseline["household"]["finance"]["total_net_income"]
41+
result = dict(
42+
budgetary_impact=budgetary_impact,
43+
tax_revenue_impact=tax_revenue_impact,
44+
state_tax_revenue_impact=state_tax_revenue_impact,
45+
benefit_spending_impact=benefit_spending_impact,
46+
households=households,
47+
baseline_net_income=baseline_net_income,
48+
)
49+
if chart:
50+
return budget_chart(simulation, result)
51+
else:
52+
return result
53+
54+
55+
def budget_chart(simulation: Simulation, data: dict) -> go.Figure:
56+
if simulation.country == "uk":
57+
x = ["Tax revenues", "Benefit spending", "Budgetary impact"]
58+
y = [
59+
data["tax_revenue_impact"],
60+
-data["benefit_spending_impact"],
61+
data["budgetary_impact"],
62+
]
63+
else:
64+
x = [
65+
"Federal tax revenues",
66+
"State tax revenues",
67+
"Benefit spending",
68+
"Budgetary impact",
69+
]
70+
y = [
71+
data["tax_revenue_impact"] - data["state_tax_revenue_impact"],
72+
data["state_tax_revenue_impact"],
73+
-data["benefit_spending_impact"],
74+
data["budgetary_impact"],
75+
]
76+
fig = go.Figure(
77+
data=[
78+
go.Waterfall(
79+
x=x,
80+
y=[i / 1e9 for i in y],
81+
orientation="v",
82+
measure=["relative"] * (len(x) - 1) + ["total"],
83+
text=[
84+
(
85+
"+" + str(round(val / 1e9, 1))
86+
if val > 0
87+
else str(round(val / 1e9, 1))
88+
)
89+
for val in y
90+
],
91+
textposition="inside",
92+
increasing={"marker": {"color": BLUE}},
93+
decreasing={"marker": {"color": DARK_GRAY}},
94+
totals={"marker": {"color": BLUE if y[-1] > 0 else DARK_GRAY}},
95+
connector={
96+
"line": {"color": DARK_GRAY, "width": 2, "dash": "dot"}
97+
},
98+
)
99+
]
100+
)
101+
102+
fig.update_layout(
103+
title="Budgetary impact by government revenue and spending",
104+
xaxis_title="",
105+
yaxis_title="Budgetary impact (£ billions)",
106+
yaxis_tickformat=",.0f",
107+
)
108+
109+
return format_fig(fig)

policyengine/outputs/macro/comparison/detailed_budget.py renamed to policyengine/outputs/macro/comparison/budget/programs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from policyengine import Simulation
22

33

4-
def detailed_budget(simulation: Simulation):
4+
def programs(simulation: Simulation):
55
"""Calculate the detailed budgetary impact of the given simulation.
66
77
Args:
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
from policyengine import Simulation
2+
import plotly.graph_objects as go
3+
import plotly.express as px
4+
import pandas as pd
5+
from policyengine.utils.charts import *
6+
7+
8+
def window(
9+
simulation: Simulation,
10+
chart: bool = False,
11+
federal_only: bool = False,
12+
count_years: int = 1,
13+
):
14+
if count_years == 1:
15+
kwargs = {}
16+
else:
17+
kwargs = {"count_years": count_years}
18+
baseline = simulation.calculate(
19+
"macro/baseline/gov/budget_window", **kwargs
20+
)
21+
reform = simulation.calculate("macro/reform/gov/budget_window", **kwargs)
22+
total_budget_effect = [
23+
(y - x) / 1e9
24+
for x, y in zip(baseline["total_budget"], reform["total_budget"])
25+
]
26+
federal_budget_effect = [
27+
(y - x) / 1e9
28+
for x, y in zip(
29+
baseline["total_federal_budget"], reform["total_federal_budget"]
30+
)
31+
]
32+
33+
result = dict(
34+
total_budget=total_budget_effect,
35+
federal_budget=federal_budget_effect,
36+
)
37+
38+
if chart:
39+
return budget_window_chart(
40+
result["federal_budget"]
41+
if federal_only
42+
else result["total_budget"]
43+
)
44+
else:
45+
return result
46+
47+
48+
def budget_window_chart(budget_effect) -> go.Figure:
49+
fig = go.Figure(
50+
data=[
51+
go.Bar(
52+
y=budget_effect,
53+
x=list(map(str, range(2025, 2025 + 10))),
54+
marker=dict(
55+
color=[BLUE if y > 0 else DARK_GRAY for y in budget_effect]
56+
),
57+
text=[
58+
f"{'+' if y >= 0 else ''}{y:.1f}" for y in budget_effect
59+
],
60+
)
61+
]
62+
).update_layout(
63+
title="Budgetary impact by year",
64+
xaxis_title="Year",
65+
yaxis_title="Budgetary impact (£ billions)",
66+
)
67+
68+
return format_fig(fig)

policyengine/outputs/macro/comparison/local_areas/parliamentary_constituencies.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66

77

88
def parliamentary_constituencies(
9-
simulation: Simulation,
10-
chart: bool = False,
11-
variable: str = None,
12-
aggregator: str = None,
13-
relative: bool = None,
14-
) -> dict:
9+
simulation: Simulation,
10+
chart: bool = False,
11+
variable: str = None,
12+
aggregator: str = None,
13+
relative: bool = None,
14+
) -> dict:
1515
if not simulation.options.get("include_constituencies"):
1616
return {}
1717

@@ -45,9 +45,11 @@ def parliamentary_constituencies(
4545
return result
4646

4747

48-
4948
def heatmap(
5049
simulation: Simulation,
50+
variable: str = None,
51+
aggregator: str = None,
52+
relative: bool = None,
5153
) -> dict:
5254
if not simulation.options.get("include_constituencies"):
5355
return {}

policyengine/outputs/macro/comparison/local_areas/parliamentary_constituencies/data.py

Lines changed: 0 additions & 27 deletions
This file was deleted.

0 commit comments

Comments
 (0)