15 分钟学会用 Matplotlib 绘制图表

上周做了个股指的回顾,想用Python画图看看最近20年不同指数的变化情况,需要用到常用的matplotlib库来绘制折线图。整个plot函数的功能和参数很多,但作为初学者,我只用到了一些基本和常用的功能,想必普通画图也已经够了。 基础 绘制折线图,实际上是根据x,y两个数组的数据来绘制一组数据点,并将它...

上周做了个股指的回顾,想用Python画图看看最近20年不同指数的变化情况,需要用到常用的matplotlib库来绘制折线图。整个plot函数的功能和参数很多,但作为初学者,我只用到了一些基本和常用的功能,想必普通画图也已经够了。

基础

绘制折线图,实际上是根据x,y两个数组的数据来绘制一组数据点,并将它们用直线连接起来。而将每个点想象在一个直角坐标系中,x,y两个数组中的数据,也就是每个点的xy坐标。所以,在绘制函数中,要求x,y的数目相等,否则便会报错:ValueError: x and y must have same first dimension。
下面是一个最简单的绘制折线的例子,如果你用过Matlab的话一定感到非常熟悉(绘制结果略):

#加载库
import matplotlib.pyplot as plt
#定义数据
x=[1,2,3,4,5]
y=[1,4,9,16,25]

fig,ax = plt.subplots()     #初始化
plt.plot(x, y)              #绘制函数
plt.show()                  #显示

其中plot()函数是最重要的一步,它也同样可以有很多参数,包括颜色,函数的标识名称,绘制点的样式等等,这些参数常用的部分大致如下:

标记 连接线型 标记 数据点型
- 实线(默认) + 加号
虚线 o 圆圈
: 点线 * 星号
-. 点横线 . 实心点
标记 颜色 x
r s 正方形
g 绿 d 钻石
b ^ 上三角
c 蓝绿 v 下三角
m 紫红 < 左三角
y > 右三角
k p 正五角形
w h 正六角形

除了对绘制的折线本身的样式定义,还有对于x,y轴和图例的定义。而在实际中,由于我们通常只是想绘制某个函数或者一组数据的图像,x轴并不那么重要,常常简化为range(0,n),用得更多的是对y轴的定义:
ax.set_ylim(min, max)以及set_yticks(range(min, max, step))。下面是一个更常用的定制版折线图绘制:

y=[1,4,9,16,25]

fig,ax = plt.subplots()
plt.title("This is a plot image")       #图像名称
ax.set_ylim(0, 30, 2)                   # y坐标轴的范围  
ax.set_yticks(range(0, 30, 2))      # y坐标轴的刻度
#绘制折线,其中数据点为实心正五边形,用虚线连接,颜色为蓝色
plt.plot(range(0,5), y, "p:",label="sample", color='b')
plt.legend(loc='best')                  # 设定图例的位置
plt.grid(True)                          # 打开网格
plt.show()

绘制后的结果如下图。其他更多高级的用法随便搜索一下就能找到无数详细介绍,我就不啰嗦了。

多条折线的绘制

有时候我们需要绘制多条折线,用于将不同的函数放在一起比较,会有各种不同的需求。首先定义一下基本概念:下文所谓的“图表”,就是一幅折线图,或者说是一个“子图”;而”窗口”,就是运行之后弹出来的一个window,带有最小化、关闭等按钮。运行语句fig,ax = plt.subplots()后,拿到的两个变量fig, ax实际上就是指初始化的窗口和图像。

在同一个窗口绘制几个不同的图表

如果我们需要在同一个窗口中绘制几个不同的图,即将几幅图并排显示在一块儿,可以使用add_subplot()函数来做到。add_subplot(a,b,c)定义了一个新的图表,它首先将窗口分割成a*b的网格,然后返回第c格(按照从上到下、从左至右的顺序,从1数起)。下面这个例子能比较清晰地看到如何来绘制好几个折线图。

import matplotlib.pyplot as plt
#生成数据
y1=[1,4,9,16,25]
y2=[2,3,4,5,6]
y4=[12,10,1,7,-3]
#在同一个fig中初始化几个不同的图
fig = plt.figure()
ax1 = fig.add_subplot(2,2,1)
#ax2 = fig.add_subplot(2,2,2)
ax3 = fig.add_subplot(2,2,3)
ax4 = fig.add_subplot(2,2,4)
#依次在几个图表上绘制线条
ax1.plot(range(0,5), y1, "p:",label="ax1", color='r')
ax1.legend(loc='best')
ax1.grid(True)
ax4.plot(range(0,5), y4, "o-",label="y4", color='k')
# 不指定目的图表,则绘制在最后一个初始化的窗口中
plt.plot(range(0,5), y2, "o-",label="y2", color='b')

