备注
Go to the end 下载完整示例代码.
图例指南#
本图例指南扩展了 legend 的文档字符串 - 在继续阅读本指南之前,请先阅读它.
本指南使用了一些常用术语,为了清晰起见,在此处记录:
- 图例条目#
一个图例由一个或多个图例条目组成.一个条目由一个键和一个标签组成.
- 图例键#
位于每个图例标签左侧的彩色/图案标记.
- 图例标签#
描述由键表示的处理对象的文本.
- 图例句柄#
用于在图例中生成适当条目的原始对象.
控制图例条目#
不带参数调用 legend() 会自动获取图例句柄及其关联的标签.此功能等效于:
handles, labels = ax.get_legend_handles_labels()
ax.legend(handles, labels)
get_legend_handles_labels() 函数返回 Axes 上存在的句柄/艺术家列表,可用于为生成的图例生成条目 - 但值得注意的是,并非所有艺术家都可以添加到图例中,此时必须创建一个"代理"(有关更多详细信息,请参见 专门创建用于添加到图例的艺术家(又名代理艺术家) ).
备注
标签为空字符串或标签以下划线"_"开头的艺术家将被忽略.
为了完全控制添加到图例中的内容,通常直接将适当的句柄传递给 legend()
fig, ax = plt.subplots()
line_up, = ax.plot([1, 2, 3], label='Line 2')
line_down, = ax.plot([3, 2, 1], label='Line 1')
ax.legend(handles=[line_up, line_down])
重命名图例条目#
当无法直接在句柄上设置标签时,可以直接将其传递给 Axes.legend
fig, ax = plt.subplots()
line_up, = ax.plot([1, 2, 3], label='Line 2')
line_down, = ax.plot([3, 2, 1], label='Line 1')
ax.legend([line_up, line_down], ['Line Up', 'Line Down'])
如果无法直接访问句柄,例如在使用某些 Third-party packages 时,可以通过 Axes.get_legend_handles_labels 访问它们.在这里,我们使用字典来重命名现有标签:
my_map = {'Line Up':'Up', 'Line Down':'Down'}
handles, labels = ax.get_legend_handles_labels()
ax.legend(handles, [my_map[l] for l in labels])
专门创建用于添加到图例的艺术家(又名代理艺术家)#
并非所有句柄都可以自动转换为图例条目,因此通常需要创建一个可以成为图例条目的艺术家.图例句柄不必存在于 Figure 或 Axes 上才能使用.
假设我们想要创建一个图例,其中包含一些由红色表示的数据的条目:
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
fig, ax = plt.subplots()
red_patch = mpatches.Patch(color='red', label='The red data')
ax.legend(handles=[red_patch])
plt.show()

有许多受支持的图例句柄.我们可以创建一个带有标记的线条,而不是创建一个颜色块:
import matplotlib.lines as mlines
fig, ax = plt.subplots()
blue_line = mlines.Line2D([], [], color='blue', marker='*',
markersize=15, label='Blue stars')
ax.legend(handles=[blue_line])
plt.show()

图例位置#
图例的位置可以通过关键字参数 loc 指定.有关更多详细信息,请参见 legend() 的文档.
bbox_to_anchor 关键字为手动图例放置提供了很大的控制度.例如,如果您希望您的 Axes 图例位于图形的右上角,而不是 Axes 的角,只需指定角的位置和该位置的坐标系:
ax.legend(bbox_to_anchor=(1, 1),
bbox_transform=fig.transFigure)
更多自定义图例放置的示例:
fig, ax_dict = plt.subplot_mosaic([['top', 'top'], ['bottom', 'BLANK']],
empty_sentinel="BLANK")
ax_dict['top'].plot([1, 2, 3], label="test1")
ax_dict['top'].plot([3, 2, 1], label="test2")
# Place a legend above this subplot, expanding itself to
# fully use the given bounding box.
ax_dict['top'].legend(bbox_to_anchor=(0., 1.02, 1., .102), loc='lower left',
ncols=2, mode="expand", borderaxespad=0.)
ax_dict['bottom'].plot([1, 2, 3], label="test1")
ax_dict['bottom'].plot([3, 2, 1], label="test2")
# Place a legend to the right of this smaller subplot.
ax_dict['bottom'].legend(bbox_to_anchor=(1.05, 1),
loc='upper left', borderaxespad=0.)

