好了,理论够了。 现在,我们已准备好将所有内容联系在一起并进行一些绘图。 从现在开始,我们将主要依赖无状态(面向对象)方法,这种方法更具可定制性,并且随着图形变得更加复杂而派上用场。

      在 OO 方法下创建具有单个轴的图形的规定方法是(不太直观)使用 plt.subplots()。 这真的是唯一一次 OO 方法使用 pyplot 创建图形和轴:

fig, ax = plt.subplots()

      上面,我们利用可迭代解包为 plt.subplots() 的两个结果中的每一个分配了一个单独的变量。 请注意,我们没有在这里向 subplots() 传递参数。 默认调用是 subplots(nrows=1, ncols=1)。 因此, ax 是单个 AxesSubplot 对象:

>>> type(ax)

<class 'matplotlib.axes._subplots.AxesSubplot'>

      我们可以调用它的实例方法来操作绘图,类似于我们调用 pyplots 函数的方式。 让我们用三个时间序列的堆积面积图来说明:

#yyds干货盘点#利用Matplotlib库画图(三)_时间序列

      以上代码的意义如下:

      1)在创建了三个随机时间序列之后,我们定义了一个包含一个 Axes的图(fig)。

      2)我们直接调用 ax 的方法来创建堆积面积图并添加图例、标题和 y 轴标签。 在面向对象的方法下,很明显,所有这些都是 ax 的属性。

      3)tiny_layout() 整体应用于 Figure 对象以清理空白填充。

#yyds干货盘点#利用Matplotlib库画图(三)_时间序列_02

      让我们看在一个图中包含多个子图(轴)的示例,从离散均匀分布中绘制的两个相关数组:

     

#yyds干货盘点#利用Matplotlib库画图(三)_解包_03

 

   

#yyds干货盘点#利用Matplotlib库画图(三)_子图_04

      在这个例子中还有更多内容:

      1)因为我们正在创建一个“1x2”图形,所以 plt.subplots(1, 2) 的返回结果现在是一个 Figure 对象和一个 Axes 对象的 NumPy 数组。

      2)我们分别处理 ax1 和 ax2,这在有状态方法中很难做到。 最后一行很好地说明了对象层次结构,我们正在修改属于第二个轴的 yaxis,将其刻度和刻度标签放在右侧。

      3)美元符号内的文本利用 TeX 标记将变量置于斜体中。

      请记住,多个轴可以包含在或“属于”给定图形中。 在上面的例子中, fig.axes 为我们获取了所有 Axes 对象的列表:

(fig.axes[0] is ax1, fig.axes[1] is ax2)

(True, True)

      更进一步,我们也可以创建一个包含 2x2 Axes 对象网格的图形:

fig, ax = plt.subplots(nrows=2, ncols=2, figsize=(7, 7))

      现在,什么是ax? 它不再是单个 Axes,而是它们的二维 NumPy 数组:

>>> type(ax)

numpy.ndarray

type(ax)

array([[<matplotlib.axes._subplots.AxesSubplot object at 0x1106daf98>,

<matplotlib.axes._subplots.AxesSubplot object at 0x113045c88>],

[<matplotlib.axes._subplots.AxesSubplot object at 0x11d573cf8>,

<matplotlib.axes._subplots.AxesSubplot object at 0x1130117f0>]],

dtype=object)


>>> ax

ax.shape

(2, 2)

      该函数的说明文档中重申了这一点:

      “ax can be either a single matplotlib.axes.Axes object or an array of Axes objects if more than one subplot was created.”

      我们现在需要在每个轴上调用绘图方法(但不是 NumPy 数组,在这种情况下它只是一个容器)。 解决此问题的常见方法是在将数组展平为一维后使用可迭代解包:

fig, ax = plt.subplots(nrows=2, ncols=2, figsize=(7, 7))

ax1, ax2, ax3, ax4 = ax.flatten()

      为了说明一些更高级的子图功能,让我们使用 Python 标准库中的 io、tarfile 和 urllib 从压缩的 tar 档案中提取一些加州宏观经济住房数据。

from io import BytesIO

import tarfile

from urllib.request import urlopen

url = 'http://www.dcc.fc.up.pt/~ltorgo/Regression/cal_housing.tgz'

b = BytesIO(urlopen(url).read())

fpath = 'CaliforniaHousing/cal_housing.data'

with tarfile.open(mode='r', fileobj=b) as archive:

... housing = np.loadtxt(archive.extractfile(fpath), delimiter=',')

       使用统计术语,下面的“响应”变量 y 是一个地区的平均房屋价值。 pop 和 age 分别是该地区的人口和平均房屋年龄:

y = housing[:, -1]

pop, age = housing[:, [4, 7]].T

... ax.text(.55, .8, text,

... horizontalalignment='center',

... transform=ax.transAxes,

... bbox=dict(facecolor='white', alpha=0.6),

... fontsize=12.5)

... return ax

      接下来让我们定义一个“辅助函数”,它将文本框放置在绘图内并充当“绘图内标题”:

>>> def add_titlebox(ax, text):

... ax.text(.55, .8, text,

... horizontalalignment='center',

... transform=ax.transAxes,

... bbox=dict(facecolor='white', alpha=0.6),

... fontsize=12.5)

... return ax

      我们准备做一些绘图。 Matplotlib 的 gridspec 模块允许更多的子图定制。 pyplot 的 subplot2grid() 与这个模块很好地交互。 假设我们要创建这样的布局:

#yyds干货盘点#利用Matplotlib库画图(三)_子图_05

      上面,我们实际拥有的是一个 3x2 的网格。 ax1 是 ax2/ax3 的高度和宽度的两倍,这意味着它占用了两列和两行。

#yyds干货盘点#利用Matplotlib库画图(三)_时间序列_06

      subplot2grid() 的第二个参数是轴在网格中的(行,列)位置:

gridsize = (3, 2)

fig = plt.figure(figsize=(12, 8))

ax1 = plt.subplot2grid(gridsize, (0, 0), colspan=2, rowspan=2)

ax2 = plt.subplot2grid(gridsize, (2, 0))

ax3 = plt.subplot2grid(gridsize, (2, 1))

      现在,我们可以照常进行,分别修改每个轴:

ax1.set_title('Home value as a function of home age & area population',

... fontsize=14)

sctr = ax1.scatter(x=age, y=pop, c=y, cmap='RdYlGn')

plt.colorbar(sctr, ax=ax1, format='$%d')

ax1.set_yscale('log')

ax2.hist(age, bins='auto')

ax3.hist(pop, bins='auto', log=True)

add_titlebox(ax2, 'Histogram: home age')

add_titlebox(ax3, 'Histogram: area population (log scl.)')

#yyds干货盘点#利用Matplotlib库画图(三)_迭代_07

      上面,colorbar()(与之前的 ColorMap 不同)直接在 Figure 上调用,而不是在 Axes 上调用。 它的第一个参数使用 Matplotlib 的 .scatter() 并且是 ax1.scatter() 的结果,它用作 y 值到 ColorMap 的映射。 从视觉上看,当我们在 y 轴上上下移动时,颜色(y 变量)没有太大差异,这表明房屋年龄似乎是房屋价值的更强决定因素。