plt.show()

画出的图像: 从图像对照代码我们可以知道:

  1. 当使用ax.plot()时,绘制会指定在某个图表中
  2. 如果不初始化网格内的某个图(add_subplot),那一部份会自动留白(见ax2部分)
  3. 如果使用add_subplot初始化了一个图表却没有画线,则会只有一个自动生成的坐标系和刻度
  4. 如果使用plt.plot/legend等实际上是配置某个图表而不是整个窗口的函数,那么系统会自动使用最后一个初始化的图表,在上面的例子中是ax4。

在同一张图表上画几条不同的折线

有时候需要在同一个图表上画几条不同的折线,也是很简单,直接重复绘制就可以了:

import matplotlib.pyplot as plt
#生成待绘制数据
data=range(-20,20)
A = list(map(lambda x:x**2, data))
B = list(map(lambda x:x**2+100, data))
C = list(map(lambda x:(x+5)**2, data))
#初始化窗口和图表
fig,ax = plt.subplots()
#绘制A,B,C三条线
ax.plot(data, A, label="y=x^2", color='r')
ax.plot(data, B, label="y=x^2+10", color='g')
ax.plot(data, C, label="y=(x+1)^2", color='b')
plt.legend(loc='best')
plt.show()

这样得到的结果是:

在不同的窗口绘制图表

接下来改讨论下在不同的窗口绘制图表了。有了之前的经验,我们很快能知道,这种方式的不同就是将折线画在不同的fig上。但要注意的是,在前一个窗口绘制结束时,需要调用plt.close(0),然后在最后调用plt.show()来显示所有图像。还是使用之前的数据为例,这次的绘制部分稍有不同:

#首次初始化,并绘制两条折线
fig,ax = plt.subplots()
ax.plot(data, A, label="y=x^2", color='r')
ax.plot(data, B, label="y=x^2+100", color='g')
#结束当前窗口绘制
plt.close(0)
#重新初始化窗口和图表,并再次绘制
fig,ax = plt.subplots()
ax.plot(data, C, label="y=(x+5)^2", color='b')
#结束绘制,显示所有
plt.show()

跑完程序得到了两个窗口: 可以看到窗口的标题上分别标记为Figure1和Figure2。如果在程序中不是用plt.close(0)而是直接使用plt.show(),第一个窗口会绘制完毕并弹出,然而整个进程会阻塞,直到你手动关闭Figure1,才会继续跑后面的内容。

在同一张图上绘制长度不同的几条折线

关于图表和窗口的问题讨论完了,但我还遇到了一个需求:如果几条折线的数据长度不同怎么办?例如统计年度数据,有的项目从2000年到2009年,有的却从2001到2007?之前我们绘制的数据长度都是一样的,plot()函数本身也要求x,y的长度一致,不然拒绝绘制。开始我采用将数据不足的部分补0的方法,但效果还是不完美,后来发现实际上这个问题的解决方案也很简单:只要在每个plot()函数中指定x的范围,让其和传入的y数据个数相同就可以了。

import matplotlib.pyplot as plt
A=[1,2,3,4,5]
B=[5,6,3,4,8,1,-2]
C=[0.1, 0.4, 10]
x=range(2001,2010)
fig,ax = plt.subplots()
ax.plot(x[0:len(A)], A, label="A", color='r')
ax.plot(x[0:len(B)], B, label="B", color='g')
ax.plot(x[0:len(C)], C, label="C", color='b')
plt.show()

于是,在图上可以看到长度不同的三条折线:

Demo

为了自己使用方便,我写了一个函数,用来在同一个图中绘制一组折线图。支持可变折线数目,支持不同数据长度,自动变化折线颜色,支持绘制多组图表,也可以作为这篇文章的一个相对完整的Demo。源码地址:Demo,自取。

举一反三

说了这么半天,图表、图表,光说折线图了,其他的图呢?其实其他几种图表的绘制本质上是一样的,完全可以自己举一反三。例如把plot()替换成别的函数:

ax1.plot(range(0,5), y, color='r')
ax2.scatter(range(0,5), y, color='k')
ax3.bar(range(0,5), y, color='b')
ax4.barh(range(0,5), y, color='y')

画出的图像如下图。同一组数据,几个不同的绘制函数分别画了折线图、散点图、柱状图、条状图四种样式。当然,他们的高级参数还是有些许不同的,在实际使用中根据需要再查细节即可。 <