图形图例#
有时,相对于(子)图形而不是单个 Axes 放置图例更有意义.通过使用约束布局并在 loc 关键字参数的开头指定"outside",图例将绘制在(子)图形上 Axes 的外部.
fig, axs = plt.subplot_mosaic([['left', 'right']], layout='constrained')
axs['left'].plot([1, 2, 3], label="test1")
axs['left'].plot([3, 2, 1], label="test2")
axs['right'].plot([1, 2, 3], 'C2', label="test3")
axs['right'].plot([3, 2, 1], 'C3', label="test4")
# Place a legend to the right of this smaller subplot.
fig.legend(loc='outside upper right')

这接受与普通 loc 关键字略有不同的语法,其中"outside right upper"与"outside upper right"不同.
ucl = ['upper', 'center', 'lower']
lcr = ['left', 'center', 'right']
fig, ax = plt.subplots(figsize=(6, 4), layout='constrained', facecolor='0.7')
ax.plot([1, 2], [1, 2], label='TEST')
# Place a legend to the right of this smaller subplot.
for loc in [
'outside upper left',
'outside upper center',
'outside upper right',
'outside lower left',
'outside lower center',
'outside lower right']:
fig.legend(loc=loc, title=loc)
fig, ax = plt.subplots(figsize=(6, 4), layout='constrained', facecolor='0.7')
ax.plot([1, 2], [1, 2], label='test')
for loc in [
'outside left upper',
'outside right upper',
'outside left lower',
'outside right lower']:
fig.legend(loc=loc, title=loc)
同一 Axes 上的多个图例#
有时,将图例条目拆分到多个图例中会更清楚.虽然这样做的本能方法可能是多次调用 legend() 函数,但您会发现 Axes 上只存在一个图例.这样做是为了可以重复调用 legend() 以将图例更新为 Axes 上的最新句柄.要保留旧的图例实例,我们必须手动将它们添加到 Axes:
fig, ax = plt.subplots()
line1, = ax.plot([1, 2, 3], label="Line 1", linestyle='--')
line2, = ax.plot([3, 2, 1], label="Line 2", linewidth=4)
# Create a legend for the first line.
first_legend = ax.legend(handles=[line1], loc='upper right')
# Add the legend manually to the Axes.
ax.add_artist(first_legend)
# Create another legend for the second line.
ax.legend(handles=[line2], loc='lower right')
plt.show()

图例处理器#
为了创建图例条目,句柄会作为参数传递给适当的 HandlerBase 子类.处理器子类的选择由以下规则决定:
使用
handler_map关键字中的值更新get_legend_handler_map().检查
handle是否在新创建的handler_map中.检查
handle的类型是否在新创建的handler_map中.检查
handle的 mro 中的任何类型是否在新创建的handler_map中.
为了完整起见,此逻辑主要在 get_legend_handler() 中实现.
所有这些灵活性意味着我们拥有必要的钩子来为我们自己的图例键类型实现自定义处理器.
使用自定义处理器的最简单示例是实例化现有的 legend_handler.HandlerBase 子类之一.为了简单起见,让我们选择 legend_handler.HandlerLine2D ,它接受 numpoints 参数(为了方便起见,numpoints 也是 legend() 函数上的一个关键字).然后,我们可以将实例到 Handler 的映射作为关键字传递给 legend.
from matplotlib.legend_handler import HandlerLine2D
fig, ax = plt.subplots()
line1, = ax.plot([3, 2, 1], marker='o', label='Line 1')
line2, = ax.plot([1, 2, 3], marker='o', label='Line 2')
ax.legend(handler_map={line1: HandlerLine2D(numpoints=4)}, handlelength=4)

