plotlydependenciesplotly-dashdash-bootstrap-components

Include multiple bar charts with callback interaction - dash


I'm aiming to plot multiple graphs in a dbc.Container that can be manipulated with various filtering controls. The following app layout includes a DatePickerRange for dates, Dropdown for days of the week, and a Checklist for unique values.

I aiming to plot two distinct bar charts that use DATE and Type columns. I'm also attempting to link both figures to ALL filtering options so any alteration will influence both bar charts at the same time.

I've attempted to achieve this using separate callbacks but only DATE bar chart is plotted. The filtering options currently has no interactivity.

from dash import dcc
from dash import html
import dash_bootstrap_components as dbc
import dash
from dash.dependencies import Input, Output, State
from datetime import datetime
import plotly.graph_objs as go
import plotly.express as px
import pandas as pd
import numpy as np
from datetime import datetime as dt

df = pd.DataFrame({
           'Type': ['A','B','B','C','D','D','E','E','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['TIMESTAMP'] = df['TIMESTAMP'].dt.floor('1min')

df['DATE'], df['TIME'] = zip(*[(d.date(), d.time()) for d in df['TIMESTAMP']])

df['DATE'] = pd.to_datetime(df['DATE'])

df['YEAR'] = df['DATE'].dt.year

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=[
    ################### Filter box ###################### 
    html.Div(children=[
        html.Label('Filter by date (D-M-Y):'),
        dcc.DatePickerRange(
            id='input_date',
            month_format='DD/MM/YYYY',
            show_outside_days=True,
            minimum_nights=0,
            initial_visible_month=dt(2022, 1, 1),
            min_date_allowed=dt(2016, 1, 1),
            max_date_allowed=dt(2022, 12, 31),
            start_date=dt.strptime("2022-01-01", "%Y-%m-%d").date(),
            end_date=dt.strptime("2022-12-31", "%Y-%m-%d").date()
        ),

        html.Label('Day of the week:', style={'paddingTop': '2rem'}),
        dcc.Dropdown(
            id='DOW',
            options=[
                {'label': 'Sun', 'value': '1'},
                {'label': 'Mon', 'value': '2'},
                {'label': 'Tue', 'value': '3'},
                {'label': 'Wed', 'value': '4'},
                {'label': 'Thurs', 'value': '5'},
                {'label': 'Fri', 'value': '6'},
                {'label': 'Sat', 'value': '7'}
            ],
            value=['1', '2', '3', '4', '5', '6', '7'],
            multi=True
        ),

        html.Label('Type:', style={'paddingTop': '2rem', 'display': 'inline-block'}),
        dcc.Checklist(
            id='Type',
            options=[
                {'label': 'A', 'value': '1'},
                {'label': 'B', 'value': '2'},
                {'label': 'C', 'value': '3'},
                {'label': 'D', 'value': '4'},
                {'label': 'E', 'value': '5'},
                {'label': 'F', 'value': '6'},
            ],
            value=['1', '2', '3', '4', '5', '6'],
        ),

    ], className="four columns",
    style={'padding':'2rem', 'margin':'1rem'} )

])

success_card = dbc.Card(
    [
        dbc.CardHeader("Some header"),
        dbc.CardBody(
            [
                html.H6("Type Count"),
                html.Div(id='count_of_types', style={"color": "Purple"})
            ]
        ),
    ],
    className='text-center m-4'
)

failure_card = dbc.Card(
    [
        dbc.CardHeader("Some header"),
        dbc.CardBody(
            [
                html.H6("Date Count"),
                html.Div(id='count_of_dates', style={"color": "Black"})
            ]
        ),
    ],
    className='text-center m-4'
)

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(success_card),
            ]),
            dbc.Row([
                dbc.Col(dcc.Graph(id = 'date-bar-chart'), style={
                        "padding-bottom": "10px",
                    },),
            ]),
            dbc.Row([
                # insert pie chart
                dbc.Col(dcc.Graph(id = "type-bar-chart")),
            ]),
        ], width=5),
        dbc.Col([
            dbc.Row([
                dbc.Col(failure_card),
            ]),
            dbc.Row([
                # insert bar chart
                dbc.Col(dcc.Graph()),
            ], className="h-100"),
        ], width=5),
    ])
], fluid=True)

@app.callback(
    Output('type-bar-chart', 'figure'),
    [Input("Type", "value")
    ])

@app.callback(
    Output('date-bar-chart', 'figure'),
    [Input("DOW", "value")
    ])    

def date_chart(value):

    #bar_data = df[df['DOW'] == value]

    count = df['DOW'].value_counts()

    data = go.Bar(x = count.index, y = count.values)

    layout = go.Layout(title = 'DOW')

    fig = go.Figure(data = data, layout = layout)  

    return fig    

def type_chart(value):

    #bar_data = df[df['Type'] == value]

    count = df['Type'].value_counts()

    data = go.Bar(x = count.index, y = count.values)

    layout = go.Layout(title = 'Type')

    fig = go.Figure(data = data, layout = layout)  

    return fig


if __name__ == '__main__':
    app.run_server(debug=True, port = 8051)

Intended output:

  1. Type bar chart should be displayed as the bottom left figure (not DOW).

  2. Filter box containing DatePickerRange for dates, Dropdown for days of the week, and a Checklist for unique values in Type should interact simultaneously with both figures (DOW,Type)

