Plotly分组堆叠条形图过滤后浮动条问题的修复咨询
分组堆叠条形图过滤后浮动条问题解决方法
问题背景
使用以下Plotly代码生成分组堆叠条形图:
from plotly import graph_objects as go data = { "left": [ {"original": 15, "model_1": 4, "model_2": 11}, {"original": 23, "model_1": 8, "model_2": 18}, {"original": 32, "model_1": 18, "model_2": 18}, {"original": 10, "model_1": 6, "model_2": 0}, {"original": 23, "model_1": 0, "model_2": 20} ], "right": [ {"original": 7, "model_1": 3, "model_2": 5}, {"original": 12, "model_1": 6, "model_2": 8}, {"original": 15, "model_1": 8, "model_2": 10}, {"original": 5, "model_1": 4, "model_2": 0}, {"original": 10, "model_1": 2, "model_2": 12} ], "labels": [ "feature", "question", "bug", "documentation", "maintenance" ] } fig = go.Figure() colors = { "original": "#636EFA", "model_1": "#EF553B", "model_2": "#00CC96" } def add_stacked_bar(side, offset_group): side_data = data[side] for i, category in enumerate(data["labels"]): current_base = 0 for model_type in ["original", "model_1", "model_2"]: value = side_data[i].get(model_type, 0) if value > 0: fig.add_trace(go.Bar( name=f"{model_type.replace('_', ' ').title()} ({side.title()})", x=[category], y=[value], offsetgroup=offset_group, base=current_base, marker_color=colors[model_type], legendgroup=model_type, showlegend=(i == 0) )) current_base += value add_stacked_bar("left", 0) add_stacked_bar("right", 1) fig.update_layout( title="Issue Types - Original and Models", yaxis_title="Number of Issues", barmode='group', legend_title="Model Types", legend=dict( orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1 ) )
对图表进行过滤操作后,出现浮动条现象。尝试将barmode改为stack未解决问题;改为stack并注释base = current_base语句虽解决浮动条,但丢失了分组堆叠的原有样式。需要找到方法在过滤后重新计算base参数,兼顾问题修复与原有图表样式。
解决方案核心
过滤后浮动条的根源是:原有base值基于所有trace都显示的情况计算,当部分trace被隐藏后,剩余trace的base未更新,导致堆叠位置错误。解决思路是:针对每个offsetgroup(分组)和每个类别,根据当前显示状态的trace重新累加计算base值。
具体实现代码
以下是添加过滤逻辑和重新计算base的完整代码示例:
from plotly import graph_objects as go # 原始数据 data = { "left": [ {"original": 15, "model_1": 4, "model_2": 11}, {"original": 23, "model_1": 8, "model_2": 18}, {"original": 32, "model_1": 18, "model_2": 18}, {"original": 10, "model_1": 6, "model_2": 0}, {"original": 23, "model_1": 0, "model_2": 20} ], "right": [ {"original": 7, "model_1": 3, "model_2": 5}, {"original": 12, "model_1": 6, "model_2": 8}, {"original": 15, "model_1": 8, "model_2": 10}, {"original": 5, "model_1": 4, "model_2": 0}, {"original": 10, "model_1": 2, "model_2": 12} ], "labels": [ "feature", "question", "bug", "documentation", "maintenance" ] } colors = { "original": "#636EFA", "model_1": "#EF553B", "model_2": "#00CC96" } fig = go.Figure() def add_stacked_bar(side, offset_group): side_data = data[side] for i, category in enumerate(data["labels"]): current_base = 0 for model_type in ["original", "model_1", "model_2"]: value = side_data[i].get(model_type, 0) if value > 0: fig.add_trace(go.Bar( name=f"{model_type.replace('_', ' ').title()} ({side.title()})", x=[category], y=[value], offsetgroup=offset_group, base=current_base, marker_color=colors[model_type], legendgroup=model_type, showlegend=(i == 0) )) current_base += value add_stacked_bar("left", 0) add_stacked_bar("right", 1) fig.update_layout( title="Issue Types - Original and Models", yaxis_title="Number of Issues", barmode='group', legend_title="Model Types", legend=dict( orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1 ) ) # 定义过滤后重新计算base的函数 def update_base_after_filter(): # 按offsetgroup和x类别分组整理trace grouped_traces = {} for trace in fig.data: key = (trace.offsetgroup, trace.x[0]) if key not in grouped_traces: grouped_traces[key] = [] # 只保留显示的trace if trace.visible: grouped_traces[key].append(trace) # 对每组trace按堆叠顺序(original -> model_1 -> model_2)排序 stack_order = ["original", "model_1", "model_2"] for key in grouped_traces: # 根据trace名称中的模型类型排序 grouped_traces[key].sort( key=lambda t: stack_order.index(t.name.split(" ")[0].lower().replace(" ", "_")) ) # 重新计算base current_base = 0 for trace in grouped_traces[key]: trace.base = current_base current_base += trace.y[0] # 示例:过滤掉model_1的所有trace for trace in fig.data: if "Model 1" in trace.name: trace.visible = False # 过滤后更新base update_base_after_filter() fig.show()
关键说明
- 分组整理trace:将trace按
offsetgroup(左右分组)和x类别(issue类型)分组,确保每个分组内的trace属于同一类别和同一侧。 - 排序trace:按照原始堆叠顺序(original -> model_1 -> model_2)对分组内的trace排序,保证堆叠层级正确。
- 重新计算base:遍历排序后的trace,累加
y值更新base,确保每个堆叠条的底部位置是前一个显示条的顶部。
内容的提问来源于stack exchange,提问作者PracticingPython
相关产品推荐
相关产品推荐

