Skip to content

Commit cd2d20a

Browse files
Merge pull request #35 from masterismail/PovertyCharts
Poverty charts
2 parents 17c1cf3 + 9884225 commit cd2d20a

File tree

8 files changed

+451
-1
lines changed

8 files changed

+451
-1
lines changed

policyengine/charts/poverty/__init__.py

Whitespace-only changes.

policyengine/charts/poverty/deep/__init__.py

Whitespace-only changes.
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import plotly.graph_objects as go
2+
from policyengine_core.charts.formatting import *
3+
4+
class DeepPovertyByAgeChart:
5+
def __init__(self,country:str, data=None):
6+
if data is None:
7+
raise ValueError("Data must be provided")
8+
9+
self.data = data
10+
11+
def _get_color(self, value):
12+
# All bars should be gray
13+
return GRAY
14+
15+
def _get_change_direction(self, value):
16+
if value > 0:
17+
return "increase"
18+
elif value < 0:
19+
return "decrease"
20+
else:
21+
return "no change"
22+
23+
def ordinal_suffix(self, n):
24+
"""Return the ordinal suffix for an integer."""
25+
if 10 <= n % 100 <= 20:
26+
suffix = 'th'
27+
else:
28+
suffix = {1: 'st', 2: 'nd', 3: 'rd'}.get(n % 10, 'th')
29+
return suffix
30+
31+
def generate_chart_data(self):
32+
categories = list(self.data.keys())
33+
values = [self.data[cat]['change'] for cat in categories]
34+
baselines = [self.data[cat]['baseline'] * 100 for cat in categories]
35+
reforms = [self.data[cat]['reform'] * 100 for cat in categories]
36+
37+
# Generate hover texts with baseline, reform, and percentage change
38+
hover_texts = [
39+
f"This reform would {self._get_change_direction(val)} the percentage of {category.lower()} in poverty by {abs(val):.1f}% from {baseline:.1f}% to {reform:.1f}%"
40+
for category, val, baseline, reform in zip(categories, values, baselines, reforms)
41+
]
42+
43+
fig = go.Figure()
44+
45+
values_in_pct = values # Use percentage values
46+
colors = [self._get_color(value) for value in values]
47+
48+
# Add bar chart with percentage values
49+
fig.add_trace(go.Bar(
50+
x=categories,
51+
y=values_in_pct,
52+
marker=dict(color=colors, line=dict(width=1)),
53+
width=0.6,
54+
text=[f"{abs(value):.1f}%" for value in values], # Display values as percentages
55+
textposition='outside',
56+
hovertemplate="<b>%{x}</b><br><br>%{customdata}<extra></extra>", # Hover shows category
57+
customdata=hover_texts
58+
))
59+
60+
# Update layout to reflect percentage values on y-axis
61+
fig.update_layout(
62+
yaxis=dict(
63+
tickformat=",.1f%%", # Format y-axis as percentages with one decimal place
64+
title="Percentage Change in Poverty"
65+
),
66+
xaxis=dict(
67+
title="Category"
68+
),
69+
hoverlabel=dict(
70+
bgcolor="white",
71+
font=dict(color="black", size=16)
72+
),
73+
title="Change in Poverty Percentage by Category"
74+
)
75+
76+
format_fig(fig) # Keep the formatting logic from policyengine_core
77+
return fig
78+
79+
80+
# # Example data
81+
# data = {
82+
# 'Child': {
83+
# 'Baseline': 0.32427219591395395,
84+
# 'Reform': 0.33392168532001054,
85+
# 'Change': 3.0
86+
# },
87+
# 'Adult': {
88+
# 'Baseline': 0.17427822561729264,
89+
# 'Reform': 0.17757158627182623,
90+
# 'Change': 1.9
91+
# },
92+
# 'Senior': {
93+
# 'Baseline': 0.12817646500651358,
94+
# 'Reform': 0.1370685860340031,
95+
# 'Change': 6.9
96+
# },
97+
# 'All': {
98+
# 'Baseline': 0.19913534734369268,
99+
# 'Reform': 0.20487670454940832,
100+
# 'Change': 2.9
101+
# }
102+
# }
103+
104+
# # Generate chart for all categories
105+
# chart = OverallChart(data=data)
106+
# fig = chart.generate_chart_data()
107+
# fig.show()
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import plotly.graph_objects as go
2+
from policyengine_core.charts.formatting import *
3+
4+
class DeepPovertyByGenderChart:
5+
def __init__(self, country:str,data=None):
6+
if data is None:
7+
raise ValueError("Data must be provided")
8+
9+
self.data = data
10+
11+
def _get_color(self, value):
12+
# All bars should be gray
13+
return GRAY
14+
15+
def _get_change_direction(self, value):
16+
if value > 0:
17+
return "increase"
18+
elif value < 0:
19+
return "decrease"
20+
else:
21+
return "no change"
22+
23+
def ordinal_suffix(self, n):
24+
"""Return the ordinal suffix for an integer."""
25+
if 10 <= n % 100 <= 20:
26+
suffix = 'th'
27+
else:
28+
suffix = {1: 'st', 2: 'nd', 3: 'rd'}.get(n % 10, 'th')
29+
return suffix
30+
31+
def generate_chart_data(self):
32+
categories = list(self.data.keys())
33+
values = [self.data[cat]['change'] for cat in categories]
34+
baselines = [self.data[cat]['baseline'] * 100 for cat in categories]
35+
reforms = [self.data[cat]['reform'] * 100 for cat in categories]
36+
37+
# Generate hover texts with baseline, reform, and percentage change
38+
hover_texts = [
39+
f"This reform would {self._get_change_direction(val)} the percentage of {category.lower()} in poverty by {abs(val):.1f}% from {baseline:.1f}% to {reform:.1f}%"
40+
for category, val, baseline, reform in zip(categories, values, baselines, reforms)
41+
]
42+
43+
fig = go.Figure()
44+
45+
values_in_pct = values # Use percentage values
46+
colors = [self._get_color(value) for value in values]
47+
48+
# Add bar chart with percentage values
49+
fig.add_trace(go.Bar(
50+
x=categories,
51+
y=values_in_pct,
52+
marker=dict(color=colors, line=dict(width=1)),
53+
width=0.6,
54+
text=[f"{abs(value):.1f}%" for value in values], # Display values as percentages
55+
textposition='outside',
56+
hovertemplate="<b>Category %{x}</b><br><br>%{customdata}<extra></extra>", # Hover shows category
57+
customdata=hover_texts
58+
))
59+
60+
# Update layout to reflect percentage values on y-axis
61+
fig.update_layout(
62+
yaxis=dict(
63+
tickformat=",.1f%%",
64+
ticksuffix = "%", # Format y-axis as percentages with one decimal place
65+
title="Percentage Change in Poverty"
66+
),
67+
xaxis=dict(
68+
title="Category"
69+
),
70+
hoverlabel=dict(
71+
bgcolor="white",
72+
font=dict(color="black", size=16)
73+
),
74+
title="Change in Poverty Percentage by Category"
75+
)
76+
77+
format_fig(fig) # Keep the formatting logic from policyengine_core
78+
return fig
79+
80+
81+
# # Example data
82+
# data = {
83+
# 'Male': {
84+
# 'Baseline': 0.18412623468617267,
85+
# 'Reform': 0.18932591339284738,
86+
# 'Change': 2.8
87+
# },
88+
# 'Female': {
89+
# 'Baseline': 0.21377616483057263,
90+
# 'Reform': 0.22004590877186603,
91+
# 'Change': 2.9
92+
# },
93+
# 'All': {
94+
# 'Baseline': 0.19913534734369268,
95+
# 'Reform': 0.20487670454940832,
96+
# 'Change': 2.9
97+
# }
98+
# }
99+
100+
# # Generate chart for all categories
101+
# chart = OverallChart(data=data)
102+
# fig = chart.generate_chart_data()
103+
# fig.show()