enter image description here


Solution

  • I saw that you did not filter your dataframe with filters and then use it to return graphs so that it did not work. I think you should revise your code as below to make it work.

    from dash import dcc
    from dash import html
    import dash_bootstrap_components as dbc
    import dash
    from dash.dependencies import Input, Output, State
    from datetime import datetime
    import plotly.graph_objs as go
    import plotly.express as px
    import pandas as pd
    import numpy as np
    from datetime import datetime as dt
    
    df = pd.DataFrame({
               'Type': ['A','B','B','C','D','D','E','E','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['TIMESTAMP'] = df['TIMESTAMP'].dt.floor('1min')
    df['DATE'], df['TIME'] = zip(*[(d.date(), d.time()) for d in df['TIMESTAMP']])
    df['DATE'] = pd.to_datetime(df['DATE'])
    df['YEAR'] = df['DATE'].dt.year
    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=[
        ################### Filter box ###################### 
        html.Div(children=[
            html.Label('Filter by date (D-M-Y):'),
            dcc.DatePickerRange(
                id='input_date',
                month_format='DD/MM/YYYY',
                show_outside_days=True,
                minimum_nights=0,
                initial_visible_month=dt(2022, 1, 1),
                min_date_allowed=dt(2016, 1, 1),
                max_date_allowed=dt(2022, 12, 31),
                start_date=dt.strptime("2022-01-01", "%Y-%m-%d").date(),
                end_date=dt.strptime("2022-12-31", "%Y-%m-%d").date()
            ),
    
            html.Label('Day of the week:', style={'paddingTop': '2rem'}),
            dcc.Dropdown(
                id='DOW',
                options=[
                    {'label': x, 'value': x} for x in df['DOW'].unique()
                ],
                value=['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday','Sunday'],
                multi=True
            ),
    
            html.Label('Type:', style={'paddingTop': '2rem', 'display': 'inline-block'}),
            dcc.Checklist(
                id='Type',
                options=[
                    {'label': 'A', 'value': 'A'},
                    {'label': 'B', 'value': 'B'},
                    {'label': 'C', 'value': 'C'},
                    {'label': 'D', 'value': 'D'},
                    {'label': 'E', 'value': 'E'},
                    {'label': 'F', 'value': 'F'},
                ],
                value=['A', 'B', 'C', 'D', 'E', 'F'],
            ),
    
        ], className="four columns",
        style={'padding':'2rem', 'margin':'1rem'} )
    
    ])
    
    success_card = dbc.Card(
        [
            dbc.CardHeader("Some header"),
            dbc.CardBody(
                [
                    html.H6("Type Count"),
                    html.Div(id='count_of_types', style={"color": "Purple"})
                ]
            ),
        ],
        className='text-center m-4'
    )
    
    failure_card = dbc.Card(
        [
            dbc.CardHeader("Some header"),
            dbc.CardBody(
                [
                    html.H6("Date Count"),
                    html.Div(id='count_of_dates', style={"color": "Black"})
                ]
            ),
        ],
        className='text-center m-4'
    )
    
    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(success_card),
                ]),
                dbc.Row([
                    dbc.Col(dcc.Graph(id = 'date-bar-chart'), style={
                            "padding-bottom": "10px",
                        },),
                ]),
                dbc.Row([
                    # insert pie chart
                    dbc.Col(dcc.Graph(id = "type-bar-chart")),
                ]),
            ], width=5),
            dbc.Col([
                dbc.Row([
                    dbc.Col(failure_card),
                ]),
                dbc.Row([
                    # insert bar chart
                    dbc.Col(dcc.Graph()),
                ], className="h-100"),
            ], width=5),
        ])
    ], fluid=True)
    
    @app.callback(
        Output('date-bar-chart', 'figure'),
        [Input('input_date','start_date'), 
         Input('input_date','end_date'),
         Input("DOW", "value"),
         Input("Type", "value")])    
    
    def date_chart(start_date, end_date, dow, types):
        dff= df[(df['DATE'] >= start_date)&(df['DATE'] <= end_date)]
        dff = dff[dff['DOW'].isin(dow)]
        dff = dff[dff['Type'].isin(types)]
        count = dff['DOW'].value_counts()
        data = go.Bar(x = count.index, y = count.values)
        layout = go.Layout(title = 'DOW')
        fig = go.Figure(data = data, layout = layout)  
    
        return fig    
    
    @app.callback(
        Output('type-bar-chart', 'figure'),
        [Input('input_date','start_date'), 
         Input('input_date','end_date'),
         Input("DOW", "value"),
         Input("Type", "value")])    
    
    def date_chart(start_date, end_date, dow, types):
        dff= df[(df['DATE'] >= start_date)&(df['DATE'] <= end_date)]
        dff = dff[dff['DOW'].isin(dow)]
        dff = dff[dff['Type'].isin(types)]
        count = dff['Type'].value_counts()
        data = go.Bar(x = count.index, y = count.values)
        layout = go.Layout(title = 'Type')
        fig = go.Figure(data = data, layout = layout)  
    
        return fig 
    
    
    if __name__ == '__main__':
        app.run_server(debug=False, port = 8051)
    

    enter image description here