Update figure (subplots) without creating a new one

Hello! I’m trying to update an existing figure. I’m making the figure as follows:

fig = make_subplots(cols=4 rows=3)
app.layout = html.Div([
dcc.Graph(
id=‘graph’)
])

with the callback:

@app.callback(Output(‘graph’, ‘figure’),
Input(‘interval’, ‘n_intervals’))
def update(n_intervals):

It works fine. The problem is when I’m trying to update the plots with extendData.
Can’t figure out what is the proper structure for this, only found how to do this for a one plot figure.
I would appreciate your help, thanks!

Hi,

Updating a chart with subplots is basically the same as updating a chart with multiple traces. From the documentation:

extendData ( list | dict ; optional): Data that should be appended to existing traces. Has the form [updateData, traceIndices, maxPoints] , where updateData is an object containing the data to extend, traceIndices (optional) is an array of trace indices that should be extended, and maxPoints (optional) is either an integer defining the maximum number of points allowed or an object with key:value pairs matching updateData

In most cases, updateData is basically a dictionary with the x and y coordinates of the new points. They should be a list of lists when updating multiple traces, where each inner list contains the new points for each respective trace.
If traceIndices is not provided, then each list will be given in ascending order to the traces. As an example, if you want to update the 1st and 3rd traces only, extendData should be:

extend_data = [
    {
        "x": [
            [0, 1], # x coord, 1st trace
            [10, 11, 12]
         ],
        "y": [
            [1.212, 2.212], # y coord, 1st trace
            [100, 200, 300]
         ], 
     },  # this is updateData
     [0, 2] # this are traceIndices, if missing, traces 0 and 1 would be updated
]

Note that the trace order in the Figure object depends on the order that you added each trace to each subplot and in principle you can have multiple traces in a single subplot. Therefore it is a good idea to take a look in figure["data"] and perhaps implement the update logic based on it.

Here’s an adaptation of an example I had for subplots:

from dash import Dash, dcc, html, Input, Output, State
import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots

app = Dash(__name__)

fig = make_subplots(cols=3, rows=1)

fig.add_scatter(
    x = [1,2,3],
    y = [100, 200, 150],
    col=1,
    row=1
)

fig.add_scatter(
    x = [1,2,3],
    y = [20, 10, 15],
    col=2,
    row=1
)
    
fig.add_scatter(
    x = [1,2,3],
    y = [20, 10, 15],
    col=3,
    row=1
)

app.layout = html.Div(
    [
        dcc.Interval(id="interval", interval=2000),
        dcc.Graph(id="graph", figure=fig)
    ]
)

@app.callback(
    Output("graph", "extendData"),
    Input("interval", "n_intervals"),
    State("graph", "figure")
)
def update(n_intervals, figure):
    print(figure)  # figure["data"] is where the traces are defined

    return [
        {
            "x": [
                [figure["data"][0]["x"][-1] + i for i in range(1, 3)],
                [figure["data"][2]["x"][-1] + i for i in range(1, 3)]
            ],
            "y": [
                [figure["data"][0]["y"][-1] + i * 50 for i in range(1, 3)],
                [figure["data"][2]["y"][-1] + i * 5 for i in range(1, 3)]
            ]
        },
        [0, 2], 
        10 # last 10 points will be kept in each trace
    ]


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

Hope that this helps!

2 Likes

Hi! This is really what I have searched for but I also want to update the plots without adding new points. I want to replace current points and only change the value in the y axis. Is it possible?

It should be possible by using the old values of “x” combined with new values of “y” in updateData and then passing maxPoints to the length of “x”. This will throw away all points prior to the update.

You right! But you need to set ‘x’ to be an empy list:

"x": [[ ], [ ]] 

instead of using the old values of “x” and then it works! Thanks a lot!

This works fine for the traces of the same kind which have the same key names in their data dictionaries. But how it’s possible to extend a graph containing non-homogeneous traces, like scatters and candlestics?