Live Graphs - Data Visualization GUIs with Dash and Python p.4




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:

The next tutorial:





  • Intro - Data Visualization Applications with Dash and Python p.1
  • Interactive User Interface - Data Visualization GUIs with Dash and Python p.2
  • Dynamic Graph based on User Input - Data Visualization GUIs with Dash and Python p.3
  • Live Graphs - Data Visualization GUIs with Dash and Python p.4
  • Vehicle Data App Example - Data Visualization GUIs with Dash and Python p.5
  • Out of the Box Sentiment Analysis options with Python using VADER Sentiment and TextBlob
  • Streaming Tweets and Sentiment from Twitter in Python - Sentiment Analysis GUI with Dash and Python p.2
  • Reading from our sentiment database - Sentiment Analysis GUI with Dash and Python p.3
  • Live Twitter Sentiment Graph - Sentiment Analysis GUI with Dash and Python p.4
  • Dynamically Graphing Terms for Sentiment - Sentiment Analysis GUI with Dash and Python p.5
  • Deploy Dash App to a VPS web server - Data Visualization Applications with Dash and Python p.11