maxschulz-COL commited on
Commit
398985a
·
verified ·
1 Parent(s): d138a11

Create utils/_charts.py

Browse files
Files changed (1) hide show
  1. utils/_charts.py +193 -0
utils/_charts.py ADDED
@@ -0,0 +1,193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Contains custom components and charts used inside the dashboard."""
2
+
3
+ from typing import List, Literal, Optional
4
+
5
+ import dash_bootstrap_components as dbc
6
+ import pandas as pd
7
+ import vizro.models as vm
8
+ import vizro.plotly.express as px
9
+ from dash import html
10
+ from vizro.models.types import capture
11
+
12
+
13
+ # CUSTOM COMPONENTS -------------------------------------------------------------
14
+ # Note: This is a static KPI Card only (it will not be reactive to controls). A new dynamic KPI Card component
15
+ # is currently in development.
16
+ class KPI(vm.VizroBaseModel):
17
+ """Static custom `KPI` Card."""
18
+
19
+ type: Literal["kpi"] = "kpi"
20
+ title: str
21
+ value: str
22
+ icon: str
23
+ sign: Literal["delta-pos", "delta-neg"]
24
+ ref_value: str
25
+
26
+ def build(self):
27
+ return dbc.Card(
28
+ [
29
+ html.H4(self.title),
30
+ html.P(self.value),
31
+ html.Span(
32
+ [
33
+ html.Span(self.icon, className="material-symbols-outlined"),
34
+ html.Span(self.ref_value),
35
+ ],
36
+ className=self.sign,
37
+ ),
38
+ ],
39
+ className="kpi-card-ref",
40
+ )
41
+
42
+
43
+ # CUSTOM CHARTS ----------------------------------------------------------------
44
+ @capture("graph")
45
+ def bar(
46
+ x: str,
47
+ y: str,
48
+ data_frame: pd.DataFrame,
49
+ top_n: int = 15,
50
+ custom_data: Optional[List[str]] = None,
51
+ ):
52
+ df_agg = data_frame.groupby(y).agg({x: "count"}).sort_values(by=x, ascending=False).reset_index()
53
+
54
+ fig = px.bar(
55
+ data_frame=df_agg.head(top_n),
56
+ x=x,
57
+ y=y,
58
+ orientation="h",
59
+ text=x,
60
+ color_discrete_sequence=["#1A85FF"],
61
+ custom_data=custom_data,
62
+ )
63
+ fig.update_layout(xaxis_title="# of Complaints", yaxis=dict(title="", autorange="reversed")) # noqa: C408
64
+ return fig
65
+
66
+
67
+ @capture("graph")
68
+ def line(x: str, y: str, data_frame: pd.DataFrame):
69
+ df_agg = data_frame.groupby(x).agg({y: "count"}).reset_index()
70
+ fig = px.area(
71
+ data_frame=df_agg,
72
+ x=x,
73
+ y=y,
74
+ color_discrete_sequence=["#1A85FF"],
75
+ title="Complaints over time",
76
+ )
77
+ fig.update_layout(xaxis_title="Date Received", yaxis_title="# of Complaints", title_pad_t=4)
78
+ return fig
79
+
80
+
81
+ @capture("graph")
82
+ def pie(
83
+ names: str,
84
+ values: str,
85
+ data_frame: pd.DataFrame = None,
86
+ title: Optional[str] = None,
87
+ custom_order: Optional[List[str]] = None,
88
+ ):
89
+ df_agg = data_frame.groupby(names).agg({values: "count"}).reset_index()
90
+
91
+ # Apply custom order so colors are applied correctly to the pie chart
92
+ order_mapping = {category: index for index, category in enumerate(custom_order)}
93
+ df_sorted = df_agg.sort_values(by=names, key=lambda names: names.map(order_mapping))
94
+
95
+ fig = px.pie(
96
+ data_frame=df_sorted,
97
+ names=names,
98
+ values=values,
99
+ color_discrete_sequence=["#1a85ff", "#7ea1ee", "#adbedc", "#df658c", "#d41159"],
100
+ title=title,
101
+ hole=0.4,
102
+ )
103
+
104
+ fig.update_layout(legend_x=1, legend_y=1, title_pad_t=2, margin=dict(l=0, r=0, t=60, b=0)) # noqa: C408
105
+ fig.update_traces(sort=False)
106
+ return fig
107
+
108
+
109
+ @capture("graph")
110
+ def choropleth(
111
+ locations: str,
112
+ color: str,
113
+ data_frame: pd.DataFrame = None,
114
+ title: Optional[str] = None,
115
+ custom_data: Optional[List[str]] = None,
116
+ ):
117
+ df_agg = data_frame.groupby(locations).agg({color: "count"}).reset_index()
118
+
119
+ fig = px.choropleth(
120
+ data_frame=df_agg,
121
+ locations=locations,
122
+ color=color,
123
+ color_continuous_scale=[
124
+ "#ded6d8",
125
+ "#f3bdcb",
126
+ "#f7a9be",
127
+ "#f894b1",
128
+ "#f780a3",
129
+ "#f46b94",
130
+ "#ee517f",
131
+ "#e94777",
132
+ "#e43d70",
133
+ "#df3168",
134
+ "#d92460",
135
+ "#d41159",
136
+ ],
137
+ scope="usa",
138
+ locationmode="USA-states",
139
+ title=title,
140
+ custom_data=custom_data,
141
+ )
142
+
143
+ fig.update_coloraxes(colorbar={"thickness": 10, "title": {"side": "right"}})
144
+ return fig
145
+
146
+
147
+ # TABLE CONFIGURATIONS ---------------------------------------------------------
148
+ CELL_STYLE = {
149
+ "styleConditions": [
150
+ {
151
+ "condition": "params.value == 'Closed with explanation'",
152
+ "style": {"backgroundColor": "#1a85ff"},
153
+ },
154
+ {
155
+ "condition": "params.value == 'Closed with monetary relief'",
156
+ "style": {"backgroundColor": "#d41159"},
157
+ },
158
+ {
159
+ "condition": "params.value == 'Closed with non-monetary relief'",
160
+ "style": {"backgroundColor": "#adbedc"},
161
+ },
162
+ {
163
+ "condition": "params.value == 'Closed without relief'",
164
+ "style": {"backgroundColor": "#7ea1ee"},
165
+ },
166
+ {
167
+ "condition": "params.value == 'Closed with relief'",
168
+ "style": {"backgroundColor": "#df658c"},
169
+ },
170
+ {
171
+ "condition": "params.value == 'Closed'",
172
+ "style": {"backgroundColor": "#1a85ff"},
173
+ },
174
+ ]
175
+ }
176
+
177
+
178
+ COLUMN_DEFS = [
179
+ {"field": "Complaint ID", "cellDataType": "text", "headerName": "ID", "flex": 3},
180
+ {"field": "Date Received", "cellDataType": "text", "headerName": "Date", "flex": 3},
181
+ {"field": "Channel", "cellDataType": "text", "flex": 3},
182
+ {"field": "State", "cellDataType": "text", "flex": 2},
183
+ {"field": "Product", "cellDataType": "text", "flex": 5},
184
+ {"field": "Issue", "cellDataType": "text", "flex": 5},
185
+ {
186
+ "field": "Company response - detailed",
187
+ "cellDataType": "text",
188
+ "cellStyle": CELL_STYLE,
189
+ "headerName": "Company response",
190
+ "flex": 6,
191
+ },
192
+ {"field": "Timely response?", "cellRenderer": "markdown", "headerName": "On time?", "flex": 3},
193
+ ]