Matplotlib绘图介绍
2020-06-23 / Yuhang Liu

本文章记录Matplotlib绘图的初步介绍和基本逻辑,在了解其背后的逻辑之后,每次绘图的时候才能知道如何下手。顺着工具的理念去使用,才能得心应手地实现心中所想即所得。

Matplotlib绘图逻辑

作为Python的一个绘图包,Matplotlib继承了Python的一切接对象的特点。绘图的过程,就可以看做是在绘图板上进行绘图的过程。当然,为了绘图的简单性和交互性,Matplotlib也提供了过程式或者命令式的函数编程。

Matplotlib绘图的逻辑基本上和自己绘画的流程是一样的。包括三个过程:

  • 找到一张纸或者一块画布;
  • 调色等一系列前戏;
  • 作画。

按照上述过程的模拟,Matplotlib有三个基本模块:

  • FigureCanvas
  • Renderer
  • Artist

三个模块分别实现上述的三个过程。其中,前两个模块主要涉及到和计算机底层的交互,暂时不用接触。我们绝大部分的工作,主要是在利用Artist接口。

Artist接口中包含两类对象:

  • 一类是类似于曲线Line2D、文字text、图像image等等基本元素。
  • 一类是容器对象,比如坐标系Axes、坐标轴Axis等等。

Matplotlib绘图方法

个人认为,Matplotlib主要有两种绘图逻辑:

  • 对象式绘图
  • 函数式绘图

其中,推荐使用对象式绘图方法,因为它可以更好地控制和自定义绘图。从Matplotlib官方文档中摘下来下图,显示了大部分的绘图元素:

绘图

本文章中主要使用Matplotlib的pyplot模块来实现绘图。

打开git bash,进入ipython界面,导入绘图模块:

1
>>> import matplotlib.pyplot as plt

构建空白画布

按照对象式绘图的思想,首先需要使用plt.figure()创建一个Figure对象(个人理解相当于空白画布),通过plt.show()显示出来:

1
2
>>> fig = plt.figure()
>>> fig.show()

这时,会显示出一个空的画布,如下图:

因为此时只是创建了一个画布,但是什么都没有画上去,所以肯定是一个空的。

另外,在创建Figure对象的时候,可以使用figsizedpi来控制图片的尺寸。比如:

1
fig = plt.figure(figsize=(16, 8), dpi=100)

figsize表示画布的宽和高;dpi表示每英寸的像素大小。因此,上面命令就会创建一个$1600\times{800}$的画布。

在空白画布上添加Axes对象

有了图像层,接下来就需要在图像层上来画画了,但是首先需要创建一个坐标轴,之后的绘图都以这个坐标轴为参考系来绘制。由此,我们调用Figure对象的add_axes方法:

1
2
3
>>> fig = plt.figure()
>>> ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
>>> plt.show()

其中,[0.1, 0.1, 0.8, 0.8]表示在图像层中,坐标轴区域(坐标层)距离画布坐标0.1倍的位置,距离下边0.1倍位置,坐标轴区域的整体宽度和高度占整体图像层0.8倍的大小,也就是说,距离右边和上边分别也是1-0.8-0.1=0.1​倍的大小。得到如下图:

可以看出,这里相当于在画布上加入了一个坐标系区域。

在Axes上绘制曲线

在画布上添加坐标系区域之后,现在就可以在该坐标系区域内进行绘制曲线了。比如,绘制sin函数曲线:

1
2
3
4
5
6
7
8
9
10
>>> import numpy as np
>>> import matplotlib.pyplot as plt
>>> x = np.linspace(0, 10, 100)
>>> y = np.sin(x)
>>> fig = plt.figure() # 创建画布
>>> ax1 = fig.add_axes([0.1, 0.1, 0.8, 0.8]) # 创建第一个坐标轴
>>> ax2 = fig.add_axes([0.2, 0.5, 0.4, 0.3]) # 创建第二个坐标轴
>>> ax1.plot(x, y, 'r')
>>> ax2.plot(x, y, 'g')
>>> plt.show()

在这里,相当于在画布Figure上创建了两个坐标系Axes区域,然后分别在这两个坐标系区域中进行绘图。得到如下图:

保存图片

上面的示例中都是plt.show()显示的是交互式的绘图界面,而不是输出的图片。如果要保存图片,还需要通过savefig()保存。可以使用plt.savefig()或者fig.savefig()方法来保存。Matplotlib可以生成多种格式的图像,包括PNGJPGPDF等等,只需要写好后缀名即可:

1
>>> fig.savefig('result.png')

小结

总的来说,按照以上方法绘图的步骤是:

  • 创建画布Figure
  • 在画布上添加坐标轴Axes
  • 在坐标轴上绘图

这种方法逻辑清晰,可以随心所欲地修改图片中的各种元素。

绘制子图AxesSubplot

在画布上还可以使用fig.add_subplot()方法添加子图对象AxesSubplot,这样,画布上能够绘制多个子图。 代码如下:

1
2
3
4
5
6
7
8
9
>>> import matplotlib.pyplot as plt
>>> import numpy as np
>>> x = np.arange(1,10,1)
>>> y = np.random.randn(len(x))
>>> fig = plt.figure() #产生一个画布
>>> ax = fig.add_subplot(111) #在画布上创建一个子图
>>> ax.plot(x,y) # 在子图上绘制
>>> ax.set_title("object oriented") #设置子图标题
>>> plt.show()

得到如下图:

可以看出,fig.add_subplot()方法其实就是起到了添加坐标层Axes的作用,类似于之前的fig.add_axes()

过程式的绘图

以前在网上查找资料的时候,经常会看见这种写法,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
x = np.linspace(0, 2, 100)

plt.plot(x, x, label='linear')
plt.plot(x, x**2, label='quadratic')
plt.plot(x, x**3, label='cubic')

plt.xlabel('x label')
plt.ylabel('y label')

plt.title("Simple Plot")

plt.legend()

plt.show()

官方说法是这个样子的:

Pyplot为底层面向对象的绘图库提供状态机接口。状态机隐式地自动创建图形和轴,以实现所需的图形。

也就是说,第一次调用plt.plot的时候,会自动创建FigureAxes对象以实现绘图。随后对plt.plot的调用会重新使用当前的Axes,设置标题、图例和轴标签同样还会自动使用当前Axes

总的来说,这种方式中,对象已经被隐式地掩盖了,但实质上,还是相当于在Axes上进行绘图。

参考

PermaLink:
https://lyuhang.github.io/2020/06/23/Matplotlib%E7%BB%98%E5%9B%BE/