导读
上篇中,介绍了numpy的常用接口及使用,并对部分接口方法进行了详细对比。与之齐名,matplotlib作为数据科学的的另一必备库,算得上是python可视化领域的元老,更是很多高级可视化库的底层基础,其重要性不言而喻。
本篇对matplotlib进行系统性介绍,不会面面俱到,但求体系完备、详略得当。
行文目录结构,重点是右三分支
01 关于matplotlib
matplotlib是python的一个绘图库,与numpy、pandas共享数据科学三剑客的美誉,也是很多高级可视化库的基础。matplotlib不是python内置库,调用前需手动安装,且需依赖numpy库。截至当前,matplotlib发行版本号为3.2.1,适配python3.6及以上版本。
matplotlib,是matrix + plot + library的缩写,虽然命名很是直观,但个人接触之初却是常常不禁嗤之以鼻:
当然,我等作为使用者、调包侠,自然是无法领会开发者的独特考虑,也绝无资格对其评三道四,仅做吐槽一二。
pyplot部分调用模块
前面说到,调用matplotlib库绘图一般是用pyplot子模块,其集成了绝大部分常用方法接口,查看pyplot源码文件可以发现,它内部调用了matplotlib路径下的大部分子模块(不是全部),共同完成各种丰富的绘图功能。其中有两个需要重点指出:figure和axes,其中前者为所有绘图操作定义了顶层类对象Figure,相当于是提供了画板;而后者则定义了画板中的每一个绘图对象Axes,相当于画板内的各个子图。换句话说,figure是axes的父容器,而axes是figure的内部元素,而我们常用的各种图表、图例、坐标轴等则又是axes的内部元素。
当然,之所以不能称pyplot为一级命名空间的原因,不仅仅在于它在形式上隶属于matplotlib,最主要的在于它还不算是matplotlib的“独裁者”,因为matplotlib的另一个重要模块——pylab——或许称得上是真正意义上的集大成者:pylab被定位是python中对MATLAB的替代产品,也就是说凡是MATLAB可以实现的功能,pylab通通都要有,例如矩阵运算(包括常规矩阵运算、线性代数、随机数、FFT等)、绘图功能等等。
pylab导入的那些重量级模块
至此,关于matplotlib的pyplot和pylab两个子模块,我们可以得出2点结论:
pylab集成了numpy和pyplot全部功能
当了解pylab模块功能之后,才真正理解开发者的深谋远虑:原以为matplotlib的意思是"面向矩阵的绘图库",哪知其真正意义是"矩阵+绘图库",绘图只是它的一半。不过,也正因为pylab模块集成了过多的功能,直接调用并不是一个明智的选择,官方已不建议用其绘图。
注:按照惯例,本文后续多以plt作为matplotlib.pyplot别名使用。
02 3种绘图接口
用matplotlib绘制可视化图表,主要有3种接口形式:
plt接口和面向对象接口混合绘图
鉴于pylab的特殊性,matplotlib绘图主要采用前2种方式。而在二者之间:
需要指出,Axes从形式上是坐标轴axis一词的复数形式,但意义上却远非2个或多个坐标轴那么简单:如果将Figure比作是画板的话,那么Axes就是画板中的各个子图,这个子图提供了真正用于绘图的空间,除了包含纯粹的两个坐标轴(axes)外,自然还包括图形、图例等。所以准确的讲,如果说Axes和坐标轴有何关联的话,那么Axes应该算是广义的坐标轴,或简单称之为子图即可。
03 绘图3步走
如同把大象装进冰箱需要3步一样,用matplotlib绘图一般也可以分3步。下面以plt接口绘图为例,面向对象接口绘图流程完全一致,仅仅是个别接口方法名略有改动:
plt.subplots同时返回figure和axes实例
默认将最后一个axes子图作为"当前"图
plot接口文档及部分参数
当然,各图表接口参数繁多且不尽一致,全部熟记几乎不现实,可仅记住常用参数及相关可选项,其他留作使用时查阅即可
关于图例配置的官方解释
plt接口绘图中配置常用图例
前面提到,绘图接口有2种形式,分别是面向"当前"图的plt接口和面向对象接口,在这2种方式的相应接口中,多数接口名是一致的,例如:plt.plot()和axes.plot()、plt.legend()和axes.legend(),但也有一些不一致的接口:
对此,一方面两类接口虽然略有区别,但也还算有规律;另一方面,在面向对象绘图配置图例时,有更为便捷的设置图例接口axes.set(),其可以接收多种参数一次性完成所有配置,这也正是面向对象绘图的强大之处。
04 自定义子图
前面提到,figure为绘图创建了画板,而axes基于当前画板创建了1个或多个子图对象。为了创建各种形式的子图,matplotlib主要支持4种添加子图的方式。
常用的添加子图的方法莫过于subplot和subplots两个接口,其中前者用于一次添加一个子图,而后者则是创建一组子图。
除此之外,plt.axes也可通过接收尺寸参数实现多子图绘制:在添加子图时传入一个含有4个数值的元组,分别表示子图的底坐标和左坐标(设置子图原点位置)、宽度和高度(设置子图大小),从而间接实现子图仅占据画板的一块子区域。相应的方法接口在面向对象接口中是fig.add_axes(),仅仅是接口名字不同,但参数和原理是一致的。例如:
应用plt.axes绘制多子图
通过axes绘制多子图,应对简单需求尚可,但面对复杂图表绘制时难免过于繁琐:需要手工计算各子图的原点位置和大小,意味着可能需要多次尝试。此时,可选的另一种绘制多子图的接口是plt.GridSpec。实际上,GridSpec只是对subplot接口的一个变形,本质上仍然是执行类似subplot多子图流程:通过切片将多子图合并,实现不规则多子图的绘制。与subplot、axes在面向对象和plt两类绘图接口间的区别类似,GridSpec在面向对象时的接口为add_gridspec()。
这里直接给出官网的一个绘制图例,具体可查看官方示例代码:
应用plt.GridSpec实现复杂多子图绘制
05 自定义配置
实际上,前述在配置图例过程中,每次绘制都需要进行大量自定义代码设置(这也是matplotlib的一个短板),在少量绘图工作时尚可接受,但在大量相似绘图存在重复操作时,仍然采取这一方法不会是一个明智的选择(虽然也可以简单的封装成一个函数)。
为此,matplotlib提供了自定义参数实现批量配置——rcParams,全称runtime configuration Parameters,即运行时配置参数。顾名思义,就是在python程序运行时临时执行的配置参数。rcParams是一个字典格式,当前共有299个键值对,分别对应一组参数配置选项。其中用得最多的可能是通过设置字体和减号编码来解决乱码的问题,但实际上它的功能强大之处可远非如此。
设置rcParams解决中文乱码的问题
另一个简单易用的自定义配置选项是style,即设置绘图风格,最早在matplotlib1.4版本中引入,当前共支持26种绘图风格,这里的绘图风格类似于很多IDE支持不同主题。可以通过plt.style.available命令查看,返回一个可选风格的列表。例如,以下命令设置绘图为senborn风格
设置seaborn绘图风格
06 走向3D
在可视化愈发重要的当下,matplotlib当然不仅支持简单的2D图表绘制,其也提供了对3D绘图的丰富接口。
如果需要绘制真3D图形,则需要额外导入matplotlib专用3D绘图库:mpl_toolkits,包括3D版的Axes对象和常用图表的3D版:
07 更高级的封装
matplotlib提供了大量丰富的可视化绘图接口,但仍然存在短板:例如绘图操作略显繁琐、图表不够美观。为此,在matplotlib基础上产生了一些封装更为便捷的可视化库,实现更为简单易用的接口和美观的图表形式,包括:
相关阅读: