demo-kpi / utils /_charts.py
li-nguyen's picture
Add last changes
cd1f702
"""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 ----------------------------------------------------------------
@capture("graph")
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
@capture("graph")
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
@capture("graph")
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
@capture("graph")
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},
]