ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
# 14.4 【可视化之matplotlib】难点:子图与子区 在 matplotlib 中有两个非常重要,而且很容易混淆的概念,一个是 subplot,一个是axes。这两个概念将贯穿整个 matplotlib 学习历程。在往后进行深度研究之前呢,务必要先弄懂这两个概念,否则将后面的绘制代码,我相信你一定会一头雾水的。 在查阅了一些相关中文文档后,我暂且将 subplot 称为 子区,而将 axes 称之为 子图。这些只是为了方便后续的描述。 你会发现,即使翻译成中文后,还是无法帮助我们直观的理解这两个概念。因此我特地画了几张图,来解释这两者到底是啥区别。 ## 1. 子区 子区(subplot),是基于 网格(grid)来规划的。 比如,这个写法 ```python plt.subplot(2, 2, 1) ``` 是将当前图像(figure)按 2 行 2 列的布局进行分割,然后取索引为 1 的子图。注意 matlibplot 是完全借鉴了 MATLAB 的思想,所以的起始索引为 1,不像 Python 的起始索引为 0。 ![](http://image.iswbm.com/20190511165103.png) 他有好几种写法,这里写我在官网学到的几个方法。 这几种写法是等价的。 ```python # 第一种写法 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 索引列(第一列)。 学完了以上内容,我们来使用最简单的方法(第一种)来实践一下。 ```python 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() ``` ![](http://image.iswbm.com/20190511165132.png) ## 2. 子图 子图(axes),和子区(subplot)非常相似,一个子图可能是由一个或多个子区域构成的。它比子区更加灵活。 它可以是这样 ![](http://image.iswbm.com/20190511165152.png) 要实现如上这个效果。常用的有两种方法。 第一种,使用 `subplot2grid` ```python 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` (可以切片) ```python 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]) ``` 这个比较规则的划分我们举个例子看看。 ![](http://image.iswbm.com/20190511165159.png) 代码如下: ```python 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)来完成它。 ![](http://image.iswbm.com/20190511165211.png) 图中的 axes 是如何实现的,刚开始我也有点懵逼,在查阅了官方文档后,我才明白。 ![](http://image.iswbm.com/20190511165221.png) `left` 是指,离左边界的距离。 `bottom` 是指,离底边的距离。 `width` 是指,子图的宽度。 `height` 是指,子图的高度。 以上四个参数,是一个(0, 1)的比例(相比于figure),而不是具体数值。 同样地,这个我们也来看一个例子。 这个图的亮点,在于中间,多了两个子图,就像往图中贴上了两个插画一样。 ![](http://image.iswbm.com/20190511165229.png) 那么这个如何实现呢? ```python 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() ``` ---