✊🏿 Black Lives Matter. Please consider donating to Black Girls Code today.
🐇 Announcing Dash VTK for 3d simulation graphics. Check out the March webinar.

3D surface live update on Dash

Hi,

For my project, I need to represent some data in 3D with a Surface in Dash, and since the values are changing I need the graph to update every 100ms. I’ve tried many things but I didn’t succeed…

With the classical way to animate a graph in Dash, nothings happens (the values are changing but the graph is not rewritten). Do you know how I can do it please ?

Here’s my current code (it’s bad, but it was for testing).

import random
import matplotlib.animation as animation
import mpl_toolkits.mplot3d.axes3d as p3
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
import plotly.offline as offline
import plotly.graph_objects as go
import pickle
import matplotlib.pyplot as plt
from minisom import MiniSom, fast_norm
from pathlib import Path
import imageio
import math
import numpy as np
import pandas as pd
import seaborn as sns
import dash_html_components as html
import dash_core_components as dcc
import dash
import plotly.graph_objects as go
import dash
from dash.dependencies import Output, Input
import dash_core_components as dcc
import dash_html_components as html
import plotly
import random
import plotly.graph_objs as go
from collections import deque

import pandas as pd

# Read data from a csv
z_data = pd.read_csv(
    'https://raw.githubusercontent.com/plotly/datasets/master/api_docs/mt_bruno_elevation.csv')


import numpy as np 
mu, sigma = 0, 50 
# creating a noise with the same dimension as the dataset (2,2) 
noise = np.random.normal(mu, sigma, z_data.shape)
s1 = go.Surface(z=((z_data+noise)/(z_data+noise).max().max()).values)
noise = np.random.normal(mu, sigma, z_data.shape)
s2 = go.Surface(z=((z_data+noise)/(z_data+noise).max().max()).values)
noise = np.random.normal(mu, sigma, z_data.shape)
s3 = go.Surface(z=((z_data+noise)/(z_data+noise).max().max()).values)
noise = np.random.normal(mu, sigma, z_data.shape)
s4 = go.Surface(z=((z_data+noise)/(z_data+noise).max().max()).values)
noise = np.random.normal(mu, sigma, z_data.shape)
s5 = go.Surface(z=((z_data+noise)/(z_data+noise).max().max()).values)

#frames = [go.Frame(data=[s1]),go.Frame(data=[s2]),go.Frame(data=[s3]),go.Frame(data=[s4]),go.Frame(data=[s5])]
frames = [{"data":[s1]},{"data":[s2]},{"data":[s3]},{"data":[s4]},{"data":[s5]}]

fig = go.Figure(data=[s1],layout=go.Layout(
        xaxis=dict(range=[0, 16], autorange=False),
        yaxis=dict(range=[0, 13], autorange=False),
        hovermode="closest",
        title="Start Title"
    ),frames=frames)

#fig.show()

#plot = offline.plot(fig, filename="dataviz.html")

app = dash.Dash(__name__)
app.layout = html.Div(
    [
        dcc.Graph(id='live-graph', animate=True),
        dcc.Interval(
            id='graph-update',
            interval=1*1000
        ),
    ]
)

@app.callback(Output('live-graph', 'figure'),
              [Input('graph-update', 'n_intervals')])
def update_graph_scatter(input_data):
    global fig,frames
    print(input_data)
    return frames[input_data]


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

Thanks !!

Hi, after multiple tests, I’ve found out that changing

dcc.Graph(id='live-graph', animate=True),

for

dcc.Graph(id='live-graph', animate=True, animation_options={"frame":{"redraw":True}}),

allowed the graph to update properly. Now my problem is that I can’t update faster than 500ms, even with an interval of 100 or 10, and the title of the web page keeps writing Dash then Updating… then Dash, etc… (i’d like it to stay still, because it’s an auto-update)

Thank you for your insights on the matter :slight_smile:

Hi @lrosique welcome to the forum! I tried your example without any animation option and the graph updates pretty fast…

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


# Read data from a csv
z_data = pd.read_csv(
    'https://raw.githubusercontent.com/plotly/datasets/master/api_docs/mt_bruno_elevation.csv')


import numpy as np 
mu, sigma = 0, 50 
# creating a noise with the same dimension as the dataset (2,2) 
noise = np.random.normal(mu, sigma, z_data.shape)
s1 = go.Surface(z=((z_data+noise)/(z_data+noise).max().max()).values)
noise = np.random.normal(mu, sigma, z_data.shape)
s2 = go.Surface(z=((z_data+noise)/(z_data+noise).max().max()).values)
noise = np.random.normal(mu, sigma, z_data.shape)
s3 = go.Surface(z=((z_data+noise)/(z_data+noise).max().max()).values)
noise = np.random.normal(mu, sigma, z_data.shape)
s4 = go.Surface(z=((z_data+noise)/(z_data+noise).max().max()).values)
noise = np.random.normal(mu, sigma, z_data.shape)
s5 = go.Surface(z=((z_data+noise)/(z_data+noise).max().max()).values)

frames = [{"data":[s1]},{"data":[s2]},{"data":[s3]},{"data":[s4]},{"data":[s5]}]

fig = go.Figure(data=[s1],layout=go.Layout(
        xaxis=dict(range=[0, 16], autorange=False),
        yaxis=dict(range=[0, 13], autorange=False),
        hovermode="closest",
        title="Start Title"
    ))


app = dash.Dash(__name__)
app.layout = html.Div(
    [
        dcc.Graph(id='live-graph'),
        dcc.Interval(
            id='graph-update',
            interval=100
        ),
    ]
)

@app.callback(Output('live-graph', 'figure'),
              [Input('graph-update', 'n_intervals')])
def update_graph_scatter(input_data):
    if input_data is None:
        return dash.no_update
    return frames[input_data % 5]


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

Hi Emmanuelle,

Thank you very much for your answer ^^ I’ve tested your code and yes, it’s not lagging, but if I change the interval for “10ms” it’s totally lagging -> I guess if we refresh too fast for our computer, it’s laggy (instead of going at a computationaly pace).

If you have the time, I’d like to ask you one last question : I’ve changed the Surface for a custom 2d mesh and I’d like to colorize my triangles depending on some z value (i.e. it’s a projection of my surface on a mesh). Do you know if it’s possible with plotly ? Because for now all I can do is plot each triangle individually in a loop (using scatter plots) and calculate the color with a viridis map, because if I plot everything in one shot I can’t change the color individually. Same (worse) goes for 3d, and I’m not animating it yet :confused:

Have a good day,
Lambert