图例指南#

在 Matplotlib 中灵活生成图例。

此图例指南是可用文档的扩展 legend()- 请确保您在继续阅读本指南之前熟悉该文档的内容。

本指南使用了一些常用术语,为清楚起见,此处记录了这些术语:

图例条目#

图例由一个或多个图例条目组成。一个条目恰好由一个键和一个标签组成。

图例键#

每个图例标签左侧的彩色/图案标记。

图例标签#

描述由键表示的句柄的文本。

图例句柄#

用于在图例中生成适当条目的原始对象。

控制图例条目#

不带参数调用legend()会自动获取图例句柄及其相关标签。此功能等效于:

handles, labels = ax.get_legend_handles_labels()
ax.legend(handles, labels)

get_legend_handles_labels()函数返回轴上存在的句柄/艺术家列表,可用于为结果图例生成条目 - 但值得注意的是,并非所有艺术家都可以添加到图例中,此时“代理”将必须创建(有关详细信息,请参阅专门创建艺术家以添加到图例(又名代理艺术家))。

笔记

使用空字符串作为标签或标签以下划线“_”开头的艺术家将被忽略。

为了完全控制添加到图例中的内容,通常将适当的句柄直接传递给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])

在某些情况下,无法设置句柄的标签,因此可以将标签列表传递给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'])

创建专门用于添加到图例的艺术家(又名代理艺术家)#

并非所有句柄都可以自动变成图例条目,因此通常需要创建一个可以. 图或轴上不必存在图例句柄即可使用。

假设我们想要创建一个图例,其中包含一些用红色表示的数据的条目:

import matplotlib.patches as mpatches
import matplotlib.pyplot as plt

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关键字为手动图例放置提供了很大程度的控制。例如,如果您希望坐标区图例位于图形的右上角而不是坐标区的角,只需指定角的位置和该位置的坐标系:

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',
                      ncol=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.)

plt.show()
传说指南

同一轴上的多个图例#

有时将图例条目拆分为多个图例会更清楚。虽然执行此操作的本能方法可能是legend()多次调用该函数,但您会发现 Axes 上只存在一个图例。这样做是为了可以legend()重复调用以将图例更新为轴上的最新句柄。要保留旧的图例实例,我们必须手动将它们添加到 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子类。处理程序子类的选择由以下规则决定:

  1. 使用关键字get_legend_handler_map() 中的值进行更新。handler_map

  2. 检查是否handle在新创建的handler_map.

  3. 检查类型handle是否在新创建的handler_map.

  4. 检查 's mro 中的任何类型handle是否在新创建的handler_map.

为了完整起见,此逻辑主要在 get_legend_handler().

所有这些灵活性意味着我们有必要的钩子来为我们自己的图例键类型实现自定义处理程序。

使用自定义处理程序的最简单示例是实例化现有legend_handler.HandlerBase子类之一。为了简单起见,让我们选择legend_handler.HandlerLine2D 哪个接受numpoints参数(为方便起见,numpoints 也是legend()函数上的关键字)。然后我们可以将实例到 Handler 的映射作为关键字传递给图例。

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)})
传说指南
<matplotlib.legend.Legend object at 0x7f2cf9a16ef0>

如您所见,“Line 1”现在有 4 个标记点,而“Line 2”有 2 个(默认)。试试上面的代码,只需将地图的键从 更改line1type(line1)。请注意现在两个Line2D实例如何获得 4 个标记。

除了用于复杂绘图类型(如误差线、茎图和直方图)的处理程序外,默认设置还有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"])
传说指南
<matplotlib.legend.Legend object at 0x7f2cfb693760>

该类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 艺术家)。处理程序必须实现一个legend_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()})
传说指南
<matplotlib.legend.Legend object at 0x7f2cddb26a10>

或者,如果我们想要全局接受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()})
传说指南
<matplotlib.legend.Legend object at 0x7f2d00dde710>

脚本总运行时间:(0分3.053秒)

由 Sphinx-Gallery 生成的画廊