随着对Matplotlib的学习的深入,我们会很自然地发现,似乎Matplotlib中有两种不同的绘图语法。
一种是类似Matlab的语法,另一种就是面向对象语法。也正是由于这一原因,很多人都觉得Matplotlib学习曲线陡峭,对新手一点都不友好。由于Matplotlib最初的目的是在Python中实现Matlab的绘图功能,所以它采用了类似的语法,即无状态语法(stateful)。所谓无状态是指plt会记录当前的axes是哪个,当使用plt.{绘图函数}绘图时,它将在当前的那个子图上绘制。简单来说,面向对象和类Matlab语法的区别就是面向对象是在指定axes对象上面绘图,而类Matlab语法中所有的绘图则是使用的plt()在当前那个axes对象上绘制。
那么问题来了,如果想绘制两幅并列的图,采用类Matlab的语法该怎么实现呢?
其实很简单,只要每次先建立一幅子图(使用plt.subplot()或plt.add_subplot()函数),然后使用plt.plot()或plt.{其他绘图属性函数}来修改对应的子图(axes)就可以了。这里需要牢记,不管调用的是plt的哪个方法,绘图都是在当前的axes(子图)上进行。下面来看一下代码。
这段代码产生的输出与图9.7相同,与前一段代码的区别在于这段代码使用的是类Matlab语法绘图,没有明确指明axes对象。其中,代码plt.subplot(1,2,1)中前两个参数(1,2)分别代表了新建的子图的行数与列数,这里是1行2列,第三个参数“1”指定当前子图为第1个子图,后续对应的plt函数将在该子图进行图形绘制。为了方便获取或设定axes与figure,Matplotlib中提供了plt.gca()和plt.gcf()函数来获取当前axes和figure。而plt.cla()和plt.clf()函数则可以清除当前axes和figure。看起来面向对象的绘图方式与类Matlab方式相比似乎没有太大优势,不过如果要绘制如图9.8所示的图形呢?
图9.8 有4幅子图的图形
此时如果采用无状态方式绘图,代码将大量重复,而采用面向对象方式则相对简单,示例如下。
这段代码通过循环遍历axes对象的方式,将重复性的工作放到了一起,绘图代码得到了极大简化。此外,还可以使用plt.suptitle()函数为整个绘图添加一个标题(plt.title()是给当前的子图添加标题),verticalalignment='bottom'参数给出了对齐方式是底部。最后,这段代码使用了ax.yaxis.set_ticks_position('none')使得Y轴不绘制刻度标记。这也是使用面向对象的好处,理论上用户可以获得axes对象的所有子对象,之后调用该子对象的方法或属性对图形进行修改,下面将进一步讨论这种设置图形属性的方式。