Matplotlib 应用程序接口 (API) #

Matplotlib 有两个主要的应用程序接口,或使用库的风格:

  • 一个显式的“轴”接口,它使用图形或轴对象上的方法来创建其他艺术家,并逐步构建可视化。这也被称为“面向对象”接口。

  • 一个隐式的“pyplot”接口,它跟踪最后创建的图形和轴,并将艺术家添加到它认为用户想要的对象中。

此外,许多下游库(如pandasxarray)提供了plot一种直接在其数据类上实现的方法,以便用户可以调用data.plot().

这些接口之间的区别可能有点令人困惑,特别是考虑到网络上使用其中一个或另一个的片段,或者有时在同一个示例中使用多个接口。在这里,我们试图指出“pyplot”和下游接口如何与显式“Axes”接口相关,以帮助用户更好地浏览库。

原生 Matplotlib 接口#

显式的“轴”接口#

“Axes”接口是 Matplotlib 的实现方式,许多自定义和微调最终都在这个级别完成。

该接口通过实例化一个 Figure类的实例(fig如下), subplots在该对象上使用方法方法(或类似方法)来创建一个或多个 Axes对象(ax如下),然后在轴上调用绘图方法(plot在本示例中):

import matplotlib.pyplot as plt

fig = plt.figure()
ax = fig.subplots()
ax.plot([1, 2, 3, 4], [0, 0.5, 1, 0.2])

源代码png

../../_images/api_interfaces-1.png

我们称其为“显式”接口,因为每个对象都被显式引用,并用于创建下一个对象。保持对对象的引用是非常灵活的,并且允许我们在创建对象之后、显示之前自定义对象。

隐含的“pyplot”接口#

pyplot模块隐藏了大多数 Axes绘图方法以提供与上述等效的方法,其中为用户完成了图形和轴的创建:

import matplotlib.pyplot as plt

plt.plot([1, 2, 3, 4], [0, 0.5, 1, 0.2])

源代码png

../../_images/api_interfaces-2.png

这很方便,尤其是在进行交互式工作或简单脚本时。可以使用 gcf和 对当前 Axes检索对当前 Figure 的引用gca。该pyplot模块保留了一个 Figures 列表,每个 Figure 都为用户保留了一个图表上的 Axes 列表,以便以下内容:

import matplotlib.pyplot as plt

plt.subplot(1, 2, 1)
plt.plot([1, 2, 3], [0, 0.5, 0.2])

plt.subplot(1, 2, 2)
plt.plot([3, 2, 1], [0, 0.5, 0.2])

源代码png

../../_images/api_interfaces-3.png

相当于:

import matplotlib.pyplot as plt

plt.subplot(1, 2, 1)
ax = plt.gca()
ax.plot([1, 2, 3], [0, 0.5, 0.2])

plt.subplot(1, 2, 2)
ax = plt.gca()
ax.plot([3, 2, 1], [0, 0.5, 0.2])

源代码png

../../_images/api_interfaces-4.png

在显式接口中,这将是:

import matplotlib.pyplot as plt

fig, axs = plt.subplots(1, 2)
axs[0].plot([1, 2, 3], [0, 0.5, 0.2])
axs[1].plot([3, 2, 1], [0, 0.5, 0.2])

源代码png

../../_images/api_interfaces-5.png

为什么要明确?#

如果您必须回溯并在未引用的旧轴上操作会发生什么plt.gca()?一种简单的方法是subplot使用相同的参数再次调用。然而,这很快就变得不优雅了。您还可以检查 Figure 对象并获取其 Axes 对象列表,但这可能会产生误导(颜色条也是 Axes!)。最好的解决方案可能是为您创建的每个 Axes 保存一个句柄,但如果您这样做,为什么不简单地在开始时创建所有 Axes 对象?

第一种方法是plt.subplot再次调用:

import matplotlib.pyplot as plt

plt.subplot(1, 2, 1)
plt.plot([1, 2, 3], [0, 0.5, 0.2])

plt.subplot(1, 2, 2)
plt.plot([3, 2, 1], [0, 0.5, 0.2])

plt.suptitle('Implicit Interface: re-call subplot')

for i in range(1, 3):
    plt.subplot(1, 2, i)
    plt.xlabel('Boo')

