Spaces:
Running
Running
"""Contains custom components and charts used inside the dashboard.""" | |
from typing import List, Literal, Optional | |
import pandas as pd | |
import plotly.graph_objects as go | |
import vizro.models as vm | |
import vizro.plotly.express as px | |
from dash import html | |
from vizro.models.types import capture | |
# CUSTOM COMPONENTS ------------------------------------------------------------- | |
class FlexContainer(vm.Container): | |
"""Custom flex `Container`.""" | |
type: Literal["flex_container"] = "flex_container" | |
title: str = None # Title exists in vm.Container but we don't want to use it here. | |
classname: str = "d-flex" | |
def build(self): | |
"""Returns a flex container.""" | |
return html.Div( | |
id=self.id, children=[component.build() for component in self.components], className=self.classname | |
) | |
# CUSTOM CHARTS ---------------------------------------------------------------- | |
def bar( | |
x: str, | |
y: str, | |
data_frame: pd.DataFrame, | |
top_n: int = 15, | |
custom_data: Optional[List[str]] = None, | |
): | |
"""Custom bar chart implementation. | |
Based on [px.bar](https://plotly.com/python-api-reference/generated/plotly.express.bar). | |
""" | |
df_agg = data_frame.groupby(y).agg({x: "count"}).sort_values(by=x, ascending=False).reset_index() | |
fig = px.bar( | |
data_frame=df_agg.head(top_n), | |
x=x, | |
y=y, | |
orientation="h", | |
text=x, | |
color_discrete_sequence=["#1A85FF"], | |
custom_data=custom_data, | |
) | |
fig.update_layout(xaxis_title="# of Complaints", yaxis={"title": "", "autorange": "reversed"}) | |
return fig | |
def area(x: str, y: str, data_frame: pd.DataFrame): | |
"""Custom chart to create unstacked area chart. | |
Based on [go.Scatter](https://plotly.com/python-api-reference/generated/plotly.graph_objects.Scatter.html). | |
""" | |
df_agg = data_frame.groupby(["Year", "Month"]).agg({y: "count"}).reset_index() | |
df_agg_2019 = df_agg[df_agg["Year"] == "2018"] | |
df_agg_2020 = df_agg[df_agg["Year"] == "2019"] | |
fig = go.Figure() | |
fig.add_trace( | |
go.Scatter(x=df_agg_2020[x], y=df_agg_2020[y], fill="tozeroy", name="2019", marker={"color": "#1a85ff"}) | |
) | |
fig.add_trace(go.Scatter(x=df_agg_2019[x], y=df_agg_2019[y], fill="tonexty", name="2018", marker={"color": "grey"})) | |
fig.update_layout( | |
title="Complaints over time", | |
xaxis_title="Date Received", | |
yaxis_title="# of Complaints", | |
title_pad_t=4, | |
xaxis={ | |
"showgrid": False, | |
"tickmode": "array", | |
"tickvals": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], | |
"ticktext": ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], | |
}, | |
) | |
return fig | |
def pie( | |
names: str, | |
values: str, | |
data_frame: pd.DataFrame = None, | |
title: Optional[str] = None, | |
): | |
"""Custom pie chart implementation. | |
Based on [px.pie](https://plotly.com/python-api-reference/generated/plotly.express.pie). | |
""" | |
df_agg = data_frame.groupby(names).agg({values: "count"}).reset_index() | |
fig = px.pie( | |
data_frame=df_agg, | |
names=names, | |
values=values, | |
color=names, | |
color_discrete_map={ | |
"Closed with explanation": "#1a85ff", | |
"Closed with monetary relief": "#d41159", | |
"Closed with non-monetary relief": "#adbedc", | |
"Closed without relief": "#7ea1ee", | |
"Closed with relief": "#df658c", | |
"Closed": "#1a85ff", | |
}, | |
title=title, | |
hole=0.4, | |
) | |
fig.update_layout(legend_x=1, legend_y=1, title_pad_t=2, margin={"l": 0, "r": 0, "t": 60, "b": 0}) | |
return fig | |
def choropleth( | |
locations: str, | |
color: str, | |
data_frame: pd.DataFrame = None, | |
title: Optional[str] = None, | |
custom_data: Optional[List[str]] = None, | |
): | |
"""Custom choropleth implementation. | |
Based on [px.choropleth](https://plotly.com/python-api-reference/generated/plotly.express.choropleth). | |
""" | |
df_agg = data_frame.groupby(locations).agg({color: "count"}).reset_index() | |
fig = px.choropleth( | |
data_frame=df_agg, | |
locations=locations, | |
color=color, | |
color_continuous_scale=[ | |
"#ded6d8", | |
"#f3bdcb", | |
"#f7a9be", | |
"#f894b1", | |
"#f780a3", | |
"#f46b94", | |
"#ee517f", | |
"#e94777", | |
"#e43d70", | |
"#df3168", | |
"#d92460", | |
"#d41159", | |
], | |
scope="usa", | |
locationmode="USA-states", | |
title=title, | |
custom_data=custom_data, | |
) | |
fig.update_coloraxes(colorbar={"thickness": 10, "title": {"side": "bottom"}, "orientation": "h", "x": 0.5, "y": 0}) | |
return fig | |
# TABLE CONFIGURATIONS --------------------------------------------------------- | |
CELL_STYLE = { | |
"styleConditions": [ | |
{ | |
"condition": "params.value == 'Closed with explanation'", | |
"style": {"backgroundColor": "#1a85ff"}, | |
}, | |
{ | |
"condition": "params.value == 'Closed with monetary relief'", | |
"style": {"backgroundColor": "#d41159"}, | |
}, | |
{ | |
"condition": "params.value == 'Closed with non-monetary relief'", | |
"style": {"backgroundColor": "#adbedc"}, | |
}, | |
{ | |
"condition": "params.value == 'Closed without relief'", | |
"style": {"backgroundColor": "#7ea1ee"}, | |
}, | |
{ | |
"condition": "params.value == 'Closed with relief'", | |
"style": {"backgroundColor": "#df658c"}, | |
}, | |
{ | |
"condition": "params.value == 'Closed'", | |
"style": {"backgroundColor": "#1a85ff"}, | |
}, | |
] | |
} | |
COLUMN_DEFS = [ | |
{"field": "Complaint ID", "cellDataType": "text", "headerName": "ID", "flex": 3}, | |
{"field": "Date Received", "cellDataType": "text", "headerName": "Date", "flex": 3}, | |
{"field": "Channel", "cellDataType": "text", "flex": 3}, | |
{"field": "State", "cellDataType": "text", "flex": 2}, | |
{"field": "Product", "cellDataType": "text", "flex": 5}, | |
{"field": "Issue", "cellDataType": "text", "flex": 5}, | |
{ | |
"field": "Company response - detailed", | |
"cellDataType": "text", | |
"cellStyle": CELL_STYLE, | |
"headerName": "Company response", | |
"flex": 6, | |
}, | |
{"field": "Timely response?", "cellRenderer": "markdown", "headerName": "On time?", "flex": 3}, | |
] | |