Extend (or append) data instead of update

Hi @Mike3. Sorry, work has kept me away. Unfortunately, I have bad news regarding your request. If you manually play around with the Plotly.js library, you’ll see that Plotly.extendTraces is only intended to extend existing traces. That is, any (and all) keys (e.g. x or y) must be array attributes.

Trying to add name to your dict (either in dash-core-components or dash-extendable-graph) will generate this console error:

Error: cannot extend missing or non-array attribute: name

So, it’s recommended that you specify the trace names at initialization.

...
app.layout = html.Div([ 
   ...
   dcc.Graph( ...
      ...
      data:[{'name': 'trace-1-will-extend', 'x': [], 'y': []},
            {'name': 'trace-2-will-not-extend', 'x': [], 'y': []},
            ...

Thank you very much for explicitly confirming that. Since reading that, I have chosen to work with your original library, and it has worked for me without a lot of the hitches that the current plotly/Dash implementation forces. Quite honestly, it is remarkable how strightforwardly your original library works.

In that vein, I have a question about reloading indices (along somewhat similar lines as what nathand was exploring).

When my app first loads, I feed my deg.ExtendableGraph() graph a bunch of figures, and I have found that this works for creating multiple traces, with the following being a snippet of what is sent on the first load:

[Histogram({
    'autobinx': False,
    'histnorm': 'probability density',
    'legendgroup': 'Group 1',
    'marker': {'color': 'rgb(31, 119, 180)'},
    'name': 'Group 1',
    'opacity': 0.7,
    'uid': '04fd38bf-ea9a-400c-9b94-880aead9e16d',
    'x': [-0.4999999999999999, 0.375, 0.9099999999999999, 0.9099999999999999],
    'xaxis': 'x',
    'xbins': {'end': 0.9099999999999999, 'size': 0.2, 'start': -0.4999999999999999},
    'yaxis': 'y'
}), Histogram({
    'autobinx': False,
    'histnorm': 'probability density',
    'legendgroup': 'Group 2',
    'marker': {'color': 'rgb(255, 127, 14)'},
    'name': 'Group 2',
    'opacity': 0.7,
    'uid': '74d73e34-6078-48c0-b0cc-12cdd4b4bdf9',
    'x': [0.0, 0.3333333333333333, 0.0, 0.0],
    'xaxis': 'x',
    'xbins': {'end': 0.3333333333333333, 'size': 0.2, 'start': 0.0},
    'yaxis': 'y'
}), Scatter({
    'legendgroup': 'Group 1',
    'marker': {'color': 'rgb(31, 119, 180)'},
    'mode': 'lines',
    'name': 'Group 1',
    'showlegend': False,
    'uid': '5f9b0d80-ab0e-49fd-a852-aeda703edab2',
    'x': [-0.4999999999999999, -0.4971799999999999, -0.4943599999999999, ...,
          0.9015399999999999, 0.9043599999999997, 0.9071799999999998],
    'xaxis': 'x',
    'y': array([0.24960125, 0.25015044, 0.25069791, ..., 0.51428571, 0.51358318,
                0.51286951]),
    'yaxis': 'y'
}), Scatter({
    'legendgroup': 'Group 2',
    'marker': {'color': 'rgb(255, 127, 14)'},
    'mode': 'lines',
    'name': 'Group 2',
    'showlegend': False,
    'uid': '92d21007-e1ae-405a-902c-691dc684843e',
    'x': [0.0, 0.0006666666666666666, 0.0013333333333333333, ...,
          0.3313333333333333, 0.332, 0.3326666666666666],
    'xaxis': 'x',
    'y': array([2.39310609, 2.39341321, 2.3936584 , ..., 0.86542708, 0.8644372 ,
                0.86343766]),
    'yaxis': 'y'
})]

The above is two data sources creating two graphs each. When I update any of the traces (let’s say trace 0, so the very first one), I send to the same graph [[{'x': [50.0]}], [0]], and this works successfully.

However, when I want to reload all my traces, and essentially wipe the slate clean of all the traces and simply send new ones in, I have not figured out how to do that. Whenever I send another chunk of figures like above, I get the following error, attribute: autobinx index: 0 must be an array, which I do not know how to diagnose.

How could I decide to reload my graph, and start from scratch, to send in completely new data?

I think the easiest way to accomplish what you’re suggesting is through two different callbacks. Use the extendData prop for updating the graph with new data, and use figure for clearing the trace. Something like below:

app.layout = html.Div([
    html.Div([
        html.P('extend trace 0, add+extend trace 1'),
        deg.ExtendableGraph(
            id='extendablegraph_example1',
            figure=dict(
                data=[{'x': [0, 1, 2, 3, 4], 'y': [0, .5, 1, .5, 0]}],
            )
        ),
        html.Button("Reset", id='reset-graph'),
    ]),
    dcc.Interval(
        id='interval_extendablegraph_update',
        interval=1000,
        n_intervals=0),
])

@app.callback(Output('extendablegraph_example1', 'figure'),
              [Input('reset-graph', 'n_clicks')])
def reset_graph(click):
    if click is None:
        raise PreventUpdate
    return dict(data=[{'x': [0, 1, 2, 3, 4], 'y': [0, .5, 1, .5, 0]}])


@app.callback(Output('extendablegraph_example1', 'extendData'),
              [Input('interval_extendablegraph_update', 'n_intervals')],
              [State('extendablegraph_example1', 'figure')])
def update_extend_then_add(n_intervals, existing):
    x_new = existing['data'][0]['x'][-1] + 1

    return [dict(x=[x_new], y=[random.random()]),
            dict(x=[x_new - .5, x_new], y=[random.random(), random.random()])
            ], [1, 0]

The Plotly.js library does have a deleteTraces() function which could be used to remove/clear individual traces added to a plot. Could be done as a new property, something like creating a resetData property. Not sure if it’s a huge use case, let me know if that’s something that is really important to you. You might be the only person using the component other than myself!

5 Likes

Check this…Python list append Vs. extend

1 Like

I’m using the extendData property successfully on my graphs. Just wondering is there any other properties that can be added to the output?

For example, I’d like to change the color of the markers in the extended data… is that possible?

Thanks.

1 Like

The answer appears to be “no.” As brad previously stated in this thread:

brad:

If you manually play around with the Plotly.js library, you’ll see that Plotly.extendTraces is only intended to extend existing traces. That is, any (and all) keys (e.g. x or y ) must be array attributes.

It seems that trying to set “color” (which is set in the “marker” dictionary of a trace) when extending a trace will invoke a similar error as when trying to set name.

Somebody please correct me if I’m wrong! This is a topic that is also of great interest to me.

‘extendData’ works perfectly for me, but I have one problem, because when graph is updated, plotly tends to show all data. Is there a way to present fixed range of data, like last 100 inputs…?

Plotly Layout has an option to set X axis range in the beggining, but you need to manually set ranges like [500,1000].

@tomazmalika Yeah, that’s a pretty common use case and luckily supported out of the box by both components (dcc.Graph as well as the dash-extendable-graph component talked about in this thread).

From the dash Graph docs:

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 Reference the Plotly.extendTraces API for full usage: Function reference in JavaScript

dash-extendable-graph has a similar API. See the README for usage example:

@app.callback(Output('extendablegraph_example', 'extendData'),
              [Input('interval_extendablegraph_update', 'n_intervals')],
              [State('extendablegraph_example', 'figure')])
def update_extendData(n_intervals, existing):
    x_new = existing['data'][0]['x'][-1] + 1
    y_new = random.random()
    return [dict(x=[x_new], y=[y_new])], [0], 100
1 Like

Hi @brad

I completely missunderstood the maxPoints and this is exactly what I was looking for.

I applied this with dcc.Interval input and I get out dynamicaly chaning graph, with ranges I like. However, when I zoom out on graph with mouse (see right figure), all previous data is missing. Would you perhaps know if there is any simple solution for this?

@tomazmalika
Not a simple solution that I’m aware of. I don’t think what you’re asking for is supported out of the box.

If you want to preserve the trace data, the way to fix this is through changing the figure layout. This means there’ll be an extra callback, which can get annoying.

Personally, I’ve been achieving “windowed zoom on latest” functionality via clientside callback that handles the x-axis zoom through calls directly to Plotly.relayout(). I posted an old example of how this can be achieved here: https://github.com/bcliang/dash-clientside-demo
The demo app is still deployed to heroku:

1 Like

Great, thank you very much.

Hi markm,

Have you been able to find a workaround for this yet? I have the exact same problem. I’d like to use extendData with a bubble chart. To effectively use a bubble chart I need to pass the marker sizes through extendData, but I haven’t been able to get it right. I’m only able to pass x and y successfully, and no other information. Does extendData only support x and y, or would we be able to pass additional data through?

1 Like

Hi, I know this post is old but my graph only plots accurately whenever I am on the page if I move to another app(with the page kept opened) it skips or doesn’t plot perfectly. I followed the same approach as the above code.
This is the violation I get in the console

async-plotlyjs.v1_16_0m1617903285.js:2 [Violation] Added non-passive event listener to a scroll-blocking 'wheel' event. Consider marking event handler as 'passive' to make the page more responsive. See https://www.chromestatus.com/feature/5745543795965952

Maybe this is the reason why it only plots when I am on the page. Any help would be appreciated.

Edit: I tried with both dash_extendable_graph.ExtendableGraph and dash_core_components.Graph but no luck the violation is blocking the update of the graph when I move to another program.

I just want to be more clear on my side so here is the thing.

1.I Run the code for extendable graphs.
2.As long as I am on the tab which contains the graphs everything works fine.
3.as soon as I shift tabs let’s say from the graph tab to YouTube it is blocking the callback.

due to which i am loosing data points in the graph as shown below.
image

can anyone suggest a way to make event handler passive?

can also suggest any other way if possible.