python - Dash plotly - 如何在回调中包含跟踪标记样式而不导致无法使用的延迟?
问题描述
我正在组合一个数据可视化工具来查看具有 10-50000 个数据点的数据集。
我在尝试合并可自定义的跟踪颜色、标记/线条样式和大小时遇到问题;当我将这些包含在我的 update_plot 回调中时,应用程序变得无法使用。我在本地运行这个。看到第一个图大约需要 8 秒,对跟踪标记/颜色/线的任何更新大约需要 3 秒才能生效。缩放非常慢。在调试模式下使用回调可视化调查显示回调需要大约 250 毫秒,比实际等待时间短得多。
如果我的 update_plot 回调中没有这些东西,那么一切都很活泼且响应迅速。我不会改变我选择要绘制的数据的方式;这只是它的样式。
是否可以将每个跟踪的样式分离到不同的回调?我所看到的一切似乎都在说我需要从回调中返回一个完整的数字。
我的代码如下。我已经尽可能地浓缩了它,同时保持了正在发生的事情的本质。当我重用几个组件时,它们的布局存储在我多次调用的函数中,只是为了减少代码重复。
import dash
from dash import dcc
from dash import html
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output, State, MATCH
import dash_daq as daq
import plotly.graph_objs as go
import pandas as pd
import numpy as np
# Read the data. Eventually, this will be uploaded by the user
df = pd.read_table("data.txt")
# what data is available to plot? There are many datasets in the df
data_list = [{"label": str(val), "value": str(val)} for val in df["_id"].unique()]
# make lists to use in the dropdowns to choose the X and Y ordinates to plot - these are the only possible values
x_list = [{"label": "x1", "value": "x1"}, {"label": "x2", "value": "x2"}]
y_list = [{"label": "y1", "value": "y1"}, {"label": "y2", "value": "y2"}, {"label": "y3", "value": "y3"}]
# I made a function to create the dropdown things, as it makes the layout clearer to read
def data_chooser(label, idn, options, value):
return html.Div([
html.Div(label, style={'display': 'inline-block', 'vertical-align': 'middle', "width": "20%"}),
html.Div(dcc.Dropdown(id=idn, options=options, value=value),
style={'display': 'inline-block', 'vertical-align': 'middle', "width": "80%"}),
])
# Probably need to refactor this to give it the same name as data_chooser, or do both to give a common func name...
def x_ordinate_chooser(label, idn, options, value):
return data_chooser(label, idn, options, value)
# This I haven't shortened, as it is a key thing in what is making things slow.
# This is used to style the traces: colour, line/marker, and size.
def y_ordinate_modal(idn, header, colour_default, lm_default, marker_default, line_default, size_default):
return dbc.Modal(
[
dbc.ModalHeader(header),
dbc.ModalBody([html.Div([ # left div
daq.ColorPicker(
id={"type": "modal-colour-picker", "id": idn},
label='Color Picker',
value=colour_default
)
], style={"width": "50%", 'display': 'inline-block', "align": "top"}),
html.Div([ # right div
html.Div(["Lines/markers"]),
html.Div([dcc.Dropdown(id={"type": "modal-linesmarkers-dropdown", "id": idn},
options=[{"label": "Lines", "value": 'lines'},
{"label": "Markers", "value": "markers"},
{"label": "Lines & markers", "value": "lines+markers"}],
value=lm_default
)]),
html.Div(["Marker style"]),
html.Div([dcc.Dropdown(id={"type": "modal-markerstyle-dropdown", "id": idn},
options=[{"label": "circle", "value": "circle"},
{"label": "diamond", "value": "diamond"},
{"label": "cross", "value": "cross"}],
value=marker_default
)]),
html.Div(["Line style"]),
html.Div([dcc.Dropdown(id={"type": "modal-linestyle-dropdown", "id": idn},
options=[{"label": "Solid", "value": 'solid'},
{"label": "Dot", "value": "dot"},
{"label": "Dash", "value": "dash"}],
value=line_default
)]),
html.Div(["Size"]), # does both line and marker
html.Div([dcc.Dropdown(id={"type": "modal-size-dropdown", "id": idn},
options=[{"label": str(size), "value": size} for size in range(1, 30)],
value=size_default
)]),
], style={"width": "48%", 'display': 'inline-block', "align": "top", "padding-left": "2%"})
]),
dbc.ModalFooter(dbc.Button("Close", id={"type": "close-modal", "id": idn}, n_clicks=0)),
],
id={"type": "modal", "id": idn}, is_open=False
)
# This function gets used several times to add many traces to the plot.
# Each trace would have between 1000 and 50000 points
def y_ordinate_chooser(label, idn, options, value,
colour_default, lm_default, marker_default, line_default, size_default):
return html.Div([
html.Div(label, style={'display': 'inline-block', 'vertical-align': 'middle', "width": "20%"}),
html.Div(dcc.Dropdown(id=idn, options=options, value=value),
style={'display': 'inline-block', 'vertical-align': 'middle', "width": "55%"}),
html.Div([dbc.Button("Options", id={"type": "options", "id": idn}, n_clicks=0),
y_ordinate_modal(idn, label, colour_default, lm_default, marker_default, line_default, size_default)],
style={'display': 'inline-block', 'vertical-align': 'middle', "width": "25%"}),
])
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.CERULEAN])
app.layout = html.Div(children= # main div
[
html.Div(children= # left div
[
html.Div(children=[dcc.Graph("plot")],
style={'width': "75%", 'display': 'inline-block', 'vertical-align': 'top'}),
html.Div(children= # right div
[
html.Div(children=[data_chooser("Data", 'data-chooser-dropdown', data_list, data_list[0]["value"])],
style={'height': "10vh", 'vertical-align': 'middle'}),
html.Div(children= # plot controls
[
x_ordinate_chooser("X axis", "x-chooser-dropdown", x_list, x_list[0]["value"]),
y_ordinate_chooser("Y axis 1", "y-chooser-dropdown-1", y_list, y_list[0]["value"],
{"hex": '#0000FF'}, "lines+markers", "circle", "solid", 6),
y_ordinate_chooser("Y axis 2", "y-chooser-dropdown-2", y_list, y_list[1]["value"],
{"hex": '#00FF00'}, "lines+markers", "square", "dash", 6),
],
)
],
style={'width': "25%", 'display': 'inline-block', 'vertical-align': 'top'} # right div
),
],
style={'width': "100%", 'height': "100vh"} # main div
)
])
# Which callback do you want to run? Slow or not slow?
slow = 0
if slow:
# this is the slow one. It allows for changing the marker colours and the like, but is unusably slow
@app.callback(Output("plot", component_property="figure"),
[Input("data-chooser-dropdown", component_property="value"),
Input("x-chooser-dropdown", component_property="value"),
Input("y-chooser-dropdown-1", component_property="value"),
Input("y-chooser-dropdown-2", component_property="value"),
Input({"type": "modal-colour-picker", "id": "y-chooser-dropdown-1"}, component_property="value"),
Input({"type": "modal-linesmarkers-dropdown", "id": "y-chooser-dropdown-1"},
component_property="value"),
Input({"type": "modal-markerstyle-dropdown", "id": "y-chooser-dropdown-1"},
component_property="value"),
Input({"type": "modal-linestyle-dropdown", "id": "y-chooser-dropdown-1"},
component_property="value"),
Input({"type": "modal-size-dropdown", "id": "y-chooser-dropdown-1"}, component_property="value"),
Input({"type": "modal-colour-picker", "id": "y-chooser-dropdown-2"}, component_property="value"),
Input({"type": "modal-linesmarkers-dropdown", "id": "y-chooser-dropdown-2"},
component_property="value"),
Input({"type": "modal-markerstyle-dropdown", "id": "y-chooser-dropdown-2"},
component_property="value"),
Input({"type": "modal-linestyle-dropdown", "id": "y-chooser-dropdown-2"},
component_property="value"),
Input({"type": "modal-size-dropdown", "id": "y-chooser-dropdown-2"}, component_property="value"),
])
def update_plot_traces(data, x_ordinate, y1_ordinate, y2_ordinate,
y1_colour, y1_linesmarkers, y1_markerstyle, y1_linestyle, y1_size,
y2_colour, y2_linesmarkers, y2_markerstyle, y2_linestyle, y2_size):
filtered_df = df[df["_id"] == data]
x = filtered_df[x_ordinate]
# a little hacky, but allows me to clear y dropdowns and get them properly removed from the plot
traces = []
try:
traces.append(go.Scatter(x=x,
y=filtered_df[y1_ordinate],
name=y1_ordinate,
meta="y1",
mode=y1_linesmarkers,
line={"dash": y1_linestyle, "color": y1_colour["hex"], "width": y1_size / 3},
marker={"symbol": y1_markerstyle, "color": y1_colour["hex"], "size": y1_size}
)
)
except KeyError:
pass
try:
traces.append(go.Scatter(x=x,
y=filtered_df[y2_ordinate],
name=y2_ordinate,
meta="y2",
mode=y2_linesmarkers,
line={"dash": y2_linestyle, "color": y2_colour["hex"], "width": y2_size / 3},
marker={"symbol": y2_markerstyle, "color": y2_colour["hex"], "size": y2_size}
)
)
except KeyError:
pass
layout = go.Layout(title={"text": data}, xaxis={"title": "x axis"}, yaxis={"title": "y axis"})
return {"data": traces, "layout": layout}
else:
# this is the fast version - but it doesn't allow changing of the trace colours, markers...
@app.callback(Output("plot", component_property="figure"),
[Input("data-chooser-dropdown", component_property="value"),
Input("x-chooser-dropdown", component_property="value"),
Input("y-chooser-dropdown-1", component_property="value"),
Input("y-chooser-dropdown-2", component_property="value")
])
def update_plot_traces(data, x_ordinate, y1_ordinate, y2_ordinate):
filtered_df = df[df["_id"] == data]
x = filtered_df[x_ordinate]
# a little hacky, but allows me to clear y dropdowns and get them properly removed from the plot
traces = []
try:
traces.append(go.Scatter(x=x, y=filtered_df[y1_ordinate], name=y1_ordinate, meta="y1"))
except KeyError:
pass
try:
traces.append(go.Scatter(x=x, y=filtered_df[y2_ordinate], name=y2_ordinate, meta="y2"))
except KeyError:
pass
layout = go.Layout(title={"text": data}, xaxis={"title": "x axis"}, yaxis={"title": "y axis"})
return {"data": traces, "layout": layout}
# this is the callback to open and close the modal windows for the trace colouring and styling.
@app.callback(
Output({"type": "modal", "id": MATCH}, "is_open"),
[Input({"type": "options", "id": MATCH}, "n_clicks"),
Input({"type": "close-modal", "id": MATCH}, "n_clicks")],
[State({"type": "modal", "id": MATCH}, "is_open")]
)
def toggle_modal(n1, n2, is_open):
if n1 or n2:
return not is_open
return is_open
if __name__ == "__main__":
app.run_server(debug=True, port=8050)
解决方案
推荐阅读
- python-3.x - 读取文件时如何跳过N条中心线?
- mongodb - 将 .catch 与 async/await 一起使用
- javascript - 通过单击按钮“onClick”和函数返回结果中的对象属性值来更改反应组件中的表行
- r - 如何更改 R data.frame 中的行名?
- firebase - 可能未处理的 Promise Rejection (id: 6): Firebase Authentication not found
- laravel - CPanel中的Laravel项目图像上传路径错误
- docusignapi - INVALID_REQUEST_PARAMETER DocuSign_eSign::EnvelopesApi
- installation - make [2]: **** [Makefile:44: envi_slc_decode] 错误 1,在 fedora 32 中安装 gmtsar 时
- java - 在 Java 中,如何在 POST 请求的正文中编写 HTML 代码?
- symfony - Symfony 事件监听器,调试栏不起作用