Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Live updating only the data in Dash/plotly

I want to monitor some live data and allow the user to select their own ranges when interacting with the plots. I created this small example (got it from the tutorial) and the problem is, every time I update the plot, everything gets reset since update_graph_live() returns a new Plotly figure. (see example below)

Is it possible to update just the data, so the figure is not reloaded and reset to the default view/settings? I was using d3.js before and sent the data through websockets, so I could filter the data in the browser. But I would like to do it with Dash directly.

import dash
from dash.dependencies import Output, Event
import dash_core_components as dcc
import dash_html_components as html
from random import random
import plotly

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


@app.callback(Output('live-update-graph', 'figure'),
              events=[Event('interval-component', 'interval')])
def update_graph_live():
    fig = plotly.tools.make_subplots(rows=2, cols=1, vertical_spacing=0.2)
    fig['layout']['margin'] = {
        'l': 30, 'r': 10, 'b': 30, 't': 10
    }
    fig['layout']['legend'] = {'x': 0, 'y': 1, 'xanchor': 'left'}

    fig.append_trace({
        'x': [1, 2, 3, 4, 5],
        'y': [random() for i in range(5)],
        'name': 'Foo',
        'mode': 'lines+markers',
        'type': 'scatter'
    }, 1, 1)
    fig.append_trace({
        'x': [1, 2, 3, 4, 5],
        'y': [random() for i in range(5)],
        'name': 'Bar',
        'type': 'bar'
    }, 2, 1)

    return fig


if __name__ == '__main__':
    app.run_server(debug=True)
like image 252
tamasgal Avatar asked Sep 06 '17 13:09

tamasgal


1 Answers

If you add animate=True to your dcc.Graph the toggled traces and selected zoom/marker/whatever is kept but this does not work for bar plots (although it should work: https://github.com/plotly/plotly.js/pull/1143). In addition, instead of returning the complete figure, you would need to just return the traces.

Best possible solution I could come up is to split it into two graphs but you would get at least most of the desired functionality.

enter image description here

import dash
from dash.dependencies import Output, Event
import dash_core_components as dcc
import dash_html_components as html
from random import random
import plotly

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


@app.callback(Output('live-update-graph-scatter', 'figure'),
              events=[Event('interval-component', 'interval')])
def update_graph_scatter():

    traces = list()
    for t in range(2):
        traces.append(plotly.graph_objs.Scatter(
            x=[1, 2, 3, 4, 5],
            y=[(t + 1) * random() for i in range(5)],
            name='Scatter {}'.format(t),
            mode= 'lines+markers'
            ))
    return {'data': traces}

@app.callback(Output('live-update-graph-bar', 'figure'),
              events=[Event('interval-component', 'interval')])
def update_graph_bar():

    traces = list()
    for t in range(2):
        traces.append(plotly.graph_objs.Bar(
            x=[1, 2, 3, 4, 5],
            y=[(t + 1) * random() for i in range(5)],
            name='Bar {}'.format(t)
            ))
    layout = plotly.graph_objs.Layout(
    barmode='group'
)
    return {'data': traces, 'layout': layout}


if __name__ == '__main__':
    app.run_server(debug=True)
like image 140
Maximilian Peters Avatar answered Nov 14 '22 02:11

Maximilian Peters