本文主要介绍pyplot接口的使用。matplotlib.pyplot的主要作用就是提供一套函数集合来模拟MATLAB的接口,让使用MATLAB的人无缝迁移过来。每个pyplot函数都是做一些让画布产生改变的事情,比如创建一个画布,创建绘图区的坐标系,在绘图区画一些直线,给曲线添加注释等等。
在matplotlib.pyplot里,当函数调用之后内部会保留各种状态,以便跟踪当前绘图的画布和绘图的区域,并且这些函数直接作用于当前的坐标系。
注意:pyplot API比面向对象的API缺少灵活性,因为它们使用同一套的状态。不过大多数这里使用的API都可以在坐标轴对象里使用。
使用pyplot接口来创建一个可视化的绘图是非常简单的,如下:
import matplotlib.pyplot as plt
plt.plot([1, 2, 3, 4])
plt.ylabel('some numbers')
plt.show()
看了上图,你也许非常好奇,为什么X轴的坐标是从0到3,而Y轴的坐标是从1到4。其实当你只传一个列表给plot函数时,matplotlib就会假定你传送的参数是Y轴显示的值,然后根据传送的值自动生成X轴的坐标值。由于python的数组开始的范围是从0开始,因此x轴的值默认是从0到y轴值的长度,从而生成X轴的坐标值为[0,1,2,3]
。
plot函数是一个参数自适应的函数,它可以处理不同的参数个数输入,比如下面传入两个列表,X-Y轴的数据值:
plt.plot([1, 2, 3, 4], [1, 4, 9, 16])
从上面可以看到,X轴的列表是在前面,而Y轴的列表是在后面,如果只有一个参数时,默认是Y轴的值。
选择plot函数绘图曲线的类型
在plot函数里,除了X轴和Y轴的坐标值,还可以传送第三个参数,这个参数用来说明绘图曲线的颜色和线的类型。它采用字母和符号来标记绘图曲线的类型,这是从MATLAB学习过来的规则。可以把颜色的字符串和线的类型字符串进行并接起来。这个函数默认的值是‘b-’,这是表示蓝色的实线。例如,把上面的例子改为红色圆圈表示:
plt.plot([1, 2, 3, 4], [1, 4, 9, 16], 'ro')
plt.axis([0, 6, 0, 20])
plt.show()
如果你想详细地了解所有线的颜色和类型,可以查看下面的连接:
https://matplotlib.org/api/_as_gen/matplotlib.pyplot.plot.html#matplotlib.pyplot.plot
在上面的例子里,使用到一个轴对象函数plt.axis,传递了一个列表[xmin, xmax, ymin, ymax]给它,这四个值限定坐标系的显示范围。
如果matplotlib只能处理列表数据对象,那么它的作用是相当有限的,不过它还可以处理numpy数组对象,这样就很方便数据处理中应用了。实际上,在matplotlib内部是把所有要处理的数据对象,全部转换为numpy数组来处理的。下面的例子演示了使用numpy数组,以及在同一个函数里绘制多条不同类型的曲线的方式:
import numpy as np
# evenly sampled time at 200ms intervals
t = np.arange(0., 5., 0.2)
# red dashes, blue squares and green triangles
plt.plot(t, t, 'r--', t, t**2, 'bs', t, t**3, 'g^')
plt.show()
显示字典参数的数据
在某些特定的情况下,也许数据的来源是由字典描述的,比如字符串作为名称,当作字典的关键字,而数组作为字典的键值。例如在numpy里的 numpy.recarray数组, pandas.DataFrame数组。
matplotlib允许使用字典对象的方式来传入参数,关键字作为名称,数组作为关键值,如下面的例子:
data = {'a': np.arange(50),
'c': np.random.randint(0, 50, 50),
'd': np.random.randn(50)}
data['b'] = data['a'] + 10 * np.random.randn(50)
data['d'] = np.abs(data['d']) * 100
plt.scatter('a', 'b', c='c', s='d', data=data)
plt.xlabel('entry a')
plt.ylabel('entry b')
plt.show()
显示分类变量的数据
有时候需要显示分类变量的数据,matplotlib允许直接传送分类变量的数据,例如:
names = ['group_a', 'group_b', 'group_c']
values = [1, 10, 100]
plt.figure(figsize=(9, 3))
plt.subplot(131)
plt.bar(names, values)
plt.subplot(132)
plt.scatter(names, values)
plt.subplot(133)
plt.plot(names, values)
plt.suptitle('Categorical Plotting')
plt.show()
控制画线的类型
在计算机里显示直线,有很多属性,比如线的宽度、线的连接状态、反锯齿显示等等。具体可以查看下面的连接:
https://matplotlib.org/api/_as_gen/matplotlib.lines.Line2D.html#matplotlib.lines.Line2D
可以有几种方法来设置线的属性:
1)使用关键值参数:
plt.plot(x, y, linewidth=2.0)
2)使用Line2D实例对象的方法,plot函数返回一个Line2D对象实例,比如line1, line2 = plot(x1, y1, x2, y2)。在下面的代码里,假定是画一条曲线,那么就返回一条line对象,因此列表长度为1。
使用元组拆包得到线对象line,它是返回列表里第一个参数:
line, = plt.plot(x, y, '-')
line.set_antialiased(False) # 关闭反锯齿的功能
3)使用setp方法。下面采用类似MATLAB的方式来设置线的属性。setp可以接收列表对象或者单一对象,可以采用关键字参数或MATLAB字符串对:
lines = plt.plot(x1, y1, x2, y2)
# 关键字参数
plt.setp(lines, color='r', linewidth=2.0)
# MATLAB 类似字符串对
plt.setp(lines, 'color', 'r', 'linewidth', 2.0)
下面Line2D对象的属性:
如果你想获取线已经设置的属性,可以调用函数setp来获取:
In [69]: lines = plt.plot([1, 2, 3])
In [70]: plt.setp(lines)
alpha: float
animated: [True | False]
antialiased or aa: [True | False]
...snip
显示多个子图和坐标系
与MATLAB类似,pyplot也有当前画布和当前坐标系的概念。所有绘图函数都是作用于当前坐标系里,通过函数gca返回当前坐标系,它是matplotlib.axes.Axes实例。通过函数gcf返回当前画布对象,它是 matplotlib.figure.Figure实例对象。一般情况下,你不需要担心怎么样使用它们,因为这些是自动隐藏在系统背后。下面的例子演示创建两个坐标系绘图:
def f(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()
plt.subplot(211)
plt.plot(t1, f(t1), 'bo', t2, f(t2), 'k')
plt.subplot(212)
plt.plot(t2, np.cos(2*np.pi*t2), 'r--')
plt.show()
plt.figure()函数在这里调用采用默认的参数,相当于调用figure(1)。同样道理,如果你不设置参数给subplot()函数,是相当于调用subplot(111)。subplot()的参数是表示多行、多少列、第几个位置,而位置参数是从1到行数*列数。如果位置参数小于10,可以采用两种方式,第一种是subplot(211),第二种是subplot(2,1,1)。如果大于10,只能用第二种方式。
你也能创建任意数量的子坐标系。可以通过手动的方式进行添加,比如使用axes对象,通过指定axes([left, bottom, width, height])的位置,left, bottom, width, height是使用0到1的比例坐标,比如left 的值为0.1就是表示离左边10%位置。
也可以通过多次调用 figure()函数来创建多个画布,每调用一次在参数里增加一。当然每个画布可以有多个坐标系和多个子图,这个就是按需要进行设置了。下面的例子来演示:
import matplotlib.pyplot as plt
plt.figure(1) # 第一个画布
plt.subplot(211) # 在第一个画布里第一个子图
plt.plot([1, 2, 3])
plt.subplot(212) # 在第一个画布里第二个子图
plt.plot([4, 5, 6])
plt.figure(2) # 第二个画布
plt.plot([4, 5, 6]) # 默认是创建一个子图subplot(111)
plt.figure(1) # 切换回到第一个画布,但子图仍然是第二个子图,subplot(212)
plt.subplot(211) # 切换到第一个子图,调用 subplot(211)
plt.title('Easy as 1, 2, 3') # 设置子图211的标题
如果想清除当前的画布,可以调用clf()函数;清除当前坐标系调用函数cla()。如果您觉得在幕后维护状态(特别是当前图像、图形和轴)很烦人,请不要绝望:这只是一个面向对象API的简单状态包装器,您可以改用它(请参阅https://matplotlib.org/tutorials/intermediate/artists.html教程)。
如果你想创建很多画布,不过要注意一件事情,就是每一个画布会占用很多内存,并且这些内存并不会随着窗口的关闭而释放,而是直到调用函数close()才会真正删除。因为内部有很多引用关闭,需要等到调用close()才能明确地删除这种引用的关系,仅仅关闭或隐藏窗口的显示是不能够做到的。
显示文本
文本可以添加在任何位置显示。xlabel、ylabel和title可以设置X轴、Y轴和标题的文本,至于其它的文本可以参考下面的连接:
https://matplotlib.org/tutorials/text/text_intro.html
下面的例子来演示一下文本的使用:
mu, sigma = 100, 15
x = mu + sigma * np.random.randn(10000)
# the histogram of the data
n, bins, patches = plt.hist(x, 50, density=1, facecolor='g', alpha=0.75)
plt.xlabel('Smarts')
plt.ylabel('Probability')
plt.title('Histogram of IQ')
plt.text(60, .025, r'\mu=100,\ \sigma=15')
plt.axis([40, 160, 0, 0.03])
plt.grid(True)
plt.show()
在例子里使用text函数返回一个matplotlib.text.Text实例对象,然后就可以设置文本显示。如果你想设置文本对象的属性,可以通过关键字参数或setp函数来设置。
t = plt.xlabel(‘my data’, fontsize=14, color=’red’)
这行文本使用关键字参数方式来设置文本的属性,更多的属性可以参考:
https://matplotlib.org/tutorials/text/text_props.html
在文本里显示数学公式
matplotlib支持TeX的数学公式表示,在任何文本区域都可以显示。比如你想在标题里显示像下面的表达式:
就可以写一个TeX的表达式,这个表达式采用美元符号在前后包含,如下:
plt.title(r'\sigma_i=15')
这个字符串里,采用r字符串格式,这样是表示采用原生的字符串格式,这样里面转义字符就不起作用,方便写公式。matplotlib内置了一个TeX表达式分析器和显示引擎,以及数学字体表示,详细内容可以参考下面连接:
https://matplotlib.org/tutorials/text/mathtext.html
你可以自由地使用TeX的功能,而不需要安装任何TeX平台的软件组件。如果在系统里安装了LaTeX或dvipng软件,那么你也可以使用LaTex格式来输出公式表达式,或者保存成postscript。具体参考下面的连接:
https://matplotlib.org/tutorials/text/usetex.html
显示注释文本
前面文本函数可以把文本放到任何地方,那么为什么还要这样一个注释文本函数呢?是因为普通的文本函数用来做注释的工作有点困难,注释文本要考虑两点:一是指出关注点,也就是重要查看的位置坐标,二是文本放在位置,也即是显示注释文本的坐标。因此,注释文本函数提供了两个参数,这两个参数都是元组构成的位置坐标,只要指定这两个坐标,就可以显示注释文本了。annotate()函数第二个参数xy是关注点的坐标,第三个xytext参数是文本显示的坐标,例子如下:
ax = plt.subplot(111)
t = np.arange(0.0, 5.0, 0.01)
s = np.cos(2*np.pi*t)
line, = plt.plot(t, s, lw=2)
plt.annotate('local max', xy=(2, 1), xytext=(3, 1.5),
arrowprops=dict(facecolor='black', shrink=0.05),)
plt.ylim(-2, 2)
plt.show()
在上面的例子里,xy和xytext的位置坐标全是使用数据坐标,其实它们有其它的坐标可以选择,可以参考下面的连接:
https://matplotlib.org/tutorials/text/annotations.html#annotations-tutorial
对数坐标轴和其它非线性坐标轴
matplotlib.pyplot不仅仅支持线性的坐标轴,也支持对数坐标轴和其它非线性的坐标轴。如果要显示的数据是跨度非常大,比如几个数量级。matplotlib里更改其它尺度的从标轴是很容易的事情:
plt.xscale(‘log’)
下面来举一个例子,同样的的数据,Y轴选择不同的坐标轴来显示:
# 设置可以重复性的随机种子
np.random.seed(19680801)
# 创建一些随机数位于区间 (0, 1)
y = np.random.normal(loc=0.5, scale=0.4, size=1000)
y = y[(y > 0) & (y < 1)]
y.sort()
x = np.arange(len(y))
# 使用不同尺度坐标轴显示数据
plt.figure()
# 线性坐标轴
plt.subplot(221)
plt.plot(x, y)
plt.yscale('linear')
plt.title('linear')
plt.grid(True)
# log对数坐标轴
plt.subplot(222)
plt.plot(x, y)
plt.yscale('log')
plt.title('log')
plt.grid(True)
# 对称对数
plt.subplot(223)
plt.plot(x, y - y.mean())
plt.yscale('symlog', linthresh=0.01)
plt.title('symlog')
plt.grid(True)
# logit
plt.subplot(224)
plt.plot(x, y)
plt.yscale('logit')
plt.title('logit')
plt.grid(True)
# 调整子图的显示,由于y轴刻度显示:"1 - 10^{-3}"
plt.subplots_adjust(top=0.92, bottom=0.08, left=0.10, right=0.95, hspace=0.25,
wspace=0.35)
plt.show()
如果上面四种不满足你的数据显示要求,也可以自己来定义一个尺度坐标轴来显示,参考下面的连接:
https://matplotlib.org/devel/add_new_projection.html#adding-new-scales