Plot bar chart with separate color legend – dash Plotly
Question:
I’m trying to plot certain values using dash as a bar chart but use a separate column to map the colors. Using below, I’m plotting dates as a bar-chart that corresponds to a drop down bar.
Is it possible to keep the dates as the x-axis but use DOW (day of the week) as a discrete color map? I’ll attach the current output below. The dates get plotted but there are a few issues.
-
The string formatting for each date in the dropdown isn’t in date time
-
The color map isn’t sequenced to the day of the week
import dash
from dash import dcc
from dash import html
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc
import plotly.express as px
import plotly.graph_objs as go
import pandas as pd
from datetime import datetime as dt
df = pd.DataFrame({
'Type': ['A','B','B','B','C','C','D','E','E','E','E','F','F','F'],
})
N = 30
df = pd.concat([df] * N, ignore_index=True)
df['TIMESTAMP'] = pd.date_range(start='2022/01/01 07:30', end='2022/01/30 08:30', periods=len(df))
df['TIMESTAMP'] = pd.to_datetime(df['TIMESTAMP'], dayfirst = True).sort_values()
df['DATE'], df['TIME'] = zip(*[(d.date(), d.time()) for d in df['TIMESTAMP']])
df['DATE'] = pd.to_datetime(df['DATE'])
df = df.sort_values(by = 'DATE')
df['DOW'] = df['DATE'].dt.weekday
df = df.sort_values('DOW').reset_index(drop=True)
df['DOW'] = df['DATE'].dt.day_name()
external_stylesheets = [dbc.themes.SPACELAB, dbc.icons.BOOTSTRAP]
app = dash.Dash(__name__, external_stylesheets = external_stylesheets)
filter_box = html.Div(children=[
html.Div(children=[
html.Label('Day of the week:', style={'paddingTop': '2rem'}),
dcc.Dropdown(
id='DATE',
options=[
{'label': x, 'value': x} for x in df['DATE'].unique()
],
value=df['DATE'].unique(),
multi=True
),
], className="four columns",
style={'padding':'2rem', 'margin':'1rem'} )
])
app.layout = dbc.Container([
dbc.Row([
dbc.Col(html.Div(filter_box, className="bg-secondary h-100"), width=2),
dbc.Col([
dbc.Row([
dbc.Col(dcc.Graph(id = 'date-bar-chart'),
),
]),
], width=5),
])
], fluid=True)
@app.callback(
Output('date-bar-chart', 'figure'),
[Input("DATE", "value"),
])
def date_chart(date):
dff = df[df['DATE'].isin(date)]
count = dff['DATE'].value_counts()
data = px.bar(x = count.index,
y = count.values,
#color = dff['DOW'],
)
layout = go.Layout(title = 'Date')
fig = go.Figure(data = data, layout = layout)
return fig
if __name__ == '__main__':
app.run_server(debug=True, port = 8051)
Answers:
Not sure why do you want to use DATE
as Dropdown but I think you should change it to string and then pass it to dcc.Dropdown
. Used the previous way to add color_discrete_map
I think you can revise your code as below:
import dash
from dash import dcc
from dash import html
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc
import plotly.express as px
import plotly.graph_objs as go
import pandas as pd
from datetime import datetime as dt
from dash.exceptions import PreventUpdate
df = pd.DataFrame({
'Type': ['A','B','B','B','C','C','D','E','E','E','E','F','F','F'],
})
N = 30
df = pd.concat([df] * N, ignore_index=True)
df['TIMESTAMP'] = pd.date_range(start='2022/01/01 07:30', end='2022/01/30 08:30', periods=len(df))
df['TIMESTAMP'] = pd.to_datetime(df['TIMESTAMP'], dayfirst = True).sort_values()
df['DATE'], df['TIME'] = zip(*[(d.date(), d.time()) for d in df['TIMESTAMP']])
df['DATE'] = pd.to_datetime(df['DATE'],format='%Y-%m-%d')
df = df.sort_values(by = 'DATE')
df['DOW'] = df['DATE'].dt.weekday
df = df.sort_values('DOW').reset_index(drop=True)
df['DOW'] = df['DATE'].dt.day_name()
df['DATE'] = df['DATE'].astype(str)
df['Color'] = df['DOW'].map(dict(zip(df['DOW'].unique(),
px.colors.qualitative.Plotly[:len(df['DOW'].unique())])))
Color = df['Color'].unique()
Category = df['DOW'].unique()
Color = df['Color'].unique()
Category = df['DOW'].unique()
cats = dict(zip(Category,Color))
external_stylesheets = [dbc.themes.SPACELAB, dbc.icons.BOOTSTRAP]
app = dash.Dash(__name__, external_stylesheets = external_stylesheets)
filter_box = html.Div(children=[
html.Div(children=[
html.Label('Day of the week:', style={'paddingTop': '2rem'}),
dcc.Dropdown(
id='DATE',
options=[
{'label': x, 'value': x} for x in df['DATE'].unique()
],
value=df['DATE'].unique(),
multi=True
),
], className="four columns",
style={'padding':'2rem', 'margin':'1rem'} )
])
app.layout = dbc.Container([
dbc.Row([
dbc.Col(html.Div(filter_box, className="bg-secondary h-100"), width=2),
dbc.Col([
dbc.Row([
dbc.Col(dcc.Graph(id = 'date-bar-chart'),
),
]),
], width=5),
])
], fluid=True)
@app.callback(
Output('date-bar-chart', 'figure'),
[Input("DATE", "value"),
])
def date_chart(date):
if date:
dff = df[df['DATE'].isin(date)]
count = dff.groupby(['DATE',"DOW"])['DATE'].count().reset_index(name='counts')
data = px.bar(x = count['DATE'],
y = count['counts'],
color = count['DOW'],
color_discrete_map = cats,
)
layout = go.Layout(title = 'Date')
fig = go.Figure(data = data, layout = layout)
return fig
else:
raise PreventUpdate
if __name__ == '__main__':
app.run_server(debug=False, port = 8051)
I used groupby in callback
to return new dataframe and then use it to make graph. Hope this help.
I’m trying to plot certain values using dash as a bar chart but use a separate column to map the colors. Using below, I’m plotting dates as a bar-chart that corresponds to a drop down bar.
Is it possible to keep the dates as the x-axis but use DOW (day of the week) as a discrete color map? I’ll attach the current output below. The dates get plotted but there are a few issues.
-
The string formatting for each date in the dropdown isn’t in date time
-
The color map isn’t sequenced to the day of the week
import dash from dash import dcc from dash import html from dash.dependencies import Input, Output import dash_bootstrap_components as dbc import plotly.express as px import plotly.graph_objs as go import pandas as pd from datetime import datetime as dt df = pd.DataFrame({ 'Type': ['A','B','B','B','C','C','D','E','E','E','E','F','F','F'], }) N = 30 df = pd.concat([df] * N, ignore_index=True) df['TIMESTAMP'] = pd.date_range(start='2022/01/01 07:30', end='2022/01/30 08:30', periods=len(df)) df['TIMESTAMP'] = pd.to_datetime(df['TIMESTAMP'], dayfirst = True).sort_values() df['DATE'], df['TIME'] = zip(*[(d.date(), d.time()) for d in df['TIMESTAMP']]) df['DATE'] = pd.to_datetime(df['DATE']) df = df.sort_values(by = 'DATE') df['DOW'] = df['DATE'].dt.weekday df = df.sort_values('DOW').reset_index(drop=True) df['DOW'] = df['DATE'].dt.day_name() external_stylesheets = [dbc.themes.SPACELAB, dbc.icons.BOOTSTRAP] app = dash.Dash(__name__, external_stylesheets = external_stylesheets) filter_box = html.Div(children=[ html.Div(children=[ html.Label('Day of the week:', style={'paddingTop': '2rem'}), dcc.Dropdown( id='DATE', options=[ {'label': x, 'value': x} for x in df['DATE'].unique() ], value=df['DATE'].unique(), multi=True ), ], className="four columns", style={'padding':'2rem', 'margin':'1rem'} ) ]) app.layout = dbc.Container([ dbc.Row([ dbc.Col(html.Div(filter_box, className="bg-secondary h-100"), width=2), dbc.Col([ dbc.Row([ dbc.Col(dcc.Graph(id = 'date-bar-chart'), ), ]), ], width=5), ]) ], fluid=True) @app.callback( Output('date-bar-chart', 'figure'), [Input("DATE", "value"), ]) def date_chart(date): dff = df[df['DATE'].isin(date)] count = dff['DATE'].value_counts() data = px.bar(x = count.index, y = count.values, #color = dff['DOW'], ) layout = go.Layout(title = 'Date') fig = go.Figure(data = data, layout = layout) return fig if __name__ == '__main__': app.run_server(debug=True, port = 8051)
Not sure why do you want to use DATE
as Dropdown but I think you should change it to string and then pass it to dcc.Dropdown
. Used the previous way to add color_discrete_map
I think you can revise your code as below:
import dash
from dash import dcc
from dash import html
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc
import plotly.express as px
import plotly.graph_objs as go
import pandas as pd
from datetime import datetime as dt
from dash.exceptions import PreventUpdate
df = pd.DataFrame({
'Type': ['A','B','B','B','C','C','D','E','E','E','E','F','F','F'],
})
N = 30
df = pd.concat([df] * N, ignore_index=True)
df['TIMESTAMP'] = pd.date_range(start='2022/01/01 07:30', end='2022/01/30 08:30', periods=len(df))
df['TIMESTAMP'] = pd.to_datetime(df['TIMESTAMP'], dayfirst = True).sort_values()
df['DATE'], df['TIME'] = zip(*[(d.date(), d.time()) for d in df['TIMESTAMP']])
df['DATE'] = pd.to_datetime(df['DATE'],format='%Y-%m-%d')
df = df.sort_values(by = 'DATE')
df['DOW'] = df['DATE'].dt.weekday
df = df.sort_values('DOW').reset_index(drop=True)
df['DOW'] = df['DATE'].dt.day_name()
df['DATE'] = df['DATE'].astype(str)
df['Color'] = df['DOW'].map(dict(zip(df['DOW'].unique(),
px.colors.qualitative.Plotly[:len(df['DOW'].unique())])))
Color = df['Color'].unique()
Category = df['DOW'].unique()
Color = df['Color'].unique()
Category = df['DOW'].unique()
cats = dict(zip(Category,Color))
external_stylesheets = [dbc.themes.SPACELAB, dbc.icons.BOOTSTRAP]
app = dash.Dash(__name__, external_stylesheets = external_stylesheets)
filter_box = html.Div(children=[
html.Div(children=[
html.Label('Day of the week:', style={'paddingTop': '2rem'}),
dcc.Dropdown(
id='DATE',
options=[
{'label': x, 'value': x} for x in df['DATE'].unique()
],
value=df['DATE'].unique(),
multi=True
),
], className="four columns",
style={'padding':'2rem', 'margin':'1rem'} )
])
app.layout = dbc.Container([
dbc.Row([
dbc.Col(html.Div(filter_box, className="bg-secondary h-100"), width=2),
dbc.Col([
dbc.Row([
dbc.Col(dcc.Graph(id = 'date-bar-chart'),
),
]),
], width=5),
])
], fluid=True)
@app.callback(
Output('date-bar-chart', 'figure'),
[Input("DATE", "value"),
])
def date_chart(date):
if date:
dff = df[df['DATE'].isin(date)]
count = dff.groupby(['DATE',"DOW"])['DATE'].count().reset_index(name='counts')
data = px.bar(x = count['DATE'],
y = count['counts'],
color = count['DOW'],
color_discrete_map = cats,
)
layout = go.Layout(title = 'Date')
fig = go.Figure(data = data, layout = layout)
return fig
else:
raise PreventUpdate
if __name__ == '__main__':
app.run_server(debug=False, port = 8051)
I used groupby in callback
to return new dataframe and then use it to make graph. Hope this help.