[Python] How do I filter a Sunburst Chart by clicking on one of the segments?

Discussão em 'Python' iniciado por Stack, Julho 21, 2025.

  1. Stack

    Stack Membro Participativo

    This question is adjacent to a previous question I asked about filtering a table by clicking on a bar segment. I'd like to do the same thing with a sunburst, but don't seem to be able to get the callback to fire off with with on_click or on_hover.

    [​IMG]


    • I have set up sunburst_chart() in the server function that cycles through traces (there appears to be only one trace for this type of chart)


    • Each trace has on_click and on_hover applied with callback instructions to execute setHoverValues and setClickValues respectively.


    • setHoverValues() and setClickValues() each dump the value of the callback attribute 'points' into a reactive value.


    • The reactive values is display as a text widget.

    I've tried to set breakpoints on the trace.on_hover() and trace.on_click(), and they stop once during program execution, but don't stop there again. Setting breakpoints anywhere inside setHoverValues() or setClickValues() doesn't do anything -- execution never stops there.

    from shiny import App, reactive, render, ui
    from shinywidgets import output_widget, render_widget
    import plotly.graph_objects as go
    import plotly.express as px
    import pandas as pd

    df=pd.DataFrame({'Category':['Utilities','Rent','Insurance','Gas','Parking','Maintenance','Groceries','Restaurants'],
    'Category_L2':['Home','Home','Home','Car','Car','Car','Food','Food']})

    app_ui = ui.page_fluid(
    ui.output_text_verbatim('hoverInfoOutput'),
    ui.output_text_verbatim('clickInfoOutput'),
    output_widget('sunburst_chart'),
    )


    def server(input, output, session):

    hoverInfo = reactive.value({})
    clickInfo = reactive.value({})

    def setHoverValues(trace, points, selector):
    if not points.point_inds:
    return
    hoverInfo.set(points.copy())

    @render.text
    def hoverInfoOutput():
    return hoverInfo.get()

    def setClickValues(trace, points, selector):
    if not points.point_inds:
    return
    clickInfo.set(points.copy())

    @render.text
    def clickInfoOutput():
    return hoverInfo.get()

    @render_widget
    def sunburst_chart():
    # Prepare data for the Sunburst chart
    # We need labels, parents, values, and ids for each level

    labels = []
    parents = []
    values = []
    ids = []

    # 1. Add Category_L2 (inner ring) data
    # Count the occurrences of each Category_L2 to determine its size
    l2_counts = df['Category_L2'].value_counts()

    for l2_category, count in l2_counts.items():
    labels.append(l2_category)
    parents.append("") # Category_L2 are root parents for this hierarchy
    values.append(count)
    ids.append(f"L2_{l2_category}") # Unique ID for each L2 category

    # 2. Add Category (outer ring) data
    # Each Category instance contributes a value of 1 to its parent Category_L2
    for index, row in df.iterrows():
    category = row['Category']
    category_l2 = row['Category_L2']
    labels.append(category)
    parents.append(f"L2_{category_l2}") # Parent is the corresponding Category_L2
    values.append(1) # Each individual category entry has a value of 1
    ids.append(f"Cat_{category}") # Unique ID for each Category

    # Create the Plotly Sunburst chart
    fig = go.Figure(go.Sunburst(
    ids=ids,
    labels=labels,
    parents=parents,
    values=values,
    # Set the branch values to 'total' to ensure parent values are sums of children
    branchvalues="total",
    # Customize hover info
    hovertemplate='<b>%{label}</b><br>Count: %{value}<extra></extra>',

    ))

    # Update layout to make it a donut chart
    fig.update_layout(
    title_text='Two-Level Donut Chart: Category vs. Category_L2',
    margin=dict(t=50, l=0, r=0, b=0), # Adjust margins for better fit
    # Set the hole to create the donut effect (0.5 is a good starting point)
    uniformtext_minsize=10, uniformtext_mode='hide' # Hide text if too small
    )
    figWidget = go.FigureWidget(fig)

    for trace in fig.data:
    trace.on_hover(setHoverValues)
    trace.on_click(setClickValues)
    return figWidget

    app = App(app_ui, server)

    Continue reading...

Compartilhe esta Página