import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
Matplotlib有两种主要的应用接口或使用库的方式:
一种是显式的“Axes”接口(explicit),它使用Figure或Axes对象上的方法创建图像效果,并逐步构建可视化。这也被称为“面向对象”的接口。
另一种是隐式的“pyplot”接口(implicit),它跟踪最后创建的Figure和Axes,并将图像效果添加到它认为用户想要的对象中。
此外,许多下游库(如pandas和xarray)在其数据类上直接实现了plot方法,以便用户可以调用data.plot()。
这些接口之间的差异可能有点令人困惑,特别是在网络上使用其中一种或多种接口的代码片段。在这里,我们试图指出“pyplot”和下游接口与显式“Axes”接口之间的关系,以帮助用户更好地使用该库。
fig = plt.figure(figsize=(20, 20))
ax = fig.subplots()
ax.plot([1, 2, 3, 4], [0, 0.5, 1, 0.2])
因为每个对象都明确引用并用于创建下一个对象。保留对这些对象的引用非常灵活,并允许我们在创建它们后但在显示之前自定义这些对象。
pyplot模块对大多数Axes绘图方法进行了封装,以提供与上述相同的功能,其中Figure和Axes的创建已经由库完成,无需用户进行操作:
plt.plot([1, 2, 3, 4], [0, 0.5, 1, 0.2])
这很方便,特别是在进行交互式工作或编写简单脚本时。可以使用gcf获取对当前Figure的引用,使用gca获取对当前Axes的引用。pyplot模块会保留Figure的列表,并且每个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])
等价于显式方式:
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])
如果你需要回溯,并操作一个不被plt.gca()引用的旧Axes,会发生什么?一种简单的方法是使用相同的参数再次调用subplot。然而,这很快就变得不雅观了。您还可以检查Figure对象并获取其Axes对象列表,但是这可能会引起误解(颜色设置也属于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])
plt.suptitle('Implicit Interface: re-call subplot')
for i in range(1, 3):
plt.subplot(1, 2, i)
plt.xlabel('Boo')
最好的解决方案可能是保存每个创建的Axes的句柄,但如果这样做,为什么不在开始时创建所有Axes对象呢?
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')
推荐的方法:
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')
一些第三方库选择为其数据对象实现绘图,例如,在pandas、xarray和其他第三方库中可以看到data.plot()。为了说明问题,下游库可能会实现一个简单的数据容器,其中包含存储在一起的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()
因此,该库可以将所有复杂的细节隐藏起来,可以根据数据类型创建适当的可视化,通常具有良好的标签、色图选择和其他方便的功能。
然而,在上述示例中,我们可能不喜欢库提供的标题。幸运的是,他们从plot()方法中将Axes传回给我们,因此我们可以使用显式的Axes接口调用ax.set_title(‘My preferred title’)来自定义标题。
许多库还允许其plot方法接受一个可选的ax参数。这使我们可以将可视化放置在我们已经放置并可能定制的Axes中。
总结:
总的来说,理解显式“Axes”接口非常有用,因为它是最灵活的接口,并支持其他接口。用户通常可以找出如何进入显式接口并操作底层对象。虽然显式接口可能需要更多的设置步骤,但是相比尝试使用隐式的“pyplot”接口,复杂的绘图通常会变得更简单。