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 badges1 Answer
Reset to default 1Interactive 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])