policyengine/charts/poverty/regular/__init__.py

Whitespace-only changes.
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import plotly.graph_objects as go
2+
from policyengine_core.charts.formatting import *
3+
4+
class RegularPovertyByAgeChart:
5+
def __init__(self,country:str, data=None):
6+
if data is None:
7+
raise ValueError("Data must be provided")
8+
9+
self.data = data
10+
11+
def _get_color(self, value):
12+
# All bars should be gray
13+
return GRAY
14+
15+
def _get_change_direction(self, value):
16+
if value > 0:
17+
return "increase"
18+
elif value < 0:
19+
return "decrease"
20+
else:
21+
return "no change"
22+
23+
def ordinal_suffix(self, n):
24+
"""Return the ordinal suffix for an integer."""
25+
if 10 <= n % 100 <= 20:
26+
suffix = 'th'
27+
else:
28+
suffix = {1: 'st', 2: 'nd', 3: 'rd'}.get(n % 10, 'th')
29+
return suffix
30+
31+
def generate_chart_data(self):
32+
categories = list(self.data.keys())
33+
values = [self.data[cat]['change'] for cat in categories]
34+
baselines = [self.data[cat]['baseline'] * 100 for cat in categories]
35+
reforms = [self.data[cat]['reform'] * 100 for cat in categories]
36+
37+
# Generate hover texts with baseline, reform, and percentage change
38+
hover_texts = [
39+
f"This reform would {self._get_change_direction(val)} the percentage of {category.lower()} in poverty by {abs(val):.1f}% from {baseline:.1f}% to {reform:.1f}%"
40+
for category, val, baseline, reform in zip(categories, values, baselines, reforms)
41+
]
42+
43+
fig = go.Figure()
44+
45+
values_in_pct = values # Use percentage values
46+
colors = [self._get_color(value) for value in values]
47+
48+
# Add bar chart with percentage values
49+
fig.add_trace(go.Bar(
50+
x=categories,
51+
y=values_in_pct,
52+
marker=dict(color=colors, line=dict(width=1)),
53+
width=0.6,
54+
text=[f"{abs(value):.1f}%" for value in values], # Display values as percentages
55+
textposition='outside',
56+
hovertemplate="<b>%{x}</b><br><br>%{customdata}<extra></extra>", # Hover shows category
57+
customdata=hover_texts
58+
))
59+
60+
# Update layout to reflect percentage values on y-axis
61+
fig.update_layout(
62+
yaxis=dict(
63+
tickformat=",.1f%%", # Format y-axis as percentages with one decimal place
64+
title="Percentage Change in Poverty"
65+
),
66+
xaxis=dict(
67+
title="Category"
68+
),
69+
hoverlabel=dict(
70+
bgcolor="white",
71+
font=dict(color="black", size=16)
72+
),
73+
title="Change in Poverty Percentage by Category"
74+
)
75+
76+
format_fig(fig) # Keep the formatting logic from policyengine_core
77+
return fig
78+
79+
80+
# # Example data
81+
# data = {
82+
# 'Child': {
83+
# 'Baseline': 0.32427219591395395,
84+
# 'Reform': 0.33392168532001054,
85+
# 'Change': 3.0
86+
# },
87+
# 'Adult': {
88+
# 'Baseline': 0.17427822561729264,
89+
# 'Reform': 0.17757158627182623,
90+
# 'Change': 1.9
91+
# },
92+
# 'Senior': {
93+
# 'Baseline': 0.12817646500651358,
94+
# 'Reform': 0.1370685860340031,
95+
# 'Change': 6.9
96+
# },
97+
# 'All': {
98+
# 'Baseline': 0.19913534734369268,
99+
# 'Reform': 0.20487670454940832,
100+
# 'Change': 2.9
101+
# }
102+
# }
103+
104+
# # Generate chart for all categories
105+
# chart = OverallChart(data=data)
106+
# fig = chart.generate_chart_data()
107+
# fig.show()

0 commit comments

Comments
 (0)