最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

python - Dynamically update bar values based on the selected legend series - Stack Overflow

programmeradmin3浏览0评论

I have the following code that produces a combo chart of multiple lines (time series) and a total sum in bars:

import plotly.graph_objects as go
import pandas as pd

data = {
    "Date": pd.date_range(start="2024-01-01", periods=10, freq="D"),
    "Category A": [10, 15, 13, 17, 14, 19, 21, 18, 22, 24],
    "Category B": [5, 8, 7, 9, 10, 11, 12, 14, 13, 15],
    "Category C": [7, 9, 6, 10, 12, 13, 15, 16, 18, 20]
}

df = pd.DataFrame(data)
df["Total"] = df.iloc[:, 1:].sum(axis=1)

fig = go.Figure()

fig.add_trace(go.Bar(
    x=df["Date"], 
    y=df["Total"], 
    name="Total", 
    marker=dict(color="lightgray", opacity=0.5),
    yaxis="y1"  
))

for col in df.columns[1:-1]:  
    fig.add_trace(go.Scatter(
        x=df["Date"], 
        y=df[col], 
        mode="lines+markers", 
        name=col,
        yaxis="y2" 
    ))

fig.update_layout(
    title="Dual-Axis Combo Chart: Time Series + Total Sum",
    xaxis_title="Date",
    
    yaxis=dict(
        title="Total Sum", 
        side="left",
        showgrid=False
    ),
    
    yaxis2=dict(
        title="Category Values", 
        side="right",
        overlaying="y",
        showgrid=False
    ),
    
    barmode="overlay", 
    template="plotly_white"
)

fig.show()

I would like the totals in the grey bar to update based on the selected series on the legend. As can be seen in the image below, I unclick categories B and C and the bar with the totals doesn't update.

I understand this is the expected behaviour, but I couldn't figure out a way of getting the totals bar to update based on the legend series being selected/unselected.

Is there a way of achieving this in a jupyter-notebook? I understand with Dash it should be possible, but my use case is for a notebook

I have the following code that produces a combo chart of multiple lines (time series) and a total sum in bars:

import plotly.graph_objects as go
import pandas as pd

data = {
    "Date": pd.date_range(start="2024-01-01", periods=10, freq="D"),
    "Category A": [10, 15, 13, 17, 14, 19, 21, 18, 22, 24],
    "Category B": [5, 8, 7, 9, 10, 11, 12, 14, 13, 15],
    "Category C": [7, 9, 6, 10, 12, 13, 15, 16, 18, 20]
}

df = pd.DataFrame(data)
df["Total"] = df.iloc[:, 1:].sum(axis=1)

fig = go.Figure()

fig.add_trace(go.Bar(
    x=df["Date"], 
    y=df["Total"], 
    name="Total", 
    marker=dict(color="lightgray", opacity=0.5),
    yaxis="y1"  
))

for col in df.columns[1:-1]:  
    fig.add_trace(go.Scatter(
        x=df["Date"], 
        y=df[col], 
        mode="lines+markers", 
        name=col,
        yaxis="y2" 
    ))

fig.update_layout(
    title="Dual-Axis Combo Chart: Time Series + Total Sum",
    xaxis_title="Date",
    
    yaxis=dict(
        title="Total Sum", 
        side="left",
        showgrid=False
    ),
    
    yaxis2=dict(
        title="Category Values", 
        side="right",
        overlaying="y",
        showgrid=False
    ),
    
    barmode="overlay", 
    template="plotly_white"
)

fig.show()

I would like the totals in the grey bar to update based on the selected series on the legend. As can be seen in the image below, I unclick categories B and C and the bar with the totals doesn't update.

I understand this is the expected behaviour, but I couldn't figure out a way of getting the totals bar to update based on the legend series being selected/unselected.

Is there a way of achieving this in a jupyter-notebook? I understand with Dash it should be possible, but my use case is for a notebook

Share Improve this question edited Feb 17 at 9:52 Daniel MB asked Feb 17 at 7:18 Daniel MBDaniel MB 335 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 1

Interactive renderers display figures using Plotly.js, so you can actually use Javascript+Plotly.js to hook into the plotly_legendclick event and override the default behavior (see also Function Reference in JavaScript) :

const gd = document.getElementById('{plot_id}');

gd.on('plotly_legendclick', function(eventData) {
    const i = eventData.curveNumber;
    const traces = eventData.fullData;

    if (traces[i].name === 'Total') {
        // Apply default behavior
        return true;
    }

    // Show/Hide clicked trace
    traces[i].visible = traces[i].visible === true ? 'legendonly' : true;
    Plotly.restyle(gd, { visible: traces[i].visible }, [i]);

    // Update total based on visible line traces
    const visible = traces.filter(t => t.name != 'Total' && t.visible === true);
    const totalY = traces[0].y.map((_, xi)=> visible.reduce((sum, t) => sum + t.y[xi], 0));
    Plotly.restyle(gd, { y: [totalY] }, [0]);

    // Prevent default behavior
    return false;
});

To do this in python, you just need to pass the js code as a string to plotly's Figure.show() method via the post_script parameter (the js snippet(s) are executed just after plot creation) :

js = '''
const gd = document.getElementById('{plot_id}');
gd.on('plotly_legendclick', function(eventData) {...});
'''

fig.show(post_script=[js])
发布评论

评论列表(0)

  1. 暂无评论