antonymilne Claude commited on
Commit
82a8080
·
1 Parent(s): a4e7aa2

Reorder chart functions by usage order in app.py

Browse files

- Move helper functions (_aggregate_data, _relabel_years,
_create_year_comparison_bar_chart) to top after constants
- Reorder chart functions to match their actual usage in app.py:
1. overview_by_month
2. overview_by_order_status
3. overview_by_region
4. overview_by_customer_segment
5. overview_by_product_category
6. create_map_bubble_new
7. bar_chart_top_n
8. bar_chart_by_category
9. scatter_with_quadrants
10. pareto_customers_chart
- Improves code readability and maintainability

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

Files changed (2) hide show
  1. app.py +1 -1
  2. charts.py +154 -172
app.py CHANGED
@@ -113,7 +113,7 @@ overview_page = vm.Page(
113
  id="metric",
114
  selector=vm.RadioItems(options=["Sales", "Profit", "Order ID", "Customer ID"]),
115
  targets=[
116
- "region_bar_chart.value_col",
117
  "category_bar_chart.column",
118
  "order_status_pie_chart.column",
119
  "month_line_chart.column",
 
113
  id="metric",
114
  selector=vm.RadioItems(options=["Sales", "Profit", "Order ID", "Customer ID"]),
115
  targets=[
116
+ "region_bar_chart.column",
117
  "category_bar_chart.column",
118
  "order_status_pie_chart.column",
119
  "month_line_chart.column",
charts.py CHANGED
@@ -1,5 +1,7 @@
1
  """Collection of custom charts."""
2
 
 
 
3
  import pandas as pd
4
  import plotly.graph_objects as go
5
  import vizro.plotly.express as px
@@ -13,6 +15,8 @@ PRIMARY_COLOR = "#2251ff"
13
  SECONDARY_COLOR = "#A0A2A8"
14
  ORANGE_COLOR = "#f6c343"
15
  GREEN_COLOR = "#60c96c"
 
 
16
  RED_COLOR = "#f17e7e"
17
  DIVERGING_RED_GREEN = [
18
  "#a84545",
@@ -38,35 +42,136 @@ DIVERGING_RED_BLUE = [
38
  ]
39
 
40
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  @capture("graph")
42
- def bar_chart_by_category(data_frame, custom_data):
43
- """Custom bar chart made with Plotly."""
44
- if not data_frame["Category"].eq(data_frame["Category"].iloc[0]).all():
45
- x = "Category"
46
- else:
47
- x = "Sub-Category"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
  fig = px.bar(
50
- data_frame,
51
- x=x,
52
- y="Sales",
53
- title=f"Sales | By {x} <br><sup> 💡 Click on the category to drill-down to sub-category. "
54
- f"Reset by using reset button next to the theme switch.</sup>",
55
- custom_data=custom_data,
56
  color_discrete_sequence=[PRIMARY_COLOR],
57
  )
58
 
 
 
 
 
 
 
 
 
 
 
 
59
  fig.update_layout(
60
- yaxis={"visible": False},
61
- showlegend=False,
62
- bargap=0.6,
63
  xaxis_title=None,
64
  yaxis_title=None,
 
 
65
  )
66
 
 
 
 
67
  return fig
68
 
69
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  @capture("graph")
71
  def create_map_bubble_new(data_frame, custom_data, value_col="Sales"):
72
  """Custom map chart made with Plotly."""
@@ -112,152 +217,52 @@ def create_map_bubble_new(data_frame, custom_data, value_col="Sales"):
112
 
113
 
114
  @capture("graph")
115
- def overview_by_region(data_frame, value_col="Sales"):
116
- """Create a lollipop chart showing aggregated metrics by region using Plotly."""
117
- # --- Aggregate based on chosen metric ---
118
- if value_col == "Order ID":
119
- region_metric = (
120
- data_frame.groupby("Region", as_index=False)["Order ID"].nunique().rename(columns={"Order ID": "Orders"})
121
- )
122
- agg_col = "Orders"
123
- elif value_col == "Customer ID":
124
- region_metric = (
125
- data_frame.groupby("Region", as_index=False)["Customer ID"]
126
- .nunique()
127
- .rename(columns={"Customer ID": "Customers"})
128
- )
129
- agg_col = "Customers"
130
- else:
131
- region_metric = (
132
- data_frame.groupby("Region", as_index=False)[value_col].sum().rename(columns={value_col: value_col})
133
- )
134
- agg_col = value_col
135
-
136
- # --- Sort regions for visual clarity ---
137
- region_metric = region_metric.sort_values(by=agg_col, ascending=True)
138
-
139
- fig = go.Figure()
140
-
141
- fig.add_trace(
142
- go.Bar(
143
- x=region_metric[agg_col],
144
- y=region_metric["Region"],
145
- showlegend=False,
146
- hoverinfo="skip",
147
- orientation="h",
148
- marker={"color": PRIMARY_COLOR},
149
- )
150
- )
151
-
152
- fig.add_trace(
153
- go.Scatter(
154
- x=region_metric[agg_col],
155
- y=region_metric["Region"],
156
- mode="markers",
157
- marker={"size": 14, "color": PRIMARY_COLOR, "line": {"color": PRIMARY_COLOR, "width": 1.5}},
158
- showlegend=False,
159
- )
160
- )
161
-
162
- fig.update_layout(
163
- title=f"{agg_col} | By Region",
164
- xaxis_title=None,
165
- yaxis_title=None,
166
- bargap=0.8,
167
- )
168
-
169
- return fig
170
-
171
-
172
- def _aggregate_data(data_frame, group_columns, column):
173
- """Aggregate data using the appropriate function for the column."""
174
- return data_frame.groupby(group_columns, as_index=False).agg({column: COLUMN_TO_AGGFUNC[column]})
175
-
176
-
177
- # TODO: move to data_processing?
178
- def _relabel_years(data_frame):
179
- """Relabel years for human-readable plotting."""
180
- data_frame["Year"] = data_frame["Year"].map({THIS_YEAR: "This year", LAST_YEAR: "Last year"})
181
- return data_frame
182
-
183
 
184
- def _create_year_comparison_bar_chart(data_frame, group_column, column, title_suffix):
185
- """Helper function to create a grouped bar chart comparing years."""
186
- grouped_df = _aggregate_data(data_frame, [group_column, "Year"], column)
187
- grouped_df = _relabel_years(grouped_df)
188
 
 
189
  fig = px.bar(
190
- grouped_df,
191
- x=group_column,
192
- y=column,
193
- color="Year",
194
- barmode="group",
195
- title=f"{COLUMN_TO_METRIC[column]} | {title_suffix}",
196
- color_discrete_map={"This year": PRIMARY_COLOR, "Last year": SECONDARY_COLOR},
197
  )
198
 
199
- fig.update_layout(xaxis_title=None, yaxis_title=None, bargap=0.4, showlegend=False)
200
-
201
- return fig
202
-
203
-
204
- @capture("graph")
205
- def overview_by_customer_segment(data_frame, column="Sales"):
206
- """Bar chart comparing current year vs previous year by customer segment."""
207
- return _create_year_comparison_bar_chart(data_frame, "Segment", column, "By Customer Segment")
208
-
209
-
210
- @capture("graph")
211
- def overview_by_product_category(data_frame, column="Sales"):
212
- """Bar chart comparing current year vs previous year by category."""
213
- return _create_year_comparison_bar_chart(data_frame, "Category", column, "By Product Category")
214
-
215
-
216
- import calendar
217
-
218
-
219
- @capture("graph")
220
- def overview_by_month(data_frame, column="Sales"):
221
- grouped_df = _aggregate_data(data_frame, ["Year", "Month"], column)
222
- # Relabel for plotting in human-readable way.
223
- grouped_df["Month"] = grouped_df["Month"].map({i: calendar.month_abbr[i] for i in range(1, 13)})
224
- grouped_df = _relabel_years(grouped_df)
225
-
226
- fig = px.line(
227
- grouped_df,
228
- x="Month",
229
- color="Year",
230
- y=column,
231
- markers=True,
232
- title=f"{COLUMN_TO_METRIC[column]} | By Month",
233
- color_discrete_map={"This year": PRIMARY_COLOR, "Last year": SECONDARY_COLOR},
234
- )
235
- fig.data[1].update(line_width=2, fill="tozeroy")
236
- fig.update_layout(
237
- xaxis={"showgrid": False, "title": None, "range": [-0.1, 11.1]},
238
- yaxis_title=None,
239
- title=f"{column} | By Month",
240
- legend={"yanchor": "top", "y": 1.2, "xanchor": "right", "x": 1, "title": None},
241
- )
242
  return fig
243
 
244
 
245
  @capture("graph")
246
- def overview_by_order_status(data_frame, column="Sales"):
247
- grouped_df = _aggregate_data(data_frame, "Order Status", column)
 
 
 
 
248
 
249
- fig = px.pie(
250
- grouped_df,
251
- names="Order Status",
252
- values=column,
253
- color="Order Status",
254
- title=f"{column} | By Order Status",
255
- color_discrete_map={"In Transit": PRIMARY_COLOR, "Processing": ORANGE_COLOR, "Delivered": GREEN_COLOR},
256
- hole=0.6,
257
  )
258
 
259
  fig.update_layout(
260
- legend={"yanchor": "bottom", "y": -0.2, "xanchor": "right", "orientation": "v"},
 
 
 
 
261
  )
262
 
263
  return fig
@@ -450,27 +455,4 @@ def pareto_customers_chart(data_frame, value_col="Sales", highlight_customer=Non
450
  xaxis={"range": [0, 105]},
451
  )
452
 
453
- return fig
454
-
455
-
456
- @capture("graph")
457
- def bar_chart_top_n(data_frame, x="Sales", y="City", n=10):
458
- """Generic bar chart to show top N by any dimension."""
459
- # TODO: Needs to be fixed when prefiltered on city. Otherwise, top N is not properly recalculated
460
- # after clicking on a state.
461
- df_top = data_frame.groupby(y).agg({x: "sum"}).sort_values(x, ascending=False).head(n).reset_index()
462
-
463
- # Sort ascending so highest appears at top in horizontal bar chart
464
- df_top = df_top.sort_values(x, ascending=True)
465
-
466
- # Create bar chart
467
- fig = px.bar(
468
- df_top,
469
- x=x,
470
- y=y,
471
- orientation="h",
472
- color_discrete_sequence=[PRIMARY_COLOR],
473
- title=f"Top {n} {y} by {x}",
474
- )
475
-
476
- return fig
 
1
  """Collection of custom charts."""
2
 
3
+ import calendar
4
+
5
  import pandas as pd
6
  import plotly.graph_objects as go
7
  import vizro.plotly.express as px
 
15
  SECONDARY_COLOR = "#A0A2A8"
16
  ORANGE_COLOR = "#f6c343"
17
  GREEN_COLOR = "#60c96c"
18
+
19
+ YEAR_COLOR_MAP = {"This year": PRIMARY_COLOR, "Last year": SECONDARY_COLOR}
20
  RED_COLOR = "#f17e7e"
21
  DIVERGING_RED_GREEN = [
22
  "#a84545",
 
42
  ]
43
 
44
 
45
+ # Helper functions
46
+ def _aggregate_data(data_frame, group_columns, column):
47
+ """Aggregate data using the appropriate function for the column."""
48
+ return data_frame.groupby(group_columns, as_index=False).agg({column: COLUMN_TO_AGGFUNC[column]})
49
+
50
+
51
+ # TODO: move to data_processing?
52
+ def _relabel_years(data_frame):
53
+ """Relabel years for human-readable plotting."""
54
+ data_frame["Year"] = data_frame["Year"].map({THIS_YEAR: "This year", LAST_YEAR: "Last year"})
55
+ return data_frame
56
+
57
+
58
+ def _create_year_comparison_bar_chart(data_frame, group_column, column, title_suffix):
59
+ """Helper function to create a grouped bar chart comparing years."""
60
+ grouped_df = _aggregate_data(data_frame, [group_column, "Year"], column)
61
+ grouped_df = _relabel_years(grouped_df)
62
+
63
+ fig = px.bar(
64
+ grouped_df,
65
+ x=group_column,
66
+ y=column,
67
+ color="Year",
68
+ barmode="group",
69
+ title=f"{COLUMN_TO_METRIC[column]} | {title_suffix}",
70
+ color_discrete_map=YEAR_COLOR_MAP,
71
+ )
72
+
73
+ fig.update_layout(xaxis_title=None, yaxis_title=None, bargap=0.4, showlegend=False)
74
+
75
+ return fig
76
+
77
+
78
+ # Chart functions in order of usage in app.py
79
  @capture("graph")
80
+ def overview_by_month(data_frame, column="Sales"):
81
+ grouped_df = _aggregate_data(data_frame, ["Year", "Month"], column)
82
+ # Relabel for plotting in human-readable way.
83
+ grouped_df["Month"] = grouped_df["Month"].map({i: calendar.month_abbr[i] for i in range(1, 13)})
84
+ grouped_df = _relabel_years(grouped_df)
85
+
86
+ fig = px.line(
87
+ grouped_df,
88
+ x="Month",
89
+ color="Year",
90
+ y=column,
91
+ markers=True,
92
+ title=f"{COLUMN_TO_METRIC[column]} | By Month",
93
+ color_discrete_map=YEAR_COLOR_MAP,
94
+ )
95
+ fig.data[1].update(line_width=2, fill="tozeroy")
96
+ fig.update_layout(
97
+ xaxis={"showgrid": False, "title": None, "range": [-0.1, 11.1]},
98
+ yaxis_title=None,
99
+ legend={"yanchor": "top", "y": 1.2, "xanchor": "right", "x": 1, "title": None},
100
+ )
101
+ return fig
102
+
103
+
104
+ @capture("graph")
105
+ def overview_by_order_status(data_frame, column="Sales"):
106
+ grouped_df = _aggregate_data(data_frame, "Order Status", column)
107
+
108
+ fig = px.pie(
109
+ grouped_df,
110
+ names="Order Status",
111
+ values=column,
112
+ color="Order Status",
113
+ title=f"{COLUMN_TO_METRIC[column]} | By Order Status",
114
+ color_discrete_map={"In Transit": PRIMARY_COLOR, "Processing": ORANGE_COLOR, "Delivered": GREEN_COLOR},
115
+ hole=0.6,
116
+ )
117
+
118
+ fig.update_layout(
119
+ legend={"yanchor": "bottom", "y": -0.2, "xanchor": "right", "orientation": "v"},
120
+ )
121
+
122
+ return fig
123
+
124
+
125
+ @capture("graph")
126
+ def overview_by_region(data_frame, column="Sales"):
127
+ grouped_df = _aggregate_data(data_frame, "Region", column)
128
+ grouped_df = grouped_df.sort_values(ascending=True)
129
 
130
  fig = px.bar(
131
+ grouped_df,
132
+ x=column,
133
+ y="Region",
134
+ orientation="h",
135
+ title=f"{COLUMN_TO_METRIC[column]} | By Region",
 
136
  color_discrete_sequence=[PRIMARY_COLOR],
137
  )
138
 
139
+ # Add scatter markers on top to create lollipop effect
140
+ fig.add_trace(
141
+ go.Scatter(
142
+ x=grouped_df[column],
143
+ y=grouped_df["Region"],
144
+ mode="markers",
145
+ marker={"size": 14, "color": PRIMARY_COLOR, "line": {"color": PRIMARY_COLOR, "width": 1.5}},
146
+ showlegend=False,
147
+ )
148
+ )
149
+
150
  fig.update_layout(
 
 
 
151
  xaxis_title=None,
152
  yaxis_title=None,
153
+ bargap=0.8,
154
+ showlegend=False,
155
  )
156
 
157
+ # Make the bars appear thinner for lollipop effect
158
+ fig.update_traces(hoverinfo="skip", selector={"type": "bar"})
159
+
160
  return fig
161
 
162
 
163
+ @capture("graph")
164
+ def overview_by_customer_segment(data_frame, column="Sales"):
165
+ """Bar chart comparing current year vs previous year by customer segment."""
166
+ return _create_year_comparison_bar_chart(data_frame, "Segment", column, "By Customer Segment")
167
+
168
+
169
+ @capture("graph")
170
+ def overview_by_product_category(data_frame, column="Sales"):
171
+ """Bar chart comparing current year vs previous year by category."""
172
+ return _create_year_comparison_bar_chart(data_frame, "Category", column, "By Product Category")
173
+
174
+
175
  @capture("graph")
176
  def create_map_bubble_new(data_frame, custom_data, value_col="Sales"):
177
  """Custom map chart made with Plotly."""
 
217
 
218
 
219
  @capture("graph")
220
+ def bar_chart_top_n(data_frame, x="Sales", y="City", n=10):
221
+ """Generic bar chart to show top N by any dimension."""
222
+ # TODO: Needs to be fixed when prefiltered on city. Otherwise, top N is not properly recalculated
223
+ # after clicking on a state.
224
+ df_top = data_frame.groupby(y).agg({x: "sum"}).sort_values(x, ascending=False).head(n).reset_index()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
225
 
226
+ # Sort ascending so highest appears at top in horizontal bar chart
227
+ df_top = df_top.sort_values(x, ascending=True)
 
 
228
 
229
+ # Create bar chart
230
  fig = px.bar(
231
+ df_top,
232
+ x=x,
233
+ y=y,
234
+ orientation="h",
235
+ color_discrete_sequence=[PRIMARY_COLOR],
236
+ title=f"Top {n} {y} by {x}",
 
237
  )
238
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
239
  return fig
240
 
241
 
242
  @capture("graph")
243
+ def bar_chart_by_category(data_frame, custom_data):
244
+ """Custom bar chart made with Plotly."""
245
+ if not data_frame["Category"].eq(data_frame["Category"].iloc[0]).all():
246
+ x = "Category"
247
+ else:
248
+ x = "Sub-Category"
249
 
250
+ fig = px.bar(
251
+ data_frame,
252
+ x=x,
253
+ y="Sales",
254
+ title=f"Sales | By {x} <br><sup> 💡 Click on the category to drill-down to sub-category. "
255
+ f"Reset by using reset button next to the theme switch.</sup>",
256
+ custom_data=custom_data,
257
+ color_discrete_sequence=[PRIMARY_COLOR],
258
  )
259
 
260
  fig.update_layout(
261
+ yaxis={"visible": False},
262
+ showlegend=False,
263
+ bargap=0.6,
264
+ xaxis_title=None,
265
+ yaxis_title=None,
266
  )
267
 
268
  return fig
 
455
  xaxis={"range": [0, 105]},
456
  )
457
 
458
+ return fig