How to show the last n seconds of high refresh streaming data

Hello amazing Plotly community,

I have high refresh data (~100ms). I’m using a combination of the app.clientside_callback and extendData attribute to plot high refresh rate incoming data.

However, due to high amount of data, the graph gets very slow quickly. So, I just want to plot the last n seconds (e.g. last 10 seconds) of the data.
I’m not sure if this is possible with the extendData attribute or not.

Any suggestions here?

data_cache = [deque(maxlen=7_500) for _ in range(2)]  # the incoming data fills data_cache

def fetch_data(signal_index):
    cache_copy = list(data_cache[signal_index])
    data_cache[signal_index].clear()
    return [(datetime.fromtimestamp(t), v) for t, v in cache_copy]

app = Dash(__name__, update_title=None)

app.layout = dbc.Container(
    [
        dcc.Interval(id="fetch-interval", interval=250, n_intervals=0),
        dcc.Interval(id="graph-interval", interval=15, n_intervals=0),
        dbc.Row(
            [
                dbc.Col(
                    [
                        dcc.Graph(
                            id="live-graph-ecg-ii",
                            figure={
                                "data": [
                                    {
                                        "x": [],
                                        "y": [],
                                        "mode": "lines",
                                        "type": "scattergl",
                                        "name": "ECG_II",
                                    },
                                ],
                                "layout": {
                                    "xaxis": {
                                        # "range": [0, 15],
                                    },
                                    "yaxis": {"title": "Plot 1"},
                                },
                            },
                        ),
                        dcc.Store(id="data-fetch-store-1", data=[]),
                    ],
                    width=8,
                ),
            ],
            justify="center",
        ),
        dbc.Row(
            [
                dbc.Col(
                    [
                        dcc.Graph(
                            id="live-graph-2",
                            figure={
                                "data": [
                                    {
                                        "x": [],
                                        "y": [],
                                        "mode": "lines",
                                        "type": "scattergl",
                                        "name": "Plot 2",
                                    },
                                ],
                                "layout": {
                                    "yaxis": {"title": "Plot 2"},
                                },
                            },
                        ),
                        dcc.Store(id="data-fetch-store-2", data=[]),
                    ],
                    width=8,
                ),
            ],
            justify="center",
        ),
    ],
    fluid=True,
)

@app.callback(Output("data-fetch-store-1", "data"), [Input("fetch-interval", "n_intervals")])
def fetch_data_1(_):
    return fetch_data(0)


@app.callback(Output("data-fetch-store-2", "data"), [Input("fetch-interval", "n_intervals")])
def fetch_data_2(_):
    return fetch_data(1)

update_graph_js = """
    function updateGraph (n_intervals, data) {
        if (data && Array.isArray(data) && data.length > 0) {
            var x = data.map(d => d[0]);
            var y = data.map(d => d[1]);
            return { "x": [[...x, null]], "y": [[...y, null]] };
        }
        return window.dash_clientside.no_update;
    }
"""

app.clientside_callback(
    update_graph_js,
    Output("live-graph-1", "extendData"),
    [Input("graph-interval", "n_intervals")],
    [State("data-fetch-store-1", "data")],
)

app.clientside_callback(
    update_graph_js,
    Output("live-graph-2", "extendData"),
    [Input("graph-interval", "n_intervals")],
    [State("data-fetch-store-2", "data")],
)


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

Just thinking, but instead of extending your data, could you just extract the current figure, delete x and y data you don’t need, add your data new point and update the figure?

All clientside, obviously.

Thanks @AIMPED for the suggestion. I’m going to try that. The main reason for using extendData is for high-refresh data as figure attribute will redo the figure at each callback.