如何在Python中使用plt.legend为图例添加列标题?
实现带列标题的Matplotlib图例
没问题!要给这种分两组(实心/空心标记对应不同属性)的图例加上列标题,用matplotlib的plt.legend完全可以实现,核心思路是用虚拟图例条目做标题,再重组图例的条目布局,我给你写个可直接运行的示例,一步步讲清楚:
核心思路
- 先正常绘制所有曲线(每种材料的实心、空心曲线)
- 创建两个「虚拟的图例条目」(空线条+空标记)作为两列的标题
- 把原有图例条目拆分为「实心组」和「空心组」
- 重组图例条目:标题+对应组,设置
ncol=2让内容分成两列 - 调整样式让标题看起来更像列头
完整代码示例
import matplotlib.pyplot as plt from matplotlib.lines import Line2D # 模拟数据:3种材料,每种对应「属性A(实心)」和「属性B(空心)」两条曲线 materials = ["材料1", "材料2", "材料3"] x = [1, 2, 3, 4] # 第一步:绘制所有曲线 fig, ax = plt.subplots(figsize=(8, 5)) for idx, mat in enumerate(materials): # 实心标记:属性A ax.plot(x, [idx + val for val in x], marker='o', linestyle='-', color=f'C{idx}', label=mat) # 空心标记:属性B ax.plot(x, [idx + val + 0.6 for val in x], marker='o', linestyle='--', color=f'C{idx}', markerfacecolor='white', label=mat) # 第二步:创建虚拟图例条目作为列标题(只显示文字,无标记/线条) title_style = {'weight': 'bold', 'fontsize': 10} title_attr_a = Line2D([], [], label='属性A', linestyle='', marker='', **title_style) title_attr_b = Line2D([], [], label='属性B', linestyle='', marker='', **title_style) # 第三步:提取原有图例的handles和labels,并拆分两组 all_handles, all_labels = ax.get_legend_handles_labels() # 拆分:偶数索引是实心(属性A),奇数是空心(属性B) handles_attr_a = all_handles[::2] labels_attr_a = all_labels[::2] handles_attr_b = all_handles[1::2] labels_attr_b = all_labels[1::2] # 第四步:重组图例条目,让标题对应各自的组 new_handles = [title_attr_a] + handles_attr_a + [title_attr_b] + handles_attr_b new_labels = [title_attr_a.get_label()] + labels_attr_a + [title_attr_b.get_label()] + labels_attr_b # 第五步:创建图例并调整布局 legend = ax.legend( handles=new_handles, labels=new_labels, ncol=2, handlelength=0, # 隐藏虚拟标题的空标记 handletextpad=0, columnspacing=4, # 调整两列之间的间距 loc='upper left' ) # 可选:微调标题位置,让它和下方条目区分开 for text in legend.get_texts(): if text.get_text() in ['属性A', '属性B']: text.set_position((0, 12)) # 向上移动标题,增强列头感 plt.tight_layout() plt.show()
关键细节说明
- 虚拟条目:用
Line2D创建没有线条和标记的对象,只保留label作为标题,这样能完美融入图例布局 - 条目拆分:因为我们绘图时是先画实心再画空心,所以可以用步长索引
[::2]和[1::2]拆分两组 - 样式调整:
handlelength=0避免虚拟标题显示空标记,columnspacing控制列间距,最后手动移动标题位置让布局更协调
运行这段代码后,你就能得到带有「属性A」「属性B」列标题的图例,正好对应实心/空心标记的分组~
内容的提问来源于stack exchange,提问作者Neo Lerata
相关产品推荐
相关产品推荐

