Is it possible to update just `layout`, not whole `figure` of Graph in callback?


#1

Hi, I am trying to set range of xaxis of a graph in callback function. From my understanding, the only way to do is by returning the figure property:


@app.callback(
    Output(component_id='graph_id', component_property='figure'),
    [Input(component_id='btn_custom_x_range', component_property='n_clicks')]
)
def set_custom_range(clicks):
    return {
        "data": data,
        "layout":
            {"xaxis": {
                "range": [1, 2]
            }
            ...
            }
        ...
    }

My concern is, that this way I am sending the whole data object back to the client again. I would like to avoid this, as the data collection is quite huge.

Is there a way, how to update the xaxis.range without resending all the data?


#2

Hi,

Maybe this will help…
Jimmybow wrote a custom component you can install, to do just this. You can find it at
https://github.com/jimmybow/mydcc look under “3. mydcc.Relayout”


#3

This is correct. Right now, there is not a way to update a nested attribute of a property.

Ideally, I’d like to support this more generally through two different solutions:

  1. Allow the developer to update nested properties in callbacks with something like
@app.callback(Output('my-graph', 'figure.layout.range'))
  1. Provide a way to tell which input was fired and use that in conjunction with Plotly.react to provide incremental updates to the figure, see https://github.com/plotly/plotly.js/pull/2341#issuecomment-364257543

For now, you can use jimmybow’s dcc package or you’ll just have to return the entire figure each time.


#4

Very interesting topic!
I have quite a few issues with slow response in Dash when updating minor figure layout elements or using events, probably caused by re-loading the entire data on each callback.

I have looked a bit into “build your own dash component”, thinking that some of these tasks could be transferred “back” to the faster JS side (such as updating a color, height, get hover information etc) without invoking a “costly” Dash callback (and leave those for more data related, Python-based stuff).

Now I understand (sorry, I’m new to React as well as relatively new to JS…) that I could build sort of custom-made graph components using Plotly.js and Plotly.newPlot(); (as done here: https://academy.plot.ly/react/3-with-plotly/) and work for example with Plotly.restyle or Plotly.relayout (as shown here: https://plot.ly/javascript/plotlyjs-function-reference/#plotlyrestyle).

However - here comes Plotly.react (as @chriddyp posted: https://github.com/plotly/plotly.js/pull/2341#issuecomment-364257543), which sounds to be faster and may work better/cleaner(?) together with React. BUT on the main plotly.react page it says that “This component currently creates a new plot every time the input changes” (https://github.com/plotly/react-plotly.js/)

So, my question is - what do you think is the way forward? After all Plotly.js is the base library, which ultimately should allow most customisation - yet Plotly.react seems really attractive. In terms of building a custom-made Dash component, what would be the most sensible approach?

Thanks for shedding some light onto my current Plotly confusion…!


#5

I am also interested in what will be the way forward here. Right now in my app i have a figure where I am animating a moving vertical line along the time axis. I am returning the entire figure each time, which works ok as long as the figure stays simple. However as figure get more complex the animation delay gets longer and longer.

Also mydcc.relayout works well for a simple example, but when I tried to use it in my case i get into some sort of infinite loop of callbacks, because I also i have callbacks that listen to chart relayout data and then update the chart to zoom in/out. Which leaves me not sure if the issue is with my callbacks or with mydcc.relayout and I don’t know to debug it.

this also seems ideal to me:

Allow the developer to update nested properties in callbacks with something like
@app.callback(Output(‘my-graph’, ‘figure.layout.range’))

For now it seems like I am stuck with “returning the entire figure each time”, until solution 1 or 2 is created (not soon?) or I learn more React/JS and custom components and can make some sort of custom hack.


#6

Is there any update on this issue?

Your first soluction would be perfect.

Thanks!


#7

No updates. This is actually quite a bit project and we’d likely need commercial sponsorship of the feature in order to move it forward


#8

Sorry I can not help with that. I don´t belong to any company.