Matplotlib柱状图图例重复、标签颜色匹配异常求解
问题描述
我正在制作欧冠冠军球队及其国内联赛最终排名的可视化图表,经测试按赛季循环逐根绘制柱形、按所属球队匹配柱体填充色与边框色的实现方式效果最佳,对应绘图代码如下:
fig,ax = plt.subplots() fig.set_size_inches(16,8) for s in P_df['Season']: x = s y = P_df.loc[P_df['Season'] == s, 'Position'] y = y.to_list()[0] team = P_df.loc[P_df['Season'] == s, 'Team'] team = team.to_list()[0] face = color_key[team] edge = color2nd_key[team] ax.bar(x,y,color=face,label=team,edgecolor =edge,linewidth =3)
直接调用默认图例时会出现重复的球队条目,因此我尝试传入去重后的球队列表生成图例,再循环遍历为图例标签匹配对应填充色与边框色,对应图例代码如下:
ax.legend(color_key,loc = 'upper right') leg = ax.legend(P_df['Team'].unique(),bbox_to_anchor=(1.02, 1), loc='upper left', borderaxespad=0, facecolor = 'black',edgecolor ='white') handles, labels = ax.get_legend_handles_labels() la = set(labels) la = list(la) for label in la: for ha in ax.legend_.legendHandles: te = label e = color2nd_key[te] f = color_key[te] ha.set_edgecolor(e) ha.set_facecolor(f)
执行上述代码后出现异常:所有图例标签都被赋值为列表首个元素对应的颜色,异常效果见参考附图:
现需要定位代码错误点,并给出更优的实现方案。
错误原因
代码存在三个核心问题:
- 嵌套循环逻辑错误:外层每遍历到一个球队标签,内层就会遍历所有图例handle,把所有图例项的颜色统一改成当前标签对应的配色。循环全部执行完成后,所有图例项都会被最后一次遍历的标签配色覆盖,因此呈现全为同一种颜色的效果。
- 重复调用
ax.legend()属于无效冗余代码:第一次调用生成的右上角图例,会被第二次ax.legend()调用直接覆盖,没有实际作用。 - 用
set()对标签去重会打乱标签原有顺序,即便修正循环逻辑,也大概率出现标签与配色错位的问题。
最优实现方案
不需要事后遍历修改图例颜色,从绘图阶段就避免重复label生成,是最简单稳定的方案,代码如下:
fig,ax = plt.subplots() fig.set_size_inches(16,8) added_teams = set() for s in P_df['Season']: # 用iloc取值比转列表更高效 y = P_df.loc[P_df['Season'] == s, 'Position'].iloc[0] team = P_df.loc[P_df['Season'] == s, 'Team'].iloc[0] face = color_key[team] edge = color2nd_key[team] # 仅球队第一次出现时传入label,后续同球队柱形不设置label,从根源避免图例重复 bar_label = team if team not in added_teams else "" added_teams.add(team) ax.bar(s, y, color=face, label=bar_label, edgecolor=edge, linewidth=3) # 直接生成无重复图例,不需要二次修改颜色 ax.legend(bbox_to_anchor=(1.02, 1), loc='upper left', borderaxespad=0, facecolor='black', edgecolor='white') plt.tight_layout() plt.show()
如果需要自定义图例排序、完全掌控图例样式,可以手动构建图例元素,可维护性更强:
from matplotlib.patches import Patch # 此处可自定义球队排序规则,默认按首次出现顺序排列 legend_teams = list(P_df['Team'].unique()) # 手动构建每个图例项的样式 legend_handles = [ Patch(facecolor=color_key[t], edgecolor=color2nd_key[t], linewidth=3) for t in legend_teams ] # 传入自定义handle和标签生成图例 ax.legend(handles=legend_handles, labels=legend_teams, bbox_to_anchor=(1.02, 1), loc='upper left', borderaxespad=0, facecolor='black', edgecolor='white')
内容的提问来源于stack exchange,提问作者user19123652
相关产品推荐
相关产品推荐

