浅析Python3绘图库matplotlib的使用

前言

import matplotlib.pyplot as plt

matplotlib.pyplot是一个有命令风格的函数集合,它看起来和Matlab很相似。每一个pyplot函数都使一副图像做出些许改变,例如创建一幅图,在图中创建一个绘图区域,在绘图区域中添加一条线等等。在matplotlib.pyplot中,各种状态通过函数调用保存起来,以便于可以随时跟踪像当前图像和绘图区域这样的东西。绘图函数是直接作用于当前axes(matplotlib中的专有名词,图形中组成部分,不是数学中的坐标系)。


一、Plot函数

先举一个简单的例子:

1
2
3
4
import matplotlib.pyplot as plt

plt.plot([1,2,3,4])
plt.show()

运行结果:

你可能会很疑惑X和Y轴为什么是0~3和1~4。原因是这样的,这里我们只是为plot()命令提供了一个list或者是array,matplotlib就会假设这个序列是Y轴上的取值,并且会自动为你生成X轴上的值。因为Python中的范围是从0开始的,因此X轴就是从0开始,长度与Y的长度相同,也就是[0,1,2,3]。

plot()是一个灵活的命令,它的参数可以是任意数量,比如:

1
plt.plot([1, 2, 3, 4], [1, 4, 9, 16])

这表示的是(x,y)对,(1,1)(2,4)(3,9)(4,16)。这里有第三个可选参数,它是字符串格式的,表示颜色和线的类型。该字符串格式中的字母和符号来自于Matlab,它是颜色字符串和线的类型字符串的组合。默认情况下,该字符串参数是 ‘b-‘,表示蓝色的实线。
举一个使用红色圆圈绘制上述点集的例子:
1
2
3
import matplotlib.pyplot as plt
plt.plot([1,2,3,4], [1,4,9,16], 'ro')
plt.show()

运行结果:

字符串参数表:

character description character color
‘-‘ solid line style ‘b’ blue
‘—‘ dashed line style ‘g’ green
‘-.’ dash-dot line style ‘r’ red
‘:’ dotted line style ‘c’ cyan
‘.’ point marker ‘m’ magenta
‘,’ pixel marker ‘y’ yellow
‘o’ circle marker ‘k’ black
‘v’ triangle_down marker ‘w’ white
‘^’ triangle_up marker
‘<’ triangle_left marker
‘>’ triangle_right marker
‘1’ tri_down marker
‘2’ tri_up marker
‘3’ tri_left marker
‘4’ tri_right marker
‘s’ square marker
‘p’ pentagon marker
‘*’ star marker
‘h’ hexagon1 marker
‘H’ hexagon2 marker
‘+’ plus marker
‘x’ x marker
‘D’ diamond marker
‘d’ thin_diamond marker
‘丨’ vline marker
‘_’ hline marker

如果matplotlib仅限于使用上面那种list,那么它将显得毫无用处。通常,我们都是使用numpy数组,实际上,所有的序列都将被在内部被转化成numpy数字。下面的例子是使用一个命令用几种不同风格的线绘制一个数组:

1
2
3
4
5
6
7
8
9
import numpy as np
import matplotlib.pyplot as plt

# 0到5之间每隔0.2取一个数
t = np.arange(0,5,0.2)

# 红色的破折号,蓝色的方块,绿色的三角形
plt.plot(t, t, 'r--', t, t**2, 'bs', t, t**3, 'g^')
plt.show()

运行结果:


1.1 xlabel()、ylabel()、title()

  • xlabel()     为X轴添加注解
  • label()       为Y轴添加注解
  • title()        为图像添加标题

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
# -*- coding:utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties #字体管理器

font = FontProperties(fname='C:\\QzmVc1\\STZHONGS.TTF',size=15)
x = np.linspace(0,20,100)
plt.plot(x,np.sin(x),'b-')
plt.xlabel('X轴',fontproperties=font)
plt.ylabel('Y轴',fontproperties=font)
plt.legend(['f(x)=sin(x)'],prop=font,loc='best')
plt.title('f(x) = sin(x)')
plt.show()

运行结果:

注:因为这里注解是中文的话会产生乱码问题,我们通过添加字体管理器来解决。

1.2 legend()、axis()、grid()

  • legend()     为图像添加图例
  • axis()          确定坐标轴的范围
  • grid()          图像网格的显示

