Multiple Inputs to filter a single graph using dropdowns

Hello Dash Community,

I am having a lot of trouble getting a single graph to update with multiple Inputs (not just the x and y axis).

TL;DR
Essentially, I am trying to get the multiple dropdowns/charts to behave like multiple slicers/charts in Excel.

Here is the situation:

  • Bar Graph with categorical variables on the X axis and their frequency on the Y axis. I want the X axis to filter based on multiple dropdown selections
  • 8 dropdowns (Shift, Group, Hour (of the work day), Quarter, Month, Week, Weekday, and Date)
  • Reading a SQLite3 DB into a Pandas DF.
  • One dropdown works perfectly, but when I try to add more, the graphs do not display and do not work.

Some Code (One Dropdown called and functioning):

    import dash
    import dash_table
    import dash_core_components as dcc
    import dash_html_components as html
    import dash_bootstrap_components as dbc
    import plotly.offline as pyo
    from dash.dependencies import Output, Input
    import plotly.graph_objs as go
    import pandas as pd
    import sqlite3 as db

    conn = db.connect('DB.db')

    def sql(query):
        df = pd.read_sql(query,conn)
        return df

    full = sql("""SELECT * FROM table""")

    bar_default = sql("""
                        SELECT Group, COUNT(*) AS Count
                        FROM table
                        WHERE Shift = "1st" OR Shift = "2nd" OR Shift= "3rd"
                        GROUP BY Group
                        ORDER BY Count DESC""")

    group_df = sql("""
                        SELECT DATE(Week) AS Week, 
                            DATE(Date) AS Date, 
                            Group, 
                            COUNT(*) AS Count 
                        FROM table
                        WHERE Shift = "1st" OR Shift = "2nd" OR Shift = "3rd"
                        GROUP BY Week, Date, Group
                        ORDER BY Date, Group
                        """)

    header = #Header styling and code here

    app = dash.Dash(__name__,
                    external_stylesheets=[dbc.themes.BOOTSTRAP],
                    meta_tags=[{"name": "viewport",
                                "content": "width=device-width"}]
                   )

    dropdowns = dbc.Row(
        [
            dbc.Col(
                dcc.Dropdown(
                    id="shift_dropdown",
                    options=[{'label':i,'value':i}for i in full['Shift'].unique()],
                    value = None,
                    placeholder="Shift")),
            dbc.Col(
                dcc.Dropdown(
                    id="hour_dropdown",
                    options=[{'label':i,'value':i}for i in full['Hour'].unique()],
                    value = None,
                    placeholder="Hour")),
            dbc.Col(
                dcc.Dropdown(
                    id="group_dropdown",
                    options=[{'label':i,'value':i}for i in full['Group'].unique()],
                    value = None,
                    placeholder="Group")),
            dbc.Col(
                dcc.Dropdown(
                    id="quarter_dropdown",
                    options=[{'label':i,'value':i}for i in full['Quarter'].unique()],
                    value = None,
                    placeholder="Quarter")),
            dbc.Col(
                dcc.Dropdown(
                    id="month_dropdown",
                    options=[{'label':i,'value':i}for i in full['Month'].unique()],
                    value = None,
                    placeholder="Month")),
            dbc.Col(
                dcc.Dropdown(
                    id="week_dropdown",
                    options=[{'label':i,'value':i}for i in full['Week'].unique()],
                    value = None,
                    placeholder="Week")),
            dbc.Col(
                dcc.Dropdown(
                    id="day_dropdown",
                    options=[{'label':'Mon','value':'Mon'},
                             {'label':'Tue','value':'Tue'},
                             {'label':'Wed','value':'Wed'},
                             {'label':'Thu','value':'Thu'},
                             {'label':'Fri','value':'Fri'}],
                    value = None,
                    placeholder="Day")),
            dbc.Col(
                dcc.Dropdown(
                    id="date_dropdown",
                    options=[{'label':i,'value':i}for i in full['Date'].unique()],
                    value = None,
                    placeholder="Date"))
        ],justify='center',style={'padding':'5px'},align="center",className="sticky-top")

    body =html.Div([
        dbc.Row(
                dbc.Col(
                    # Bar Graph
                    dcc.Graph(id='bar'),style={'margin':'5px','border':'1px solid black',}),
            className='twelve columns',
            justify="center")
            ],style={'textAlign':'center','margin':'5px'})

    footer = #Footer styling and code here 

    app.layout = html.Div([header,dropdowns,body,footer])

    @app.callback(Output('bar','figure'),
                 [Input('week_dropdown','value')])
    def update_bar(week):
        if week == None:
            df = bar_default
        else:
            df = group_df.loc[group_df['Week'] == week]
        data = []
        figure = go.Bar(x=df['Group'],
                        y=df['Count'],
                        name="Group Stats")

        data.append(figure)
        
        layout = {
            'title':"Stats By Group",
            'plot_bgcolor':'#f0f0ef',
            'paper_bgcolor':'#d6d6d6'}
        
        return {'data':data,
                'layout':layout}

    if __name__ == '__main__':
        app.run_server()

Thank you in advance!

I am also looking for a way to make multiple dropdown menus filter each other like Excel slicers. I keep stumbling into circular logic issues. Is there some principaled solution to this?

In my case, there are ~5 dropdown menus. Let’s say i select a value from Dropdown Menu #1 (Dd1), then that should filter the options for the remaining Dd2-5. If I go on to select a value from Dd2, then that should filter the options for Dd3-5 – but not for Dd1, obviously. I then select a value from Dd3, and Dd4-5 options are filtered – Dd1 and Dd2 are not affected.

If I now clear the selected value from Dd2, the options for Dd2, Dd4 and Dd5 should now be refreshed and filtered by the Dd1-Dd3-combination. If I then clear the value selected in Dd1, then options are refreshed for Dd1 and Dd3-5 – Dd2 is the only one with a selected value, on which the options for the remaining dropdown menus rely.