Как обновить построенный график da sh с разными выпадающими списками - PullRequest
2 голосов
/ 10 апреля 2020

Я пытаюсь обновить графически график da sh с двумя различными раскрывающимися списками в качестве входных данных.

Это мой примерный кадр данных:

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

enter image description here

И то, что я притворяюсь, это то, что если я выбираю категорию А, то в разбросе отображаются все соответствующие точки на эту категорию, но если также я выбираю подкатегорию, которая изменяет график, отображающий только соответствующую категорию -подкатегория точки данных.

Код приведен ниже, и он работает, если я добавляю только обратный вызов первого раскрывающегося списка, но когда я добавляю второй обратный вызов в подкатегорию, он не работает. Я следую советам из учебника da sh, где говорится:

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.

Da sh обратные вызовы документации

Но в любом случае, если я помещаю вывод в отдельные обратные вызовы или в то же самое, я не могу заставить его работать, вот код, который я пытаюсь (используя ноутбук jupyter):

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

Он работает лучше (или более близко к тому, что я притворяюсь), но, тем не менее, когда я переключаюсь между категориями, я не вижу данных.

Заранее спасибо за вашу помощь и РЕКОМЕНДАЦИИ.

1 Ответ

1 голос
/ 10 апреля 2020

У меня были похожие проблемы, трюк в том, чтобы добавить во второй выпадающий список параметр 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)

...