Welcome to part four of the web-based data visualization with Dash tutorial series. In this tutorial, we're going to be create live updating graphs with Dash and Python. Live graphs can be useful for a variety of tasks, but I plan to use live graphs to display data from sensors that are constantly collecting information. To begin, let's make some imports:
import dash from dash.dependencies import Output, Event 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
Most of this should make sense to you except maybe the last 2 imports. We're going to import plotly.graph_objs
since that's the way I've found to set axis limits for charts. There's probably a way to do it without that import, I just don't know it! Next, we're importing deque
, which is a nifty container that comes with the ability to set a size limit (maxlen). Once the deque
container is full, any subsequent appends will pop the first element(s) as required to meet the constraint.
Next, let's start our sample data. We're just going to create some random data for the sake of an example:
X = deque(maxlen=20) X.append(1) Y = deque(maxlen=20) Y.append(1)
From here, we'll append random movements to simulate some data. Next, let's setup the app itself:
app = dash.Dash(__name__) app.layout = html.Div( [ dcc.Graph(id='live-graph', animate=True), dcc.Interval( id='graph-update', interval=1*1000 ), ] )
A graph is here as usual, only, this time, just with an ID, and animate
set to true. Below this, we have a dcc.Interval
, which will specify how frequently this div is to be updated. Now, all we need is some sort of function that updates the element with the id
of live-graph
. We've done this before with the input/output from the text field. In this case though, we don't actually need any input, just output. Just because we don't have any input, doesn't mean we don't still need some sort of trigger for this function to run, however. This trigger is called an event
. In our case, the event
is actually just the interval
that we've set to run with the id
of graph-update
. Thus, we need to make a function which outputs to live-graph
, and is triggered by an event with the id
of graph-update
. Our decorator/wrapper will thus be:
@app.callback(Output('live-graph', 'figure'), events=[Event('graph-update', 'interval')])
Continuing on this, let's add some random data in. Maybe you'll use a database, or maybe some .csv or .txt file. Who knows.
@app.callback(Output('live-graph', 'figure'), events=[Event('graph-update', 'interval')]) def update_graph_scatter(): X.append(X[-1]+1) Y.append(Y[-1]+Y[-1]*random.uniform(-0.1,0.1))
Now that we've added some new data every time this function is run, we also want to go ahead and graph it. This will be your typical plotly graph:
data = plotly.graph_objs.Scatter( x=list(X), y=list(Y), name='Scatter', mode= 'lines+markers' )
Note that we need to pass a list for x and y, we can't keep the deque
object.
Finally, all we need to do is return something that fully populates a "graph" element in dash. Recall the example from part 1:
dcc.Graph( id='example', figure={ 'data': [ {'x': [1, 2, 3, 4, 5], 'y': [9, 6, 2, 1, 5], 'type': 'line', 'name': 'Boats'}, {'x': [1, 2, 3, 4, 5], 'y': [8, 7, 2, 7, 3], 'type': 'bar', 'name': 'Cars'}, ], 'layout': { 'title': 'Basic Dash Example' } } )
We already have the dcc.Graph
, which already has an id, so we really just need that figure part. Thus:
return {'data': [data],'layout' : go.Layout(xaxis=dict(range=[min(X),max(X)]), yaxis=dict(range=[min(Y),max(Y)]),)}
The full function now:
@app.callback(Output('live-graph', 'figure'), events=[Event('graph-update', 'interval')]) def update_graph_scatter(): X.append(X[-1]+1) Y.append(Y[-1]+Y[-1]*random.uniform(-0.1,0.1)) data = plotly.graph_objs.Scatter( x=list(X), y=list(Y), name='Scatter', mode= 'lines+markers' ) return {'data': [data],'layout' : go.Layout(xaxis=dict(range=[min(X),max(X)]), yaxis=dict(range=[min(Y),max(Y)]),)}
Full code up to this point:
import dash from dash.dependencies import Output, Event 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 X = deque(maxlen=20) X.append(1) Y = deque(maxlen=20) Y.append(1) 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'), events=[Event('graph-update', 'interval')]) def update_graph_scatter(): X.append(X[-1]+1) Y.append(Y[-1]+Y[-1]*random.uniform(-0.1,0.1)) data = plotly.graph_objs.Scatter( x=list(X), y=list(Y), name='Scatter', mode= 'lines+markers' ) return {'data': [data],'layout' : go.Layout(xaxis=dict(range=[min(X),max(X)]), yaxis=dict(range=[min(Y),max(Y)]),)} if __name__ == '__main__': app.run_server(debug=True)
With the following result: