注意
转到末尾下载完整的示例代码。
图例指南#
此图例指南扩展了 legend
文档字符串 - 在继续本指南之前请阅读它。
本指南使用了一些常用术语,在此处记录以方便理解
- 图例条目#
图例由一个或多个图例条目组成。一个条目由恰好一个键和一个标签组成。
- 图例键#
每个图例标签左侧的彩色/图案标记。
- 图例标签#
描述键所表示的句柄的文本。
- 图例句柄#
用于在图例中生成适当条目的原始对象。
控制图例条目#
不带任何参数调用 legend()
会自动获取图例句柄及其相关标签。此功能等效于
get_legend_handles_labels()
函数返回坐标轴上存在的句柄/艺术家列表,这些句柄/艺术家可用于生成结果图例的条目 - 但值得注意的是,并非所有艺术家都可以添加到图例中,此时必须创建一个“代理”(有关详细信息,请参阅 专门为添加到图例而创建艺术家(又名代理艺术家))。
注意
标签为空字符串或以"_"开头的标签的艺术家将被忽略。
为了完全控制添加到图例的内容,通常将相应的句柄直接传递给 legend()
重命名图例条目#
当无法直接在句柄上设置标签时,可以直接将它们传递给 Axes.legend
如果无法直接访问句柄,例如在使用某些 第三方软件包 时,可以通过 Axes.get_legend_handles_labels
访问它们。这里我们使用字典来重命名现有标签
专门为添加到图例而创建艺术家(又名代理艺术家)#
并非所有句柄都可以自动转换为图例条目,因此通常需要创建一个可以的艺术家。图例句柄不必存在于图形或坐标轴上即可使用。
假设我们想要创建一个图例,其中包含由红色表示的某些数据的条目
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
关键字为手动放置图例提供了很大的控制程度。例如,如果您希望将坐标轴图例放置在图形的右上角而不是坐标轴的角上,只需指定角的的位置和该位置的坐标系
自定义图例放置的更多示例
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.)
图形图例#
有时,将图例放置在相对于(子)图形而不是单独的坐标轴更有意义。通过使用约束布局并在 loc 关键字参数的开头指定 “outside”,图例将绘制在(子)图形上的坐标轴之外。
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)
同一坐标轴上的多个图例#
有时,将图例条目拆分到多个图例中会更清晰。虽然执行此操作的本能方法可能是多次调用 legend()
函数,但您会发现坐标轴上只有一个图例存在。这样做是为了可以重复调用 legend()
以将图例更新为坐标轴上的最新句柄。要保留旧的图例实例,我们必须手动将它们添加到坐标轴
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()
函数上的一个关键字)。然后,我们可以将实例到处理程序的映射作为关键字传递给图例。
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
关键字增加了句柄的长度,以适应更大的图例条目。尝试上述代码,只将映射的键从 line1
更改为 type(line1)
。请注意,现在两个 Line2D
实例都有 4 个标记。
除了用于复杂绘图类型(如误差条、茎图和直方图)的处理程序外,默认的 handler_map
还有一个特殊的 tuple
处理程序 (legend_handler.HandlerTuple
),它只是将给定元组中每个项目的句柄相互叠加绘制。下面的示例演示如何将两个图例键叠加在一起
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 分 7.292 秒)