6.4 子图与子区 难点突破¶
在 matplotlib 中有两个非常重要,而且很容易混淆的概念,一个是 subplot,一个是axes。这两个概念将贯穿整个 matplotlib 学习历程。在往后进行深度研究之前呢,务必要先弄懂这两个概念,否则将后面的绘制代码,我相信你一定会一头雾水的。
在查阅了一些相关中文文档后,我暂且将 subplot 称为 子区,而将 axes 称之为 子图。这些只是为了方便后续的描述。
你会发现,即使翻译成中文后,还是无法帮助我们直观的理解这两个概念。因此我特地画了几张图,来解释这两者到底是啥区别。
6.4.1 子区¶
子区(subplot),是基于 网格(grid)来规划的。
比如,这个写法
plt.subplot(2, 2, 1)
是将当前图像(figure)按 2 行 2 列的布局进行分割,然后取索引为 1
的子图。注意 matlibplot 是完全借鉴了 MATLAB 的思想,所以的起始索引为
1,不像 Python 的起始索引为 0。
他有好几种写法,这里写我在官网学到的几个方法。
这几种写法是等价的。
# 第一种写法
ax = plt.subplot(2, 2, 1)
# 第二种写法
ax = plt.subplot2grid((2, 2), (0, 0))
# 第三种写法: GridSpec
import matplotlib.gridspec as gridspec
gs = gridspec.GridSpec(2, 2)
ax = plt.subplot(gs[0, 0])
这第二种写法呢,是将图像分成 2 行 2 列,再取 第 0 索引行(第一行),第 0 索引列(第一列)。
学完了以上内容,我们来使用最简单的方法(第一种)来实践一下。
import numpy as np
import matplotlib.pyplot as plt
def fig(t):
return np.exp(-t) * np.cos(2*np.pi*t)
t1 = np.arange(0.0, 5.0, 0.1)
t2 = np.arange(0.0, 5.0, 0.02)
plt.figure(1)
# 等同于 plt.subplot(2, 1,1)
plt.subplot(211)
plt.plot(t1, fig(t1), 'bo', t2, fig(t2), 'k')
# 等同于 plt.subplot(2, 1,2)
plt.subplot(212)
plt.plot(t2, np.cos(2*np.pi*t2), 'r--')
plt.show()
6.4.2 子图¶
子图(axes),和子区(subplot)非常相似,一个子图可能是由一个或多个子区域构成的。它比子区更加灵活。
它可以是这样
要实现如上这个效果。常用的有两种方法。
第一种,使用 subplot2grid
axes1 = plt.subplot2grid((3, 3), (0, 0), colspan=3)
axes2 = plt.subplot2grid((3, 3), (1, 0), colspan=2)
axes3 = plt.subplot2grid((3, 3), (1, 2), rowspan=2)
axes4 = plt.subplot2grid((3, 3), (2, 0))
axes5 = plt.subplot2grid((3, 3), (2, 1))
第二种,使用 GridSpec
(可以切片)
import matplotlib.gridspec as gridspec
gs = gridspec.GridSpec(3, 3)
ax1 = plt.subplot(gs[0, :])
ax2 = plt.subplot(gs[1, :-1])
ax3 = plt.subplot(gs[1:, -1])
ax4 = plt.subplot(gs[-1, 0])
ax5 = plt.subplot(gs[-1, -2])
这个比较规则的划分我们举个例子看看。
代码如下:
import numpy as np
import matplotlib.pyplot as plt
def f(t):
return np.exp(-t) * np.cos(2*np.pi*t)
t1 = np.arange(0.0, 3.0, 0.01)
ax1 = plt.subplot(212)
ax1.margins(0.05) # Default margin is 0.05, value 0 means fit
ax1.plot(t1, f(t1), 'k')
ax2 = plt.subplot(221)
ax2.margins(2, 2) # Values >0.0 zoom out
ax2.plot(t1, f(t1), 'r')
ax2.set_title('Zoomed out')
ax3 = plt.subplot(222)
ax3.margins(x=0, y=-0.25) # Values in (-0.5, 0.0) zooms in to center
ax3.plot(t1, f(t1), 'g')
ax3.set_title('Zoomed in')
plt.show()
为什么说,子图的灵活性更高呢,因为它允许把图片放置到图像(figure)中的任何地方(如下图)。所以如果我们想要在一个大图片中嵌套一个小点的图片,我们通过子图(axes)来完成它。
图中的 axes
是如何实现的,刚开始我也有点懵逼,在查阅了官方文档后,我才明白。
left
是指,离左边界的距离。 bottom
是指,离底边的距离。
width
是指,子图的宽度。 height
是指,子图的高度。
以上四个参数,是一个(0, 1)的比例(相比于figure),而不是具体数值。
同样地,这个我们也来看一个例子。
这个图的亮点,在于中间,多了两个子图,就像往图中贴上了两个插画一样。
那么这个如何实现呢?
import matplotlib.pyplot as plt
import numpy as np
# Fixing random state for reproducibility
np.random.seed(19680801)
# create some data to use for the plot
dt = 0.001
t = np.arange(0.0, 10.0, dt)
r = np.exp(-t[:1000] / 0.05) # impulse response
x = np.random.randn(len(t))
s = np.convolve(x, r)[:len(x)] * dt # colored noise
# the main axes is subplot(111) by default
plt.plot(t, s)
plt.axis([0, 1, 1.1 * np.min(s), 2 * np.max(s)])
plt.xlabel('time (s)')
plt.ylabel('current (nA)')
plt.title('Gaussian colored noise')
# this is an inset axes over the main axes
a = plt.axes([.65, .6, .2, .2], facecolor='k')
n, bins, patches = plt.hist(s, 400, density=True)
plt.title('Probability')
plt.xticks([])
plt.yticks([])
# this is another inset axes over the main axes
b = plt.axes([0.2, 0.6, .2, .2], facecolor='k')
plt.plot(t[:len(r)], r)
plt.title('Impulse response')
plt.xlim(0, 0.2)
plt.xticks([])
plt.yticks([])
plt.show()