Matplotlib是建立在Numpy基础之上的可视化程序库。John Hunter在2002年提出了Matplotlib构想——通过IPython补丁,在IPython中使用gnuplot画出类似MATLAB风格的交互式图形。但当时IPython开发者Fernando Perez 正忙着自己博士论文,几个月内没时间审核IPython补丁,John就将IPython补丁写成了Matplotlib,并与2003年发布0.1版本。后来经过美国太空望远镜研究所应用与资助,Matplotlib逐步得到完善。
Matplotlib具有良好的操作系统兼容性和图形显式底层接口兼容性。支持几十种图形显示接口与输出格式,使得用户无论在何种平台,都可以得到想要的图形,这种跨平台特征称为Matplotlib最重要的技术特性,吸引了大量用户,并形成了一个庞大开发者团队,晋升为Python科学计算领域不可或缺的强大武器。
虽然截止到目前,基于R语言的ggplot2,ggvis也开始使用D3js和HTML5 canvas构建网页可视化工具,这让Matplotlib显得略微老旧;已有很多基于Matplotlib的绘图库,如seaborn,Bokeh,Altair等,绘制的图形更加简洁和现代化,但Python社区的大部分绘图工具依赖于matplotlib底层功能;同时掌握Matplotlib语法可以灵活地控制最终图形,因此学习和掌握matplotlib,对数据可视化仍然是非常重要的。
常用 Matplotlib 小技巧¶
深入学习matplotlib之前,先来看看一些常用的技巧与设置。
导入 Matplotlib¶
正如将numpy导入为np,将pandas 导入为pd一样,我们常常这样导入Matplotlib:
import matplotlib as mpl
import matplotlib.pyplot as plt
plt简称是我们在后面使用的一种常态。
设置绘图样式和显式特征¶
应用 plt.style 为图形选择显示风格。在这里选择经典格式,即MATLAB形式的显式风格。
plt.style.use('classic')
为了显式出中文和表示负数的负号,我们需要调整运行时参数设置,简单来讲就是增加中文显式需要的Unicode条件。
# 以下两行代码在中文绘图中几乎是必备的,请确保在使用PLT时放置在前面
plt.rcParams['font.sans-serif'] =['SimHei'] #设置字体为SimHei
plt.rcParams['axes.unicode_minus'] = False #负号显式正常
#如果使用pandas,还可以设置如下:
#pd.set_option('display.float-format',lambda x :'%.2f' % x) #浮点型数显式小数点后2位
用不用show()命令? 如何显示图形¶
应用matplotlib显式图形与每个人的开发环境有密切关系。目前有三种开发环境:脚本、Ipython shell 、Jupyter Notebook.
脚本中实现绘图¶
如果在脚本中实现画图,必须要使用 plt.show() 函数。
plt.show() 函数开启一个事件循环(event loop),找到可用的图形对象,然后打开一个或者多个交互式窗口显示图像。
例如,你建立了一个 myplot.py 脚本文件,里面包含如下内容:
# ------- file: myplot.py ------
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10, 100)
plt.plot(x, np.sin(x))
plt.plot(x, np.cos(x))
plt.show()
你在命令行工具中执行这个脚本,如下面的命令所示,然后会看到一个新窗口,里面会显示你的图形。
$ python myplot.py
plt.show() 命令行在后台完成了许多事情,它需要与你的操作系统图形显示接口取得联系并进行交互。虽然不同操作系统这种交互是不同的,但matplotlib隐藏了所有细节,非常省心。
需要注意的是,在一个Python会话(session)中只能使用一次 plt.show() ,因此,该函数经常放在最后。如果使用多个plt.show(),可能导致难以预料的显示异常。
在 IPython shell中绘图¶
在Ipython中使用matplotlib也是非常方便,在IPython 启动matpoltlib模式即可使用。启动方法是在启动Ipython后,使用Ipython 内部的魔法命令。具体操作如下:
In [1]: %matplotlib
Using matplotlib backend: TkAgg
In [2]: import matplotlib.pyplot as plt
在这种交互中,任何一个plt绘图命令都会打开一个图形窗口,增加新的命令,图形就会更新。有一些变化,如改变已经画好的线条属性,不能做到及时更新,可以使 plt.draw()函数强制更新。在Ipython shell 中启动matplotlib模式之后,就不需要使用plt.show()函数了。
在Jupyter notebook中绘制图形¶
在Notebook中绘制图像与在Ipython shell 中绘制图形类似,也需要使用 %matplotlib启动绘图功能,可以将图直接绘制在Notebook页面中。这里有两种展现形式:
%matplotlib notebook会在 notebook 中启动交互式图形%matplotlib inline会在notebook中启动静态图形
%matplotlib inline
#%matplotlib notebook
matplotlib inline命令运行后,每个单元中创建图形就会直接将PNG格式图形嵌入单元格中。
import numpy as np
x = np.linspace(0, 10, 100)
fig = plt.figure()
plt.title('三角函数曲线')
plt.plot(x, np.sin(x), '-')
plt.plot(x, np.cos(x), '--')
plt.show();
将图像保存为文件¶
一旦使用matplotlib生成图形,可以很方便的保存成各种格式。使用 savefig() 命令,可以生成相应的图形文件。
如将上面的图形保存为PNG文件,可以用如下命令实现:
fig.savefig('my_figure.png')
你会在当前目录中发现一个名为 my_figure.png 新文件。
我们可以 IPython 中的Image对象来显示已经保存的图形:
from IPython.display import Image
Image('my_figure.png')
在 savefig(), 保存的图片文件格式是文件的扩展名。Matplotlib支持许多图形格式,具体格式由操作系统已经按照的图形显示接口来决定。可以用canvas对象的方法查看系统支持的文件格式。
fig.canvas.get_supported_filetypes()
{'eps': 'Encapsulated Postscript',
'jpg': 'Joint Photographic Experts Group',
'jpeg': 'Joint Photographic Experts Group',
'pdf': 'Portable Document Format',
'pgf': 'PGF code for LaTeX',
'png': 'Portable Network Graphics',
'ps': 'Postscript',
'raw': 'Raw RGBA bitmap',
'rgba': 'Raw RGBA bitmap',
'svg': 'Scalable Vector Graphics',
'svgz': 'Scalable Vector Graphics',
'tif': 'Tagged Image File Format',
'tiff': 'Tagged Image File Format',
'webp': 'WebP Image Format'}
两种画图接口¶
Matplotlib有一个让人混淆的特征,它有两种画图接口:一种是便捷的MATLAB风格接口,另一种是功能更为强大的面向对象接口。
MATLAB-风格的接口¶
Matplotlib 早期为了实现 MATLAB 风格设计的,这种风格的书写方式与MATLAB类似。这种风格保存在 pyplot (plt) 接口中。
plt.figure() # 创建一个画布
# 创建第一个图形,并设置相关轴
plt.subplot(2, 1, 1) # (总行, 总列, 第几个)
plt.plot(x, np.sin(x))
# 创建第二个图形
plt.subplot(2, 1, 2)
plt.plot(x, np.cos(x))
plt.show();
一个重要的特性是这种plt接口是一种有状态(stateful)特性:它会持续跟踪当前图形和坐标轴,所有plt命令都可以应用。你可以plt.gcf()(获取当前图形)或者plt()(获取当前坐标轴)来查看具体信息
这种方式虽然又快又方面,但也容易出现问题。如上面的两个图形中,当绘制完成第二个子图后,如何回到第一个子图,并增加新的内容?虽然该接口可以实现,但过于复杂。那么下面基于对象的接口会更加简单一些。
面向对象的接口¶
面向对象接口可以适应更为复杂的场景,更好控制自己图形。在面向对象的接口中,画图函数不再受到当前“活动”图形或者坐标轴的限制,而是变成了显式的Figure 和 Axes 的方法。通过下面代码,可以用面向对象接口重新创建之前的图形。
# 先创建一个图形网格
# ax 是一个包含两个 Axes 对象的数组
fig, ax = plt.subplots(3)
# 在每个对象上调用 plot() 方法
ax[0].plot(x, np.sin(x))
ax[1].plot(x, np.cos(x))
ax[2].plot(x, x**2);
面向对象的绘图接口首先将fig 和 axes 作为图形的对象,其中fig像是一个画布,而axes则是画布中的画,在本例中axes有三个元素,可以分别进行绘图。axes 与axis 由于外形相似,容易混淆,需要注意。Axis是坐标轴对象,而Axes表示的是图形对象中一片区域,在subplot()下是多片区域。
下图是关于几个概念的示意图

关于Matplotlib的图形化介绍如下图:
