Сюжетно пунктирные проблемы обратного вызова с двумя раскрывающимися меню и двумя ползунками диапазона

#python #plotly #visualization #plotly-dash

Вопрос:

У меня есть приложение dash с двумя ползунками, тремя раскрывающимися списками — имя пробной версии, идентификатор капсулы и ось y, а также диаграмма рассеяния.

  • Два ползунка сверху предназначены для визуализации элемента времени, а второй ползунок используется для перемещения, где точки диаграммы рассеяния будут обновляться в соответствии с точками на втором ползунке.
  • Два выпадающих списка находятся в цепочке обратного вызова, т. е. Выбор имени пробной версии автоматически создаст соответствующий идентификатор капсулы.
  • В соответствии с тем, что перемещается на втором слайдере, первый слайдер (вверху) сможет увеличивать масштаб точек. (см. 3-е изображение)
  • Обратный вызов для среднего графика/ползунка не отображается для большинства идентификаторов капсул (первое изображение), но для некоторых капсул ползунок отображается как второе изображение. Я бы хотел, чтобы все капсулы могли это сделать, но, похоже, не могу найти проблему. Любая помощь будет очень признательна! 🙂

введите описание изображения здесь

введите описание изображения здесь

введите описание изображения здесь

 import pandas as pd
import plotly.express as px  # (version 4.7.0)
import plotly.graph_objects as go
import numpy as np

import openpyxl
import dash  # (version 1.12.0) pip install dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate

app = dash.Dash(__name__)
server = app.server

df = pd.read_excel("Book2.xlsx")

df = df.replace(-1, 0)
print(df)

df['Time_since_ingestion'] = ((df['Wallclock'] - df['Time_Ingested'])/np.timedelta64(1, 'm')).round(2)

trial = df['trial_name'].unique()
trial_names = sorted(list(trial))
trial_names_1 = [{'label': k, 'value': k} for k in sorted(trial)]
trial_names_all = trial_names_1

def slider_fig(df):
    return px.scatter(
                df.groupby("Wallclock", as_index=False).size(), x="Wallclock", y="size"
            ).update_layout(
                xaxis={"rangeslider": {"visible": True}, "title":None},
                height=300,
                yaxis={"tickmode": "array", "tickvals": [], "title": None},
                margin={"l": 0, "r": 0, "t": 0, "b": 0},
            )

app.layout = html.Div([

    dcc.Dropdown(id="trial_select",
                 options=[{'label': s, 'value': s} for s in sorted(df.trial_name.unique())],
                 optionHeight=25,
                 clearable=False,
                 placeholder='Please select trial name...',
                 value='',
                 style={'width': "50%"},
                 ),
    dcc.Dropdown(id="capsule_select",
                 options=[],
                 value=[],
                 optionHeight=25,
                 multi=True,
                 placeholder='Please select...',
                 style={'width': "50%"}
                 ),
    dcc.Dropdown(id='y-axis_select',
        options=[
            {'label': 'TSI', 'value': 'TSI'},
            {'label': 'VOCR_100', 'value': 'VOCR_100'},
        ], value="VOCR_100",
            style={'width': "30%"}),
    
    dcc.Graph(
        id="slider",
        figure=slider_fig(df),
        ),

    dcc.Graph(id="the_graph",
              figure=slider_fig(df)
                  ),
])

# -----------------------------------------------------------
# Populate the options of capsule dropdown based on the trial name dropdown
@app.callback(
    Output('capsule_select', 'options'),
    Input('trial_select', 'value'))
def set_trial_name_options(trial_name_chosen):
    dff = df[df.trial_name == trial_name_chosen]
    print(dff)
    return [{'label': c, 'value': c} for c in sorted(dff['Capsule_ID'].unique())]


# Populate initial values of capsule ID dropdown
@app.callback(
    Output('capsule_select', 'value'),
    Input('capsule_select', 'options'))
def set_trial_name_value(capsule_option):
    print(capsule_option)
    return [x['value'] for x in capsule_option]


@app.callback(
    # Output('chart', 'figure'),
    Output('the_graph', 'figure'),
    Output('slider', 'figure'),
    Input('capsule_select', 'value'),
    Input('trial_select', 'value'),
    Input('y-axis_select', 'value'),
    Input('slider', 'relayoutData'),
    State('slider', 'figure')
)

def update_graph(selected_capsule, selected_trial, selected_y, slider, sfig):
    if selected_capsule == '':
        return dash.no_update
    else:
        dff = df[(df['trial_name'] == selected_trial) amp; (df['Capsule_ID'].isin(selected_capsule))]

    if slider and "xaxis.range" in slider.keys():
        dff = dff.loc[dff["Wallclock"].between(*slider["xaxis.range"])]
    else:
        # update slider based on selected capsules
        sfig = slider_fig(dff)


    fig = px.scatter(
        data_frame=dff,
        x="TSI",
        y=selected_y,
        hover_data=["TSI", "GET", "ICJ", "Excretion"],
        color="Time_since_ingestion",
        opacity = 0.5
        )

    fig.update_traces(textposition='top center')

    return fig, sfig

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