Hi,
I have a plot with multiple line traces. I want to highlight a line when hovering over it, by increasing the line width and by changing the opacity of all other traces. This is what it looks like:
For a small number of traces, this works well. In my application however, I expect to have at least 100 traces.
I already tried implementing this both as a regular server side callback and as a client side callback but even with a client side callback, the plot updates are slow for 100 traces. I built a small example app here:
import dash
from dash import dcc, html, callback, clientside_callback, Input, Output, State, no_update
import pandas as pd
import numpy as np
import plotly
from plotly import graph_objects as go
app = dash.Dash(__name__)
app.layout = html.Div(
[
dcc.Input(id="n-curves-input", value=10, type="number", min=1),
html.Button(id="update-button", children="Update Plot"),
dcc.Graph(id="plot"),
]
)
@callback(
Output("plot", "figure"),
Input("update-button", "n_clicks"),
State("n-curves-input", "value"),
)
def update_plot(_n_clicks, n_curves):
df = pd.DataFrame()
df["curve_id"] = np.arange(n_curves)
curve_data = np.random.randn(n_curves, 45)
fig = go.Figure()
for i, row in df.iterrows():
fig.add_trace(
go.Scatter(
x=np.arange(45),
y=curve_data[i],
mode="lines+markers",
marker_color="rgba(0,0,0,0)",
line_color=plotly.colors.DEFAULT_PLOTLY_COLORS[i % len(plotly.colors.DEFAULT_PLOTLY_COLORS)],
customdata=[row["curve_id"]] * 45,
hovertemplate="<b>%{customdata}</b><br><br>",
line_width=2
)
)
fig.update_layout(height=800)
return fig
# server side callback
# @callback(
# Output("plot", "figure", allow_duplicate=True),
# Input("plot", "hoverData"),
# State("plot", "figure"),
# prevent_initial_call=True,
# )
# def highlight_curve(hover_data, fig):
# if hover_data is None:
# return no_update
# curve_id = hover_data["points"][0]["customdata"]
# for trace in fig["data"]:
# if trace["customdata"][0] == curve_id:
# trace["line"]["width"] = 4
# trace["opacity"] = 1
# else:
# trace["line"]["width"] = 2
# trace["opacity"] = 0.4
# return fig
# client side callback
clientside_callback(
"""
function(hoverData, fig) {
if (!hoverData) {
return window.dash_clientside.no_update;
}
const curveId = hoverData.points[0].customdata;
for (let i = 0; i < fig.data.length; i++) {
if (fig.data[i].customdata[0] === curveId) {
fig.data[i].line.width = 4;
fig.data[i].opacity = 1;
} else {
fig.data[i].line.width = 2;
fig.data[i].opacity = 0.4;
}
}
return {...fig};
}
""",
Output("plot", "figure", allow_duplicate=True),
Input("plot", "hoverData"),
State("plot", "figure"),
prevent_initial_call=True,
)
if __name__ == "__main__":
app.run_server(debug=True)
Do you have any idea for improving the performance of this so that the highlighting feels smooth for ~100-200 traces?