Selecting the value at the center of a sunburst plot after updating

I have used plotly to create a sunburst plot. All of this is inside a django_dash_plotly app, which as I understand it, is just a wrapper around a dash app. It works, all good.

I have created a callback so that when a segment of the sunburst plot is clicked, I can get the label of the selected section. Drilling down, this works out of the box. When I select one of the lower segments, the plot changes to center that segment and I can retrieve the value of the label.

import pandas as pd
import plotly.express as px
from django_plotly_dash import DjangoDash

def create_tree_dataframe(tree):
    <... code to generate data frame ...>

data = create_tree_dataframe(diseaseTreewithModels)
sunburst = px.sunburst(
    data,
    names='child',
    parents='parent',
    values='sampleCount',
    maxdepth=3,
    hover_data='cancerId',
)
sunburst.update_layout(margin=dict(l=0, r=0, t=0, b=0), autosize=True)
sunburst.update_traces( textfont=dict(family="Arial", size=12), insidetextorientation='radial')

app = DjangoDash('NavTest')

app.layout = html.Div([
    html.Div(id='selectedCancer'),
    dcc.Graph(id="cancerNav", figure = sunburst),
])

@app.callback(
    dash.dependencies.Output('selectedCancer', 'children'),
    [dash.dependencies.Input('cancerNav', 'clickData')],
    )
def update_output(clickData):
    print(clickData)
    if clickData is not None:
        clicked_point = clickData['points'][0]
        return "The selected value is: {}".format(clicked_point['label'])
    else:
        return "Click on a section of the sunburst plot to see its value."

Once I have selected one of the lower segments however, I can’t retrieve the value of the label when I click the center segment to go back up. ie. I start here:


I select Sarcoma and as expected I get this with the selected value in the top left being correctly reported as Sarcoma:

and the clickData looks like this:

{'points': [{'curveNumber': 0, 'pointNumber': 139, 'currentPath': 'ZERO2/', 'root': 'ZERO2', 'entry': 'ZERO2', 'percentRoot': 0.22387223311852059, 'percentEntry': 0.22387223311852059, 'percentParent': 0.22387223311852059, 'parent': 'ZERO2', 'label': 'Sarcoma', 'value': 242, 'customdata': [94]}]}

I then hit the middle section to return to the top level and I get this with the selected value in the top left still being reported as Sarcoma, rather than ZERO2:


and the clickData looks like this:

{'points': [{'curveNumber': 0, 'pointNumber': 139, 'currentPath': 'ZERO2/', 'root': 'ZERO2', 'entry': 'Sarcoma', 'percentRoot': 0.22387223311852059, 'percentEntry': 1, 'percentParent': 0.22387223311852059, 'parent': 'ZERO2', 'label': 'Sarcoma', 'value': 242, 'customdata': [94]}]}

When drilling down, the value reported is the value after the change, why is this not the case when drilling up?

I can’t for the life of me figure this out. How do I change my call back so then when hitting the middle section to drill up, the correct reported value is selected? Sadly it’s not a matter of checking to see if one is at the root of the tree or not, as the same thing happens if I go from a level 3 node to a level 2 node - the level 3 node remains in the center, so I can’t drill up.

Thanks
Ben.

Would you be able to add some data to your example to make it into a self-contained example that reproduces the issue?

1 Like

I think the value that is reported is the value in the cell that is clicked on - this is ‘Sarcoma’ when both drilling up and down in the example you give.

One thought is to examine the display state of the figure after the click action, but I don’t think that’s encoded in State('cancerNav', 'figure') (though I’m not certain about that).

So if the display state can’t be examined, another approach could be for the callback to calculate and store it (in a dcc.Store), storing enough information to be able to work out what the centre circle must be after each click. Not very elegant, I’m afraid - hopefully someone might have a better approach.

I managed to fix it in the end. What I needed was apparently “percentEntry” in the clickData.

if clickData is not None:
        if clickData["points"][0]["percentEntry"] == 1 and clickData["points"][0]["parent"] == "":
            root_label = "ZERO2"
        elif clickData["points"][0]["percentEntry"] == 1:
            root_label = clickData["points"][0]["parent"]
        else:
            root_label = clickData["points"][0]["id"]
    else:
        root_label = "ZERO2"

Found it in a plotly js version of a sunburst that a colleague had made, and eventually in a the comments from a question in 2020.

I can’t for the life of me find any documentation telling me what percentEntry is, or anything documenting why or how one needs to do this when one comes back up a tree in a sunburst plot, so I don’t know if this is how it’s supposed to be done. It does appear to work though.

If it works, go for it, but I think this code relies on clickData[“points”][0][“percentEntry”] == 1 being a reliable indicator of whether the centre circle has been clicked. It can go a bit wrong if a node has only one child, which (it appears) can lead to percentEntry being 1.0 when an outer segment is clicked. I don’t see any way round this by examining clickData however.

I can’t find any documentation on the contents of ‘clickData’ either.