Matplotlib 快速入门

由于Matplotlib考虑到Matlab用户,提供了函数接口作图,但是这种方法是隐式创建对象的,以至于你想在哪张图的哪个子图上面画什么东西不是那么得心应手,
于是,这里介绍一下基于对象的作图方式,显示创建对象,作图逻辑更加清晰,作图更加灵活。

Jupyter Notebook中设置绘图GUI后端(Backends)

  • 方法一:import matplotlib;matplotlib.use(‘TkAgg’)

    • 注意事项:必须放在import matplotlib.pyplot as plt前面,否则因为执行顺序问题,

      调用 use() 将覆盖 matplotlibrc 中的设置,会导致一些意想不到的异常,比如绘图窗口会卡死

  • 方法二:%matplotlib是窗口显示,简单好用, 参考说明,%matplotlib notebook是行内显示

  • 两种执行模式简介

    • 交互式模式:plt.ion(),不用关闭窗口,继续执行后面的操作,这是notebook的默认执行模式。配合

      plt.show(),plt.pause()可以实现同时显示多个窗口,程序不会阻塞

    • 阻塞模式:plt.ioff(),窗口会阻塞后面代码的执行,需要关闭窗口来继续执行(在notebook中设置似乎不起作用,需要用plt.show(block=True))

1
2
3
4
5
6
7
import numpy as np
import matplotlib
from PIL import Image
import matplotlib.pyplot as plt
%matplotlib
print(matplotlib.get_backend()) #查看当前GUI后端
print(matplotlib.is_interactive()) #查看是否使用了交互式模式
Using matplotlib backend: TkAgg
TkAgg
True

Matplotlib中对象层次

  • Matplotlib图像中最重要的三个对象分别是figure(画布),ax(坐标系),axis (坐标轴),它们有一个共同的基类:artist

  • 一个figure容器中可以有多个 axes(多个子图)

  • 一个axes中有多个axis(轴),title(标题),data(作图区的对象)

  • axis又有label,tick等对象,可以设置坐标轴刻度,坐标轴标签,坐标轴标题等

使用面向对象接口时,正确的作图流程应该是:

1.创建 Figure 实例

2.在 Figure 上创建 Axes

3.在 Axes 上添加基础类对象

或者简化为:

1.创建 Figure 对象和 Axes 对象;

2.为每个容器类元素添加基础类元素。

再浓缩成指导思想就是:

1.先找对象

2.再解决问题

打个比方,画图时你需要拿出一张素描纸,这就是创建画布,然后在这张纸上你想画碧海、蓝天、凉亭,你肯定事先会设计在哪个地方画这些内容,你决定的每个元素的放置位置就是在划分子图,而你决定好在哪画之后其实等价于在该子图位置创建了坐标系。后面就容易理解了,你的绘图等价于在子图的坐标系上绘图、添加图例、添加标题等。最后,对于坐标轴的样式(是否显示、刻度范围、刻度标签等),可以通过坐标系对象的方法,得到坐标轴对象,通过坐标轴对象的方法设置。

Chapter A:面向对象绘图(2张画布)

Step1:建立画布

1
2
3
4
5

fig1=plt.figure(figsize=(5,3))
fig2=plt.figure(figsize=(8,4))
# 如果是交互式模式,会直接调用show显示画布

Step2:建立子图

1
2
3
4
5
6
7
8
9
10
11
#清空两个画布,以免每次运行都会覆盖画一堆东西
fig1.clear()
fig2.clear()

#创建两个子图,存在两个列表当中
axes1=fig1.subplots(nrows=2,ncols=2)
axes2=fig2.subplots(nrows=2,ncols=1)

#显示此时的画布
fig1.show()
fig2.show()

Step3:选中子图,并在上面添加元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#选择坐标轴系
ax1=axes1[0][0]
ax2=axes1[1][1]
ax3=axes2[1]


#作图:在data区添加线元素
x = np.linspace(0, 2, 100)
ax1.plot(x, x, label='linear')
ax2.plot(x, x**2, label='quadratic')
ax3.plot(x, x**3, label='cubic')
fig1.show()
fig2.show()

#设置坐标轴,标题,图标,网格
ax1.set_xlabel('x label')
ax1.set_ylabel('y label')
ax1.set_title("linear curve")
ax1.legend()
ax1.grid(color='b', linestyle=':', linewidth=1)

ax2.set_xlabel('x label')
ax2.set_ylabel('y label')
ax2.set_title("quadratic curve")
ax2.legend()


ax3.set_xlabel('x label')
ax3.set_ylabel('y label')
ax3.set_title("cubic curve")
ax3.legend()

fig1.show()
fig2.show()

Step4:对图像作最后微调

更多的作图细节,以及其他元素的设置细节,请参考官网文档或其他网络教程

1
2
3
4
5
6
#调整子图间距,使其不重叠
fig1.tight_layout()
fig2.tight_layout()
fig1.show()
fig2.show()

1
2
3
4
5
6
7