例如:

1
2
3
4
5
6
7
8
9
10
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0,10,5,endpoint=False)
plt.plot(x,x**2,linewidth=1.5,linestyle='-',color='red',marker='x')
plt.plot(x,x**3,linewidth=1.5,linestyle='-',color='green',marker='^')
plt.legend(['x^2','x^3'],loc = 'best') #对好位置
plt.axis([0,10,0,600])
plt.grid(True)
plt.show()

运行结果:

注:关于legend()的loc参数

序号 位置
0 ‘best’
1 ‘upper right’
2 ‘upper left’
3 ‘lower left’
4 ‘lower right’
5 ‘right’
6 ‘center left’
7 ‘center right’
8 ‘lower center’
9 ‘upper center’
10 ‘center’

1.3 xlim()、ylim()、xticks()、yticks()

  • xlim()、ylim()      设置坐标轴刻度取值范围
  • xticks()、yticks()    设置XY轴的刻度标签值

例如:

1
2
3
4
5
6
import matplotlib.pyplot as plt
import numpy as np

plt.xlim(-1,4)
plt.ylim(-2,5)
plt.show()

运行结果:

1
2
3
4
5
6
import numpy as np
import matplotlib.pyplot as plt

plt.xticks([0,2,3,6,8],[r'two',r'four',r'six',r'8',r'10'])
plt.yticks([-1.0,0.0,1.0,2.0,3.0,4.0],[r'bottom',r'0.0',r'1.0',r'2.0',r'3.0',r'4.0'])
plt.show()

运行结果:

1.4 text()、annotate()

  • text()     设置图片任何位置上的文字描述和注解
  • annotate()     对图片上某个点加注解

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import numpy as np
import matplotlib.pyplot as plt

mu,sigma = 100,15
x = mu + sigma * np.random.randn(10000)
# 直方图
plt.hist(x,50,normed=1,facecolor='g',alpha=0.75)

plt.xlabel('Smarts')
plt.ylabel('Probability')
plt.title('Histogram of IQ')
plt.text(60,0.025,r'$\mu=100,\sigma=15$') #TeX表达式 &...&
plt.axis([40,160,0,0.03])
plt.grid(True)
plt.show()

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import numpy as np
import matplotlib.pyplot as plt

t = np.arange(0.0, 5.0, 0.01)
s = np.cos(2*np.pi*t)
plt.plot(t,s,linewidth=2)

#  xy -- 为注释点的坐标
#  xytext -- 为注解内容位置坐标,当该值为None时,注解内容放置在xy处
#  arrowprops -- 用于设置箭头的形状,类型为字典类型
plt.annotate('local max',xy=(2,1),xytext=(3,1.5),
arrowprops=dict(facecolor='black',shrink=0.05),)

plt.ylim(-2,2)
plt.show()

运行结果:


二、图表和子图

Matlab和pyplot都有当前图形(figure)和当前坐标系(axes)的概念。所有的绘图命令都是应用于当前坐标系的。gca()和gcf()(get current axes/figures)分别获取当前axes和figures的对象。通常,你不用担心这些,因为他们都在幕后被保存了,下面是一个例子,创建了两个子绘图区域(subplot):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import numpy as np
import matplotlib.pyplot as plt

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("2subplot")
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()

运行结果:

figure()命令在这儿可以不写,因为figure(1)将会被默认执行,同样,subplot(111)也是默认被执行的。subplot()中的参数分别指定了numrows、numcols、fignum,其中fignum的取值范围为1到numrows*numcols,分别表示的是将绘图区域划分为numrows行和numcols列个子绘图区域,fignum为当前子图的编号。编号是从1开始,一行一行由左向右编号的。其实subplot中的参数【111】本应写作【1,1,1】,但是如果这三个参数都小于10(其实就是第三个参数小于10)就可以省略逗号。你可以创建任意数量的子图(subplots)和坐标系(axes)。如果你想手动放置一个axes,也就是它不再是一个矩形方格,你就可以使用命令axes(),它可以让坐标系位于任何位置,axes([left,bottom,width,height]),其中所有的值都是0到1(axes([0.3,0.4,0.2,0.3])表示的是该坐标系位于figure的(0.3,0.4)处,其宽度和长度分别为figure横坐标和纵坐标总长的0.2和0.3)。其实subplot和axes的区别就在于axes大小和位置更加随意。
你可以创建多个figure,通过调用figure(),其参数为figure的编号。当然每个figure可以包含多个subplot或者是多个axes。例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import matplotlib.pyplot as plt
plt.figure(1) # 编号为1的figure
plt.subplot(211) # figure1中的第一个子图
plt.plot([1, 2, 3])
plt.subplot(212) # figure1中的第二个子图
plt.plot([4, 5, 6])