正如您所看到的,"Line 1"现在有 4 个标记点,而"Line 2"有 2 个(默认值).我们还使用 handlelength 关键字增加了句柄的长度,以适应更大的图例条目.尝试上面的代码,只将 map 的键从 line1 更改为 type(line1) .请注意,现在两个 Line2D 实例都获得了 4 个标记.
除了用于复杂绘图类型(如 errorbar,stem plot 和直方图)的处理器之外,默认的 handler_map 还有一个特殊的 tuple 处理器( legend_handler.HandlerTuple ),它只是将句柄相互叠加在一起,对于给定元组中的每个项目.以下示例演示了将两个图例键相互叠加在一起:
from numpy.random import randn
z = randn(10)
fig, ax = plt.subplots()
red_dot, = ax.plot(z, "ro", markersize=15)
# Put a white cross over some of the data.
white_cross, = ax.plot(z[:5], "w+", markeredgewidth=3, markersize=15)
ax.legend([red_dot, (red_dot, white_cross)], ["Attr A", "Attr A+B"])

legend_handler.HandlerTuple 类还可以用于将多个图例键分配给同一个条目:
from matplotlib.legend_handler import HandlerLine2D, HandlerTuple
fig, ax = plt.subplots()
p1, = ax.plot([1, 2.5, 3], 'r-d')
p2, = ax.plot([3, 2, 1], 'k-o')
l = ax.legend([(p1, p2)], ['Two keys'], numpoints=1,
handler_map={tuple: HandlerTuple(ndivide=None)})

实现自定义图例处理器#
可以实现自定义处理器,将任何句柄转换为图例键(句柄不一定需要是 matplotlib artist).处理器必须实现一个 legend_artist 方法,该方法返回单个 artist 供图例使用. legend_artist 的必需签名记录在 legend_artist .
import matplotlib.patches as mpatches
class AnyObject:
pass
class AnyObjectHandler:
def legend_artist(self, legend, orig_handle, fontsize, handlebox):
x0, y0 = handlebox.xdescent, handlebox.ydescent
width, height = handlebox.width, handlebox.height
patch = mpatches.Rectangle([x0, y0], width, height, facecolor='red',
edgecolor='black', hatch='xx', lw=3,
transform=handlebox.get_transform())
handlebox.add_artist(patch)
return patch
fig, ax = plt.subplots()
ax.legend([AnyObject()], ['My first handler'],
handler_map={AnyObject: AnyObjectHandler()})

或者,如果我们希望全局接受 AnyObject 实例,而无需一直手动设置 handler_map 关键字,我们可以使用以下方法注册新处理器:
from matplotlib.legend import Legend
Legend.update_default_handler_map({AnyObject: AnyObjectHandler()})
虽然这里的强大功能是显而易见的,但请记住,已经实现了许多处理器,并且您想要实现的目标可能已经可以通过现有类轻松实现.例如,要生成椭圆形的图例键,而不是矩形的图例键:
from matplotlib.legend_handler import HandlerPatch
class HandlerEllipse(HandlerPatch):
def create_artists(self, legend, orig_handle,
xdescent, ydescent, width, height, fontsize, trans):
center = 0.5 * width - 0.5 * xdescent, 0.5 * height - 0.5 * ydescent
p = mpatches.Ellipse(xy=center, width=width + xdescent,
height=height + ydescent)
self.update_prop(p, orig_handle, legend)
p.set_transform(trans)
return [p]
c = mpatches.Circle((0.5, 0.5), 0.25, facecolor="green",
edgecolor="red", linewidth=3)
fig, ax = plt.subplots()
ax.add_patch(c)
ax.legend([c], ["An ellipse, not a rectangle"],
handler_map={mpatches.Circle: HandlerEllipse()})

脚本的总运行时间:(0 分钟 3.181 秒)

