如何使用不同的下拉菜单更新 plotly graph dash

How to update a plotly graph dash with different dropdowns

我正在尝试使用两个不同的下拉菜单作为输入来更新绘图破折号。

这是我的示例数据框:

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

而我假装做的是,如果我 select 类别 A,将所有对应点分散地绘制到该类别,但如果我也 select 修改图表的子类别仅绘制数据框的对应类别-子类别点。

代码如下,如果我只添加第一个下拉列表的回调,它就可以工作,但是当我将第二个回调添加到子类别时,它就不起作用了。 我正在按照 dash plotly 教程中的建议进行操作:

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

但无论如何,如果我将输出放在单独的回调中或放在同一个回调中,我将无法使其工作,这是我正在尝试的代码(使用 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

如果我像这样在回调中使用多个输入:

@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

效果更好(或更接近我假装的效果),但是当我在类别之间切换时,我看不到任何数据。

在此先感谢您的帮助和建议。

我遇到了类似的问题,诀窍是向第二个下拉列表添加一个选项 all。然后我想在第二个下拉列表中只显示给定类别中的子类别。所以我实际上对下拉菜单使用了 2 个回调,对绘图使用了 1 个回调。

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)