文章目录
前言
在上一章中,我们主要介绍了通过fig.add_subplot()
和fig.add_ases()
的方法来增加子图,第二种方法可以较为灵活的实现任意格式的画面布局,而本文将介绍其他的布局方法,使得在实际绘图过程中可以更快得到自己想要的布局。
一、绘图布局
1.1 子图集(plt.subplots())
之前我们通过在Figure上利用add_subplot的方式来添加子图,实际上我们可以用pyplot中的subplots功能来快速创建画板Figure多个子图集,语法如下:
plt.subplots(nrows=1, ncols=1, *, sharex=False, sharey=False, squeeze=True, subplot_kw, gridspec_kw)
这里主要的参数有这么几个:
nrows
: 行数ncols
: 列数sharex
和sharey
: 表示这些子图的xticks和yticks是否是一样的,默认值都是Falsesqueeze
: 表示返回的子图对象的数组格式是否被压缩,即squeeze=True
,对于行数为1或者列数为1,则用一维数组格式(1DArray)返回子图对象,squeeze=False
,哪怕行数为1或者列数为1,仍用二维数组格式(2DArray)返回子图对象。下文会利用实例解释如何用返回的子图对象的数组格式来对子图进行操作。subplot_kw
: 用字典格式将参数传递给add_subplot()
,比如可以传颜色等参数,详见add_subplot()
。gridspec_kw
: 用字典格式将参数传递给mpl.gridspec.GridSpec()
,用于创建子图所摆放的网格。这里的gridspec时matplotlib中另一个重要的画板布局的class,也会在下文介绍。
我们常用这两种方式来利用plt.subplots()
创建画板和子图集:
#方法一:
fig,((ax1,ax2,ax3),(ax4,ax5,ax6),(ax7,ax8,ax9))=plt.subplots(3,3,sharex=True,sharey=True)
#方法二:
fig,axes=plt.subplots(3,3,sharex=True,sharey=True)
显然,这两种方法都能得到画板fig
和子图集,区别在于:我们可以用3*3的tuple来将这些子图集一一命名,如法一,然后直接利用这些名字来操作对应子图;也可以像法二一样,用axes来表示整个子图集,然后可以根据用数组格式的调用,来对操作子图集中的子图。
比如我们要对最中间的那张子图来画一条直线,两种方法的绘图语句如下:
import numpy as np
pos=np.arange(0,5,1)
方法一:
ax5.plot(pos)
方法二:
axes[1,1].plot(pos)
对于方法一,很好理解,对于方法二,这个调用手法本质上就是把axes看成了一个二维矩阵,则axes[1,1]
代表从上往下,从左往右数第二行第二个(坐标从0开始)。这里回头看squeeze
参数,默认值为True,表示如果你做2*1的子图,他将返回一维矩阵,你就不能用axes[0,0]
来取第一个子图,只能用axes[0]
来取。
- 调整子图之间的间距
plt.subplots_adjust()
可以用来调整子图大小,也可以用其中的子图之间的行间距和列间距,如下:
plt.subplots_adjust(wspace = 0.0,hspace = 0.0)
for ax in plt.gcf().get_axes():
ax.tick_params(bottom=False,left=False)
这样可以将子图间的行间距和列间距都调整为0。
如果觉得内部的刻度轴太多余,可以用tick_params()来去除,语法见下文。
- 解决内部子图轴刻度不可见问题
如上图所示,我们发现如果共享了x轴刻度和y轴刻度,matplotlib会自动隐藏子图的内部刻度,这个问题困扰了我一段时间,在老版本的matplotlib中,只要将各个子图的左和下两个xticklabel设置为可见就可以了,如下:
for ax in plt.gcf().get_axes():
for label in ax.get_xticklabels() + ax.get_yticklabels():
label.set_visible(True)
plt.gcf().canvas.draw() #有的编辑器需要再运行这个语句来实现绘画
但在新的matplotlib中(我用的是3.6.0版本)就不可以了,最终我通过设置对应的axes的tick_params()解决了这个问题,里面的参数left、right、top、bottom
表示刻度线,而labelleft、labeltop、labelright、labelbottom
则表示刻度标签,不像显示,设置为False即可示例代码如下:
for ax in plt.gcf().get_axes():
ax.tick_params(labelbottom=True, labelleft=True)
1.2 马赛克子图(plt.subplot_mosaic())
这个布局功能也是我在解决subplots() x、y轴共刻度时内部图刻度不可见时找到的新的布局方法,语法如下:
plt.subplot_mosaic(mosaic, sharex=False, sharey=False, subplot_kw, gridspec_kw, empty_sentinel='.', **fig_kw)
大部分语法与subplots一样,除了以下两个:
mosaic
: 可以传入list或者str来进行视觉布局,见下文例子。empty_sentinel
:用来表示此位置上子图为空,默认用’.'表示此位置上子图为空。
比如我们想将子图分为3*3 一共9块,第一行第一、二列合并,第一列第二、三行合并,第三列第一、二行合并,第三行第二列不显示,可以用如下两种办法来实现(我们顺便设置了内部子图的刻度与子图间距):
- 用list做mosaic参数创建图像
import matplotlib.pyplot as plt
fig,axes=plt.subplot_mosaic([['A','A','B'],
['C','D','B'],
['C','.','E']],sharex=True,sharey=True)
for ax in plt.gcf().get_axes():
ax.tick_params(labelbottom=True, labelleft