简体   繁体   中英

How to update a plotly graph dash with different dropdowns

I am trying to update a plotly graph dash with two different dropdowns as inputs.

This is my sample dataframe:

import pandas as pd
df1 = {'category' : ['A','A','A','B','B','B'],'subcategory' : ['x', 'y', 'z', 'x1','y1','z1'],
      'x_coord' : [1, 2,3,2,2,2],'y_coord' : [1,3,2,1,3,2]}

df_test = pd.DataFrame(df1)
df_test

在此处输入图像描述

And what I pretend to do is if I select category A, that plots in a scatter all the correspondent points to the category, but If Also I select a subcategory that modifies the graph plotting only the correspondent category-subcategory point of the dataframe.

The code is below, and it works if I only add the callback of the first dropdown, but when I add the second callback to the subcategory it doesn't work. I am following the suggestions in the dash plotly tutorial where it says:

 A word of caution: it's not always a good idea to combine Outputs, even if you can: If the Outputs depend on some but not all of the same Inputs, keeping them separate can avoid unnecessary updates. If they have the same Inputs but do independent computations with these inputs, keeping the callbacks separate can allow them to run in parallel.

Dash documentation callbacks

But anyway if I put the output in separate callbacks or in the same I cannot make it work, here is the code that I am trying (using jupyter notebook):

import dash
import plotly as py
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.graph_objects as go
from jupyter_plotly_dash import JupyterDash
py.offline.init_notebook_mode(connected = True)

app = JupyterDash('Test')

app.layout = html.Div([
    dcc.Dropdown(id='dropdown1',
                 options=[{'label':i, 'value':i} for i in df_test['category'].unique()]),
    dcc.Dropdown(id='dropdown2',
                 options=[{'label':i, 'value':i} for i in df_test['subcategory'].unique()]),
    dcc.Graph(id='graphic')
])

@app.callback(
    Output('dropdown2', 'options'),
    [Input('dropdown1', 'value')])

def update_drop2(selected_drop):
    filtered_df = df_test[(df_test.category == selected_drop)]
    return [{'label':i, 'value':i} for i in filtered_df['subcategory'].unique()]

@app.callback(
    Output('graphic', 'figure'),
    [Input('dropdown1', 'value')])

def update_figure(selected_drop):

    filtered_df = df_test[(df_test.category == selected_drop)]

    fig = go.Figure()

    fig.add_trace(go.Scatter(x=filtered_df.x_coord,y=filtered_df.y_coord, marker = dict(size=15, color='green'), mode='markers'))

    return fig


@app.callback(
    Output('graphic', 'figure'),
    [Input('dropdown2', 'value')])

def update_figure(selected_drop):

    filtered_df = df_test[(df_test.subcategory == selected_drop)]

    fig = go.Figure()

    fig.add_trace(go.Scatter(x=filtered_df.x_coord,y=filtered_df.y_coord, marker = dict(size=15, color='green'), mode='markers'))

    return fig

app

If I use multiple inputs on the callback like this:

@app.callback(
Output('graphic', 'figure'),
[Input('dropdown1', 'value'), Input('dropdown2', 'value')])

def update_figure(selected_drop1, selected_drop2):

    if not selected_drop2:
        filtered_df = df_test[(df_test.category == selected_drop1)]
    else: 
        filtered_df = df_test[(df_test.category == selected_drop1) & 
                      (df_test.subcategory == selected_drop2)]
    fig = go.Figure()

    fig.add_trace(go.Scatter(x=filtered_df.x_coord,y=filtered_df.y_coord, 
    marker = dict(size=15, color='green'), mode='markers'))

    return fig

It works better (or more near what I pretend), but however when I switch between categories I see no data.

Thanks in advance for your help and reccomendations.

I had a similar problems the trick is to add to the second dropdown an option all . Then I wanted on the second dropdown to show only the subcategories in the given category. So I actually use 2 callbacks for dropdowns and 1 callback for the plot.

app.py

import pandas as pd
import os
import plotly.graph_objs as go
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output

df = pd.DataFrame({'category' : ['A','A','A','B','B','B'],
                   'subcategory' : ['x', 'y', 'z', 'x1','y1','z1'],
                   'x_coord' : [1, 2,3,2,2,2],
                   'y_coord' : [1,3,2,1,3,2]})

# lists of categories
options1 = sorted(df["category"].unique().tolist())

# dictionary of category - subcategories
all_options = df.groupby("category")["subcategory"].unique()\
                .apply(list).to_dict()

# we add as first subcategory for each category `all`
for k, v in all_options.items():
    all_options[k].insert(0, 'all')


app = dash.Dash()
app.layout = html.Div([
    dcc.Dropdown(
        id='first-dropdown',
        options=[{'label': k, 'value': k} for k in all_options.keys()],
        value=options1[0]
    ),

    html.Hr(),

    dcc.Dropdown(id='second-dropdown'),

    html.Hr(),

    dcc.Graph(id='display-selected-values')
])

# the following two callbacks generate a dynamic 2 option

@app.callback(
    dash.dependencies.Output('second-dropdown', 'options'),
    [dash.dependencies.Input('first-dropdown', 'value')])
def set_2_options(first_option):
    return [{'label': i, 'value': i} for i in all_options[first_option]]


@app.callback(
    dash.dependencies.Output('second-dropdown', 'value'),
    [dash.dependencies.Input('second-dropdown', 'options')])
def set_2_value(available_options):
    return available_options[0]['value']


@app.callback(
    dash.dependencies.Output('display-selected-values', 'figure'),
    [dash.dependencies.Input('first-dropdown', 'value'),
     dash.dependencies.Input('second-dropdown', 'value')])
def update_graph(selected_first, selected_second):
    if selected_second == 'all':
        ddf = df[df["category"]==selected_first]
    else:
        ddf = df[(df["category"]==selected_first) &
                 (df["subcategory"]==selected_second)]

    fig = go.Figure()
    fig.add_trace(
        go.Scatter(x=ddf["x_coord"],
                   y=ddf["y_coord"],
                   marker = dict(size=15, color='green'),
                   mode='markers'))
    return fig

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM