Plotly Python:如何将Trace关联至多个legendgroup实现分组控制?
Plotly多子图分组批量控制Trace显示/隐藏方案
因为每个Trace确实只能绑定一个legendgroup,但可以通过以下两种方法实现分组批量控制的需求:
方案1:用Dash添加回调控制(适合可引入Dash的项目)
如果你的项目可以引入Dash框架,这是最直观的实现方式:
- 先定义好每个key对应的分组,比如一个字典
group_mapping - 添加Trace时给每个Trace加上
customdata标记所属分组 - 创建分组的图例按钮(或下拉菜单),编写回调实现批量切换
修改后的示例代码:
import dash from dash import dcc, html, Input, Output, State import plotly.graph_objects as go from plotly.subplots import make_subplots # 假设的分组映射,根据你的实际测试分组定义 group_mapping = { "test1": "Group A", "test2": "Group A", "test3": "Group B", "test4": "Group B" } groups = list(set(group_mapping.values())) colorList = ["#1f77b4", "#ff7f0e", "#2ca02c", "#d62728"] # 创建子图 fig = make_subplots(rows=2, cols=1, vertical_spacing=0.04, shared_xaxes=True) c = 0 for key, value in filtered_dict.items(): # 第一个子图的Trace,添加customdata标记分组 fig.add_trace(go.Scatter(x=eval(data_to_plot_X)[key], y=eval(data_to_plot_Y)[key], hovertemplate=hoverY1, marker=dict(color=colorList[c]), name=key, legendgroup=key, customdata=[group_mapping[key]]), row=1, col=1,) # 第二个子图的Trace,同样添加customdata fig.add_trace(go.Scatter(x=eval(data_to_plot_X)[key], y=eval(data_to_plot_Y2)[key], hovertemplate=hoverY2, marker=dict(color=colorList[c]), name=key, legendgroup=key, showlegend=False, customdata=[group_mapping[key]]), row=2, col=1,) c += 1 fig.update_layout(hovermode="x") # 初始化Dash应用 app = dash.Dash(__name__) app.layout = html.Div([ # 添加分组控制按钮 html.Div([ html.Button(f"切换{group}", id=f"btn-{group}", n_clicks=0) for group in groups ], style={"margin": "10px"}), dcc.Graph(id="fig", figure=fig) ]) # 回调函数:点击分组按钮,切换该组所有Trace的可见性 @app.callback( Output("fig", "figure"), Input("btn-Group A", "n_clicks"), Input("btn-Group B", "n_clicks"), State("fig", "figure") ) def update_group_visibility(btn_a, btn_b, fig): # 确定哪个按钮被点击 ctx = dash.callback_context if not ctx.triggered: return fig button_id = ctx.triggered[0]["prop_id"].split(".")[0] target_group = button_id.split("-")[1] # 遍历所有Trace,切换目标分组的可见性 for trace in fig["data"]: if trace["customdata"][0] == target_group: trace["visible"] = not trace["visible"] return fig if __name__ == "__main__": app.run_server(debug=True)
方案2:纯Plotly注入JavaScript实现(无需Dash)
如果不想引入Dash,可以通过给Plotly图表注入自定义JS代码来实现:
核心思路是添加虚拟Trace作为分组图例,然后监听图例点击事件,找到对应分组的所有真实Trace并切换可见性。
修改后的示例代码:
import plotly.graph_objects as go from plotly.subplots import make_subplots # 定义分组映射 group_mapping = { "test1": "Group A", "test2": "Group A", "test3": "Group B", "test4": "Group B" } groups = list(set(group_mapping.values())) group_colors = {"Group A": "#1f77b4", "Group B": "#ff7f0e"} colorList = ["#1f77b4", "#ff7f0e", "#2ca02c", "#d62728"] # 创建子图 fig = make_subplots(rows=2, cols=1, vertical_spacing=0.04, shared_xaxes=True) c = 0 for key, value in filtered_dict.items(): # 给真实Trace添加分组标记(通过name包含分组前缀) trace_name = f"{group_mapping[key]}|{key}" # 第一个子图的Trace fig.add_trace(go.Scatter(x=eval(data_to_plot_X)[key], y=eval(data_to_plot_Y)[key], hovertemplate=hoverY1, marker=dict(color=colorList[c]), name=trace_name, legendgroup=key), row=1, col=1,) # 第二个子图的Trace fig.add_trace(go.Scatter(x=eval(data_to_plot_X)[key], y=eval(data_to_plot_Y2)[key], hovertemplate=hoverY2, marker=dict(color=colorList[c]), name=trace_name, legendgroup=key, showlegend=False), row=2, col=1,) c += 1 # 添加虚拟Trace作为分组图例(无数据,仅用于显示分组按钮) for group in groups: fig.add_trace(go.Scatter( x=[None], y=[None], name=group, legendgroup=f"group-{group}", marker=dict(color=group_colors[group]), showlegend=True )) fig.update_layout(hovermode="x") # 注入自定义JS代码,监听图例点击事件 js_code = """ document.addEventListener('DOMContentLoaded', function() { var plotDiv = document.getElementById('plotly-figure'); plotDiv.on('plotly_legendclick', function(data) { var clickedGroup = data.legendItem.legendgroup; // 判断点击的是分组虚拟Trace if (clickedGroup.startsWith('group-')) { var targetGroup = clickedGroup.split('-')[1]; var fig = plotDiv.data; // 遍历所有真实Trace,切换目标分组的可见性 fig.forEach(function(trace) { if (trace.name.startsWith(targetGroup + '|')) { trace.visible = trace.visible === true ? 'legendonly' : true; } }); // 更新图表 Plotly.react(plotDiv, fig, plotDiv.layout); // 阻止默认的单个图例点击行为 return false; } }); }); """ # 将JS代码添加到图表的HTML中 fig.update_layout( annotations=[ dict( text=f"<script>{js_code}</script>", showarrow=False, xref="paper", yref="paper", x=0, y=0, xanchor="left", yanchor="bottom", font=dict(size=0) ) ] ) # 保存或显示图表,指定div的id为plotly-figure fig.write_html("grouped_plot.html", full_html=True)
注意点
- 方案2中,虚拟Trace的
legendgroup要和真实Trace区分开,比如加前缀group- - 真实Trace的
name需要包含分组标识,方便JS识别,也可以用customdata存储分组信息,JS里通过trace.customdata[0]读取 - 如果用
fig.show(),建议本地打开生成的HTML文件,确保JS正常生效
内容的提问来源于stack exchange,提问作者chang thenoob
相关产品推荐
相关产品推荐