#调整图例大小,位置
ax1.legend(loc='lower right',fontsize='5')
ax2.legend(loc='upper left',fontsize='5')
ax3.legend(loc='best',fontsize='10')
fig1.show()
fig2.show()

Chapter B:面向对象绘图(1张画布)

1
2
3
4
5
6
7
8
9
#创建画布
fig3=plt.figure()

#添加子图
ax4=fig3.add_subplot(221)
ax5=fig3.add_subplot(223)
ax6=fig3.add_subplot(122)

#后面的作图过程完全一样,这里略

1
2
3
#快速构建画布,与子图
fig4,axes3=plt.subplots(1,2) #subplots返回一个元组,第一个是画布,第二个是子图列表
fig4.show()

Chapter C:面向过程绘图(2张画布)

利用pyplot接口提供的函数,直接隐式创建对象,并在当前对象上进行操作

  • plt.figure(编号),自动转向数字编号的画布,如果没有,创建并转向;

    注意,之前的绘图,每一张画图会自动按顺序给一个数字编号,这里如果新增画布,那么数字应该接着前面的编号

  • plt.subplot(nrow,ncol,位置编号),划分画布区域,转向指定编号的轴域(子图)
  • plt.plot(),作图函数
1
2
3
4
5
6
7
8
9
10
11
import matplotlib.pyplot as plt
plt.figure(5) # 第一个图形
plt.subplot(211) # 第一个图形的第一个子图
plt.plot([1, 2, 3])
plt.subplot(212) # 第一个图形的第二个子图
plt.plot([4, 5, 6])
plt.figure(6) # 第二个图形
plt.plot([4, 5, 6]) # 默认创建 subplot(111)
plt.figure(5) # 当前是图形 1,subplot(212)
plt.subplot(211) # 将第一个图形的 subplot(211) 设为当前子图
plt.title('Easy as 1, 2, 3') # 子图 211 的标题
Text(0.5, 1.0, 'Easy as 1, 2, 3')

Chapter D:交互式绘制动态图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

def simple_plot():

# 生成画布
fig=plt.figure(figsize=(8, 6), dpi=80)

#建立一个空列表,用来存每一帧图片
frames=[]

# 循环,并且走出循环后,画布依旧还在,没有回收
for index in range(30):
path_png='./frame/frame{}.png'.format(index)
# 清除原有图像
plt.cla()

# 设定标题等
plt.title("dynamic")
plt.grid(True)

# 生成测试数据
x = np.linspace(-np.pi + 0.1*index, np.pi+0.1*index, 256, endpoint=True)
y_cos, y_sin = np.cos(x), np.sin(x)

# 设置X轴
plt.xlabel("X")
plt.xlim(-4 + 0.1*index, 4 + 0.1*index)
plt.xticks(np.linspace(-4 + 0.1*index, 4+0.1*index, 9, endpoint=True))

# 设置Y轴
plt.ylabel("Y")
plt.ylim(-1.0, 1.0)
plt.yticks(np.linspace(-1, 1, 9, endpoint=True))

# 画两条曲线
# 由于在循环中,会不停在同一张画布上作图,开头使用cla函数清空画布,以免全都画在上面了
plt.plot(x, y_cos, "b--", linewidth=2.0, label="cos")
plt.plot(x, y_sin, "g-", linewidth=2.0, label="sin")

# 设置图例位置,loc可以为[upper, lower, left, right, center]
plt.legend(loc="upper left", shadow=True)

# 图形显示
plt.show()

# 获取当前图片,用PIL打开,存入列表
frame=plt.gcf()
frame.savefig(path_png)
frame = Image.open(path_png)
frames.append(frame)

# 暂停
plt.pause(0.2)

#将列表中的所有图片制作为gif动态图,frames[0]是一个image对象,可以调用save方法
frames[0].save('./plot/plot15.gif',save_all=True,append_images=frames[1:],loop=0,disposal=2)
print('done!')


simple_plot()

c:\Users\Amadeus\AppData\Local\Programs\Python\Python39\lib\site-packages\PIL\Image.py:945: UserWarning: Palette images with Transparency expressed in bytes should be converted to RGBA images
  warnings.warn(


done!

存储所有画布

1
2
3
4
5
6
7
8
saves_path='./saves/'
fig1.savefig(saves_path+'plot_fig1.png') #chapterA的图1
fig2.savefig(saves_path+'plot_fig2.png') #chapterA的图2
fig3.savefig(saves_path+'plot_fig3.png') #chapterB的图1
fig4.savefig(saves_path+'plot_fig4.png') #chapterB的图2
plt.figure(5).savefig(saves_path+'plot_fig5.png') #chapterC的图1
plt.figure(6).savefig(saves_path+'plot_fig6.png') #chapterC的图2
plt.figure(7).savefig(saves_path+'plot_fig7.png') #制作动态图最后一次循环出来后的画布

Chapter E:具体绘制各种图,可视化教程

参考教程1

https://www.bookstack.cn/read/MatplotlibUserGuide/3.1.md

参考教程2

https://github.com/rougier/scientific-visualization-book