plt.figure(2) # figure2
plt.plot([4, 5, 6]) # 默认使用subplot(111),此时figure2为当前figure

plt.figure(1) # 设置figure1为当前figure;
# 但是subplot(212)为当前子图
plt.subplot(211) # 使subplot(211)为当前子图
plt.title('Easy as 1, 2, 3') # 对subplot(211)命名

我们可以使用clf()和cla()(clear current figure/axes)清除当前figure和当前axes。
如果你创建了许多figures,你需要注意一件事:figure的内存直到显示调用close()函数才会被完全释放,否则它并没有被全部释放。如果只是删掉对figure的引用,或者是通过关闭window进程管理器关闭该figure,这都是不完全删除figure的,因为pyplot在内部维持了一个引用,直到close()被调用。

2.1 sca()函数

sca()用来选择子图,例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

import numpy as np
import matplotlib.pyplot as plt

plt.figure(1,dpi=50) # dpi为图表的大小,默认为 80
ax1 = plt.subplot(211) #创建子图 ax1
ax2 = plt.subplot(212) #创建子图 ax2

x = np.linspace(0,10,100) # x轴定义域

plt.sca(ax1) #选择子图 ax1
plt.plot(x,np.exp(x)) #在子图 ax1中绘制函数 exp(x)

plt.sca(ax2) #选择子图 ax2
plt.plot(x,np.sin(x)) #在子图 ax2中绘制函数 sin(x)

plt.show() #展示所有图表


三、直方图hist()

1
2
3
4
5
6
7
8
import numpy as np
import matplotlib.pyplot as plt

plt.figure(1) # 创建图表1
data = [1,1,1,2,2,2,3,3,4,5,5,4,6]
plt.hist(data) # 只要传入数据,直方图就会统计数据出现的次数

plt.show()

运行结果:


四、散点图scatter()

1
2
3
4
5
6
7
import numpy as np
import matplotlib.pyplot as plt

#产生测试数据
x = y = np.arange(1,10)
plt.scatter(x,y,color='r',marker='o')
plt.show()

运行结果:


五、饼图pie()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import numpy as np
import matplotlib.pyplot as plt
data = [100,500,300] #饼图中的数据
plt.pie(data, # 每个饼块的实际数据,如果大于1,会进行归一化,计算percentage
explode=[0.0,0.0,0.1], # 每个饼块离中心的距离
colors=['y','r','g'], # 每个饼块的颜色,黄红绿
labels=['A part','B part','C part'], # 每个饼块的标签
labeldistance=1.2, # 每个饼块标签到中心的距离
autopct='%0.1f%%', # 百分比的显示格式
pctdistance=0.4, # 百分比到中心的距离
shadow=True, # 每个饼块是否显示阴影
startangle=0, # 默认从x轴正半轴逆时针起
radius=1 # 饼块的半径
)
plt.show()

运行结果:


六、图片保存savefig()

问题

当使用如下代码使用plt.savefig()保存生成的图片时,结果打开生成的图片却是一片空白。

1
2
3
4
5
6
import matplotlib.pyplot as plt

""" 一些画图代码 """

plt.show()
plt.savefig("filename.png")

原因

其实产生这个现象的原因很简单:在plt.show()后调用了plt.savefig(),在plt.show()后实际上已经创建了一个新的空白的图片,这时候你再plt.savefig()就会保存这个新生成的空白图片。

解决

知道了原因,就不难知道解决办法了,解决办法有两种:

1.在plt.show()之前调用plt.savefig()

1
2
3
4
5
6
>import matplotlib.pyplot as plt

>""" 一些画图代码 """

>plt.savefig("filename.png")
>plt.show()

2.画图的时候获取当前图像(这一点非常类似于Matlab的句柄的概念)

1
2
3
4
># gcf: Get Current Figure
fig = plt.gcf()
plt.show()
fig.savefig('filename.png', dpi=100)