Black Lives Matter. Please consider donating to Black Girls Code today.
Dash Enterprise delivers an incredible 21x cost savings 💸Download the e-book!

Incremental plot data

My dash application query data from a mongodb server based upon the time period selected by the user. The time period selection is implemented using a dropdown list. ‘3 minutes’, ‘5 minutes’,‘10 minutes’…‘1 hour’, ‘2 hours’…‘6 hours’…‘12 hours’…‘1 day’,‘2 days’…


Imagine the following sequence:

  1. User select ‘6 hours’ first, and the dash application reads the 6 hour data from mongodb and send the data to the client for plotting.
  2. The user see the plot corresponding to the ‘6 hour’ data and want to see more data. So he select ‘12 hours’ from the dropdown. What happen next is that dash reads the 12 hour data from the mongodb and sends 12 hour data back to the client.

Here comes the question: After 1), the client already has the first 6 hour data. When he select ‘12 hours’ what he needs is the other 6 hour data. I didn’t see any option regarding this sort of incremental data in dash/plotly, is there?

I recommend just re-reading the data from your database every time.

If you can’t do that, then you could “cache” the results in either a hidden div or in the plot. This will complicate the logic in your app.

One concept that might be useful is dash.dependencies.State - similar to dash.dependencies.Input it provides the current value of the attribute but it doesn’t fire the callback when it changes. Combine this with an dash.dependencies.Input to get the previously selected figure alongside the value from the dropdown:

@app.callback(Output('graph', 'figure'), [Input('dropdown', 'value'], state=[State('graph', 'figure']):
def update_graph(hours, previous_figure):
    previously_queried_data = previous_figure['data'][0]['x']
    if hours * 60 < len(previously_queried_data):
        # we're looking at a smaller region, so grab the previous data
        new_data = previously_queried_data[hours*60, :]
    else:
       # need to query more data
       # ...

If you are graphing a (relatively) small amount of data, then you could just update the range of the plot and not change the data. Something like:

@app.callback(Output('graph', 'figure'), [Input('dropdown', 'value'], state=[State('graph', 'figure']):
def update_graph(hours, previous_figure):
    if previous_figure is None:
        # first time - need to compute the figure from data in the database
        data = query_data(days=5) # query the _maximum_ amount of data possible and return it to the client
        figure = {'data': [{'x': data['x'], 'y': data['y']}]} # or w/e
    else:
        now = datetime.datetime.now()
        previous = now - datetime.timedelta(hours=hours)
        previous_figure['layout']['xaxis']['range'] = [previous, now]
        return previous_figure
1 Like

Thanks @chriddyp

That give me new ideas for improving the performance of my dash application.
Regarding cache the previously queried data at server side, that’s exactly something in my mind for long. However, I didn’t realize the ‘state’ of the graph object can be used to cache the data. The first approach you provided (with littler coding effort) will definitely improve the performance when the user select shorter period from previously longer period. I appreciate that.
Sorry, I forgot to mention the data size of my application is about 8MB/24 hour. So it’s really a trade off that how many data is queried a time and where to cache the super set of the possible queried data in order to reduce the frequency of database query.

1 Like