源代码png

../../_images/api_interfaces-6.png

二是保存一个句柄:

import matplotlib.pyplot as plt

axs = []
ax = plt.subplot(1, 2, 1)
axs += [ax]
plt.plot([1, 2, 3], [0, 0.5, 0.2])

ax = plt.subplot(1, 2, 2)
axs += [ax]
plt.plot([3, 2, 1], [0, 0.5, 0.2])

plt.suptitle('Implicit Interface: save handles')

for i in range(2):
    plt.sca(axs[i])
    plt.xlabel('Boo')

源代码png

../../_images/api_interfaces-7.png

但是,推荐的方法是从一开始就明确:

import matplotlib.pyplot as plt

fig, axs = plt.subplots(1, 2)
axs[0].plot([1, 2, 3], [0, 0.5, 0.2])
axs[1].plot([3, 2, 1], [0, 0.5, 0.2])
fig.suptitle('Explicit Interface')
for i in range(2):
    axs[i].set_xlabel('Boo')

源代码png

../../_images/api_interfaces-8.png

第三方库“数据对象”接口#

一些第三方库已选择为其数据对象实现绘图,例如data.plot(),在pandasxarray和其他第三方库中可见。出于说明目的,下游库可以实现一个简单的数据容器,将x数据y存储在一起,然后实现一个plot方法:

import matplotlib.pyplot as plt

# supplied by downstream library:
class DataContainer:

    def __init__(self, x, y):
        """
        Proper docstring here!
        """
        self._x = x
        self._y = y

    def plot(self, ax=None, **kwargs):
        if ax is None:
            ax = plt.gca()
        ax.plot(self._x, self._y, **kwargs)
        ax.set_title('Plotted from DataClass!')
        return ax


# what the user usually calls:
data = DataContainer([0, 1, 2, 3], [0, 0.2, 0.5, 0.3])
data.plot()

源代码png

../../_images/api_interfaces-9.png

因此,该库可以对用户隐藏所有细节,并且可以制作适合数据类型的可视化,通常具有良好的标签、颜色图的选择和其他方便的功能。

然而,在上面,我们可能不喜欢图书馆提供的标题。值得庆幸的是,他们从plot()方法中将 Axes 传回给我们,并且理解了显式的 Axes 接口,我们可以调用: 来自定义标题。ax.set_title('My preferred title')

许多库还允许其plot方法接受可选的ax 参数。这允许我们将可视化放置在我们已放置并可能自定义的轴中。

总结#

总的来说,理解显式的“Axes”接口是很有用的,因为它是最灵活的并且是其他接口的基础。用户通常可以弄清楚如何下拉到显式界面并对底层对象进行操作。虽然显式接口的设置可能有点冗长,但复杂的绘图通常比尝试使用隐式“pyplot”接口更简单。

笔记

我们pyplot为这两个接口导入有时会让人们感到困惑。目前,该pyplot模块实现了“pyplot”接口,但它也提供了顶级的 Figure 和 Axes 创建方法,并最终启动图形用户界面(如果正在使用的话)。pyplot无论选择何种接口,仍然需要如此。

类似地,合作伙伴库提供的声明性接口使用“Axes”接口可访问的对象,并且通常接受这些作为参数或将它们从方法传回。通常必须使用显式的“Axes”接口来执行默认可视化的任何自定义,或者将数据解包到 NumPy 数组中并直接传递给 Matplotlib。

附录:带有数据结构的“轴”接口#

大多数方法通过将数据对象传递给方法并将参数指定为字符串来Axes允许另一个 API 寻址 :

import matplotlib.pyplot as plt

data = {'xdat': [0, 1, 2, 3], 'ydat': [0, 0.2, 0.4, 0.1]}
fig, ax = plt.subplots(figsize=(2, 2))
ax.plot('xdat', 'ydat', data=data)

源代码png

../../_images/api_interfaces-10.png

附录:“pylab”接口#

还有一个非常不鼓励使用的接口,那就是基本上做. 这让用户可以简单地调用. 虽然方便,但如果用户无意中将变量命名为与 pyplot 方法相同的名称,这可能会导致明显的问题。from matplotlib.pyplot import *plot(x, y)