Search code examples
pythonplotlyplotly-dash

How to implement dropdown menu in Plotly Dash using Python?


In this app, I'm trying to display a plot that changes when the value in the dropdown menu is changed. The values are the boroughs in London. The data can be found here. Below is the code for base plot.

import plotly.graph_objects as go

df = pd.read_excel('multi-year-station-entry-and-exit-figures.xls', sheet_name='2017 Entry & Exit', skiprows=6)

df = df.loc[df['Borough'] == 'Islington']

df['Sunday'] = df['Sunday'] + df['Sunday.1']
df['Saturday'] = df['Saturday'] + df['Saturday.1']

df = df[['Borough', 'Station', 'Saturday', 'Sunday']]
df.index = range(len(df))
print(df['Borough'])

fig = go.Figure(data=[
    go.Bar(name='Saturday', x=df["Station"], y=df["Saturday"]),
    go.Bar(name='Sunday', x=df["Station"], y=df["Sunday"])
])

fig.update_layout(title='Weekend entry and exit figures in 2017',
                  xaxis_tickfont_size=14,
                  yaxis=dict(
                      title='Entry and exit numbers',
                      titlefont_size=16,
                      tickfont_size=14,
                  )
                  , barmode='group', template='plotly_dark', bargap=0.3, bargroupgap=0.1)
fig.show()

I am able to change the the borough name manually to change the plot. I then created the Dash app with the the dropdown menu. However, I can't figure out how to change the plot when a dropdown option is selected. I created a version using conditional statements where I add an if-elif statement for each borough. I am still unable to change the plot itself however. Basically, I need to incorporate this piece of code df = df.loc[df['Borough'] == 'Islington'] to the Dash app. The Dash app code is shown below.

import dash_core_components as dcc
import dash_html_components as html
import plotly.express as px
import pandas as pd
import os
import plotly.io as pio
import plotly.graph_objects as go
import dash_bootstrap_components as dbc

df = pd.read_excel('multi-year-station-entry-and-exit-figures.xls', sheet_name='2017 Entry & Exit', skiprows=6)

df['Sunday'] = df['Sunday'] + df['Sunday.1']
df['Saturday'] = df['Saturday'] + df['Saturday.1']

df = df[['Borough', 'Station', 'Saturday', 'Sunday']]
df.index = range(len(df))
df = df[:-3]

app = dash.Dash()

fig_names = ['Islington', 'Camden']
fig_dropdown = html.Div([
    dcc.Dropdown(
        id='fig_dropdown',
        options=[{'label': x, 'value': x} for x in fig_names],
        value=None
    )])
fig_plot = html.Div(id='fig_plot')
app.layout = html.Div([fig_dropdown, fig_plot])


@app.callback(
    dash.dependencies.Output('fig_plot', 'children'),
    [dash.dependencies.Input('fig_dropdown', 'value')])
def update_output(fig_name):
    return name_to_figure(fig_name)


def name_to_figure(fig_name):
    figure = go.Figure()
    if fig_name == 'Islington':
        figure = go.Figure(data=[
            go.Bar(name='Saturday', x=df["Station"], y=df["Saturday"]),
            go.Bar(name='Sunday', x=df["Station"], y=df["Sunday"])
        ])
    elif fig_name == 'Camden':
        figure = go.Figure(data=[
            go.Bar(name='Saturday', x=df["Station"], y=df["Saturday"]),
            go.Bar(name='Sunday', x=df["Station"], y=df["Sunday"])
        ])
    return dcc.Graph(figure=figure)


app.run_server(debug=True, use_reloader=False)

Solution

  • You can create a copy of your data frame containing only the data corresponding to the dropdown selection, and then use this filtered data frame for generating the figure:

    import dash
    import dash_core_components as dcc
    import dash_html_components as html
    import pandas as pd
    import plotly.graph_objects as go
    
    app = dash.Dash()
    
    # load the data
    df = pd.read_excel('multi-year-station-entry-and-exit-figures.xls', sheet_name='2017 Entry & Exit', skiprows=6)
    df['Sunday'] = df['Sunday'] + df['Sunday.1']
    df['Saturday'] = df['Saturday'] + df['Saturday.1']
    df = df[['Borough', 'Station', 'Saturday', 'Sunday']]
    df.index = range(len(df))
    df = df[:-3]
    
    # extract the list of all boroughs
    fig_names = df['Borough'].unique().tolist()
    
    # generate the app layout
    app.layout = html.Div([
    
        # add a dropdown for selecting the borough
        html.Div([
            dcc.Dropdown(
                id='fig_dropdown',
                options=[{'label': x, 'value': x} for x in fig_names],
                value=fig_names[0]  # use the first borough as the initial selection
            )]),
    
        # add a container for the figure
        html.Div(id='fig_plot'),
    
    ])
    
    # define a callback for updating the figure
    # based on the dropdown selection
    @app.callback(dash.dependencies.Output('fig_plot', 'children'),
                 [dash.dependencies.Input('fig_dropdown', 'value')])
    def update_output(fig_name):
    
        # extract the data for the selected borough
        df_fig = df[df['Borough'] == fig_name]
    
        # plot the data for the selected borough
        figure = go.Figure(data=[
            go.Bar(name='Saturday', x=df_fig['Station'], y=df_fig['Saturday']),
            go.Bar(name='Sunday', x=df_fig['Station'], y=df_fig['Sunday'])
        ])
    
        return dcc.Graph(figure=figure)
    
    if __name__ == '__main__':
        app.run_server(debug=True, host='0.0.0.0', port=1234)