Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
3.7k views
in Technique[技术] by (71.8m points)

python - Plotly-Dash: How to improve plot responsiveness to slider changes

I'm developing a tool to visualize the effects of a set of parameters on a mathematical function by modifying those parameters via Dash sliders. I'm using the approach from a few of the Dash tutorial examples which use a callback to replace the figure.

This works, but the plot isn't as responsive to slider changes as built-in operations such as rotating the plot via dragging. This is especially the case when there are many elements in the figure.

Is there a different approach that will improve responsiveness? For example, is there a way to target only the plot elements that changed rather than replacing the entire figure?

Here's a minimal working example consisting of a static circle (with many samples) and a line segment that we rotate via a slider. Rotation of the line segment is quite choppy.

import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objects as go
from dash.dependencies import Input, Output, State
import numpy as np

app = dash.Dash(__name__)

# plot a circle
t = np.linspace(0, 2*np.pi, 10000)  # intentionally use many points to exaggerate the issue
x = np.cos(t)
y = np.sin(t)
z = np.zeros_like(t)
marker_size = 4
fig = go.Figure()
fig.add_trace(go.Scatter3d(x=x, y=y, z=z, mode='lines'))
fig.add_trace(go.Scatter3d(
    x=[0.0, 0.0], y=[0.0, 0.0], z=[0.0, 0.0],
    marker=go.scatter3d.Marker(size=marker_size),
    line=dict(width=3.0),
    showlegend=False,
))

fig.update_layout(
    uirevision='constant',
    autosize=False,
    width=900,
    height=900,
    scene=dict(
        xaxis=dict(range=[-1, 1]),
        yaxis=dict(range=[-1, 1]),
        zaxis=dict(range=[-1, 1.0]),
        aspectratio=dict(x=2, y=2, z=2),
    ),
)

app.layout = html.Div(children=[
    dcc.Graph(
        id='example-graph',
        figure=fig
    ),
    html.Div(
        [
            dcc.Slider(
                id='slider-phi',
                min=0.0,
                max=360.0,
                step=1.0,
                value=0.0,
                marks={0: '0', 180: '180', 360: '360'},
                updatemode='drag',
            ),
        ],
        style=dict(width='50%'),
    ),
    html.Div(children='', id='output-box'),
])

@app.callback(
    Output('example-graph', 'figure'),
    [Input('slider-phi', 'value')],
    [State('example-graph', 'figure')]
)
def display_structure(phi, myfig):
    myfig['data'][1]['x'][1] = np.cos(np.radians(phi))
    myfig['data'][1]['y'][1] = np.sin(np.radians(phi))
    myfig['data'][1]['z'][1] = 0

    return myfig

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

It looks like although display_structure modifies myfig in place, the return value is taken as a brand new plot which plotly will render all over again.

Is there any chance that the callback does not require a return value? If your function modifies the plot attributes this could be in place.

FYI: I use bokeh and have limited knowledge about plotly.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...