0

I'm plotting datasets with plotly express as sunburst charts. One thing I'm trying to achieve is the possibility to select the values to be plotted so that the plot gets updated if the values change meaning that a different column in the dataframe is selected.

I've created an example based on this sunburst example in the official docs https://plotly.com/python/sunburst-charts/#sunburst-of-a-rectangular-dataframe-with-plotlyexpress

There the column 'total_bill' is selected for plotting and that works. I can recreate the plot in that example. Now I would like to use updatemenus to switch that to the 'tip' column that also holds floats and should be usable.

The example code I've tried:

import plotly.express as px

df = px.data.tips()

updatemenus = [{'buttons': [{'method': 'update',
                             'label': 'total_bill',
                             'args': [{'values': 'total_bill'}]
                              },
                            {'method': 'update',
                             'label': 'tip',
                             'args': [{'values': 'tip'}]
                             }],
                'direction': 'down',
                'showactive': True,}]

fig = px.sunburst(df, path=['day', 'time', 'sex'], values='total_bill')
fig.update_layout(updatemenus=updatemenus)
fig.show()

Now this will successfully plot the same plot as in the example, but when I select back and forth between the two updatemenu options, it doesn't behave properly. I've also tried to use Series everywhere, but the results is the same. I've also looked at this example, which has a similar focus Plotly: How to select graph source using dropdown? but the answers there didn't solve my problem either, since the sunburst in some way seems to behave differently from the scatter plot?

Any idea how to get this working?

2 Answers2

1
  • similar to solution you arrived at. Use Plotly Express to build all the figures, collect into a dict
  • menu can now be built with dict comprehension
import plotly.express as px

df = px.data.tips()

# construct figures for all columns that can provide values
figs = {
    c: px.sunburst(df, path=["day", "time", "sex"], values=c)
    for c in ["total_bill", "tip"]
}

# choose a column that becomes the figure
fig = figs["total_bill"]
# now build menus, that use parameters that have been pre-built using plotly express
fig.update_layout(
    updatemenus=[
        {
            "buttons": [
                {
                    "label": c,
                    "method": "restyle",
                    "args": [
                        {
                            "values": [figs[c].data[0].values],
                            "hovertemplate": figs[c].data[0].hovertemplate,
                        }
                    ],
                }
                for c in figs.keys()
            ]
        }
    ]
)
Rob Raymond
  • 29,118
  • 3
  • 14
  • 30
  • Using a dictionary here is a really good idea. Thanks for the suggestion. :) Specifically for the sunburst plot, it might however be a good idea to also transfer names, ids and parents. In your example it shouldn't make a difference, but it can be useful if the path should be changed by the drop down menu. – Bernd Steinhauser Oct 21 '21 at 06:29
  • agreed - should be a case of using more parameters in the **args** – Rob Raymond Oct 21 '21 at 07:33
0

Ok, I found one solution that works, but maybe someone could point me towards a "cleaner" solution, if possible. I stumbled across this question that is actually unrelated: Plotly: How to create sunburst subplot using graph_objects?

There, one solution was to save the figure data of a plotly express figure and reuse it in a graph objects figure. This gave me an idea, that I could maybe save the data of each figure and then reuse (and update/modify) it in a third figure. And, as it turns out, this works!

So here is the working example:

import plotly.express as px

df = px.data.tips()

# create two figures based on the same data, but with different values
fig1 = px.sunburst(df, path=['day', 'time', 'sex'], values='total_bill')
fig2 = px.sunburst(df, path=['day', 'time', 'sex'], values='tip')
# save the data of each figure so we can reuse that later on
ids1 = fig1['data'][0]['ids']
labels1 = fig1['data'][0]['labels']
parents1 = fig1['data'][0]['parents']
values1 = fig1['data'][0]['values']
ids2 = fig2['data'][0]['ids']
labels2 = fig2['data'][0]['labels']
parents2 = fig2['data'][0]['parents']
values2 = fig2['data'][0]['values']

# create updatemenu dict that changes the figure contents
updatemenus = [{'buttons': [{'method': 'update',
                             'label': 'total_bill',
                             'args': [{
                                  'names': [labels1],
                                  'parents': [parents1],
                                  'ids': [ids1],
                                  'values': [values1]
                                 }]
                              },
                            {'method': 'update',
                             'label': 'tip',
                             'args': [{
                                  'names': [labels2],
                                  'parents': [parents2],
                                  'ids': [ids2],
                                  'values': [values2]
                                 }]
                             }],
                'direction': 'down',
                'showactive': True}]

# create the actual figure to be shown and modified
fig = px.sunburst(values=values1, parents=parents1, ids=ids1, names=labels1, branchvalues='total')
fig.update_layout(updatemenus=updatemenus)
fig.show()