[Explainer] How to update markers via 'extendData' Output callback

I’d like to share with Plotly community how I’ve been able to update Plotly marker properties using a minimal working example. After studying Plotly.JS and with trial and error I figured out how to extend dictionary values inside figures. Here’s my working example:

import numpy as np
import dash
from dash import html, dcc, Input, Output
import plotly.graph_objects as go
import dash_extendable_graph as deg

# simple linear equation
NUM = 30
X = np.linspace(0, NUM, NUM+1)
Y = 2 * X + 10
# randomly change size
rng = np.random.default_rng(seed=42)
S = rng.integers(low=10, high=40, size=NUM)
# color alternates red and green
C = ["red", "green"] * 250
# create scatter trace
trace = go.Scatter(
        x=X,
        y=Y,
        mode='markers',
        marker=dict(size=S, color=C, showscale=True)
        )
# layout
layout = go.Layout(title="y=2x+10 plot")
# figure
fig = go.Figure(data=trace, layout=layout)

# instantiate dash app
app = dash.Dash()
# configure dash app layout using dash_extendable_graph library
app.layout = html.Div([
        html.Div([
            deg.ExtendableGraph(
                id='graph-extendable',
                figure=fig
            ),
        ]),
        dcc.Interval(
            id='interval-graph-update',
            interval=500,
            n_intervals=NUM+1),
        ])

# NOTE: no brackets around output
@app.callback(Output('graph-extendable', 'extendData'),
              Input('interval-graph-update', 'n_intervals')
              )
def extend_scatter_trace(n_intervals):
        # sample equation to update x, y values
        x_new = [n_intervals]
        y_new = [2 * n_intervals + 10]
        # update marker size randomly
        marker_size_new = rng.integers(low=10, high=40, size=1)
        # update marker color to orange if odd else indigo so it's obvious
        marker_color_new = ["orange" if n_intervals % 2 else "indigo"]
        return ([
            {'x':x_new,
            'y':y_new,
            'marker.color':marker_color_new,
            'marker.size': marker_size_new}
        ], [0], NUM)

app.run_server(debug=True)

I hope it’s found to be useful for folks who are trying to implement similar thing. I haven’t tried other properties but assume it still works. Please share here if you try similar properties!

Limitation: This solution only works when extendData maxPoints return value is less or equal to original plot’s number of points.

1 Like