初识绘图

本章对基本的 Matplotlib 绘图进行介绍,包括点型、线型与多图绘制。

本章加载的库如下所示:

[1]:
import os
import numpy as np
from matplotlib import pyplot as plt
import matplotlib as mpl

以上加载的库中,mpl 一般是不用加载的。本章由于需要介绍全局参数,故需要加载。在使用 Jupyter Notebook 时,我们往往还需要指定图像的输出尺寸,以获得较好的视觉效果:

[2]:
plt.rcParams['figure.figsize'] = [10, 6]

在本章最后更新时,使用的 Matplotlib 版本是:

[3]:
mpl.__version__
[3]:
'3.0.2'

基础的 plt.plot()

本小节仅介绍简单的绘制命令,包括线型与颜色。关于添加标题、图例、文字等内容,请参阅后续章节。

Matplotlib 的大部分绘图命令都是通过 plt 下的功能实现的,而最简单的函数就是 plt.plot()。比如一个二次函数图像:

[4]:
x = np.linspace(0, 2, 100)
y1 = x ** 2

plt.plot(x, y1)
plt.show()  # 记得在最后用 plt.show() 输出图像
_images/BasicPlot_7_0.png

从上面也能看出,我们使用 plt.show() 函数来输出图像。

我们也可以在一幅图上绘制多个函数,它们会自动以不同的颜色绘制:

[5]:
y2 = 2 + 2 * np.sin(np.pi * x)

plt.plot(x, y1)
plt.plot(x, y2)
plt.show()
_images/BasicPlot_9_0.png

也就是说,“叠加绘制”默认是开启的——如果有读者使用过 MatLab,那么恭喜你,在 Matplotlib 这里不需要再惦记着什么 hold on 命令了。

如果我们不需要叠加绘制,我们可以随时插入 plt.close() 命令,来关闭当前绘制。或者使用 plt.close('all') 来清除所有绘制。

点型与线型

在有多个曲线时,我们往往通过更改点型或线型来区分它们。尤其是需要黑白打印的场合,线的颜色并不是有效的方案。下面是一个极端的例子:

[6]:
plt.plot(x, y1, color="b", linestyle=":", linewidth=3, dashes=(2,1),  # 线的属性
         marker="d", ms=10, mew=1.5, mec="g", mfc="w",  # 点的属性
         markevery=10)
plt.plot(x, y2, "#bf242a", ls="-", lw=1)
plt.show()
_images/BasicPlot_12_0.png

以上的参数(括号中是参数的缩写)的含义分别是:

  • color:线的颜色(详细请参考“颜色”一节)。基础的有红r,绿g,蓝b,青c,洋红m,黄y,黑k,白w。以及其他的表述:

    • HTML 码:例如 "#66ccff"

    • 标准化后的 RGB 元组:例如 (1, 0, 0)

    • 灰度字串:"0.5"

  • linestyle (ls):线型。实线”-“,虚线”–“,点划线”-.”,点线”:“。

  • dashes: 虚线比例。传入元组 (a,b),那么划线长与间隔长之比为 a/b。

  • linewidth (lw):线宽。

  • marker:点样式。实心”.”,像素”,“,圆点”o”,方块”s”,上折/下折箭头”v”/“^”,左折/右折箭头”<“/”>“,五边形”p”,六边形”h”,星”*“,加号”+“,叉号”x”,(瘦)钻石”d/D”,竖线”|“,横线”_“。

    • markevery:每几个点才绘制一个点。

    • markersize (ms):点大小。

    • markeredgewidth (mew):点边缘线宽。

    • markeredgecolor (mec):点边缘颜色。默认与线同色。

    • markerfacecolor (mfc):点填充颜色。默认与线同色。

颜色

Matplotlib 的颜色使用是个复杂的话题。通常来说,直接调用的颜色包括:

  • 标准化的 RGB:以三元元组的形式,比如 (0, 0, 1) 代表 (0, 0, 255),即纯蓝。

  • HTML 16进制颜色码:以字符串的形式,比如 "#0F0F0F"

  • 标准化的灰度值:以字符串形式,比如 0.5

  • RGB 与 CMYK 标准色字符:以单个字符形式,有:r,g,b,c,m,y,k,w 八种,其中 w 是白色。

  • X11/CSS4 标准的颜色名:参考下面的图像。

  • XKCD 颜色调查(参考此页面):例如”xksd:sky blue”。

以及 matplotlib 采用的色环:

  • matplotlib 默认的十色环:“C0”, “C1”, …,“C9”。这是 matplotlib 绘图默认依次使用的颜色。

  • 十色环的另一种形式:‘tab:blue’, ‘tab:orange’, ‘tab:green’, ‘tab:red’, ‘tab:purple’, ‘tab:brown’, ‘tab:pink’, ‘tab:gray’, ‘tab:olive’, ‘tab:cyan’。

以上字符串(除16进制码外)均是大小写敏感的。

下图是一个 X11 颜色表。该颜色表是我用 Matplotlib 生成的,不过为了排版简洁,我把这个代码块在生成网站时隐藏了;感兴趣的读者可以访问本章对应的 ipynb 文件进行查看。

_images/X11.svg

子图绘制 plt.subplots()

需要将多张子图绘制到一张图内时,使用 plt.subplots() 函数。

语句 fig, axarr = plt.subplots(...) 可以设置一个 nrows 行、ncols 列个子图组成的大图:

  • 参数:

    • nrows=1, ncols=1: 子图矩阵的行数与列数。默认一行一列。

    • sharex/sharey=False:共享 x/y 轴。True 是所有子图共享,'row' 是每行共享,'col' 是每列共享。

  • 返回值:

    • fig :所有子图组成的总图像,可以用于保存为图像文件。

    • axarr :一个 \(nrows\times ncols\) 的矩阵,其第 \(j\) 个元素即是第 \(j\) 个子图。

下面是一个例子,展示了绘制 2 个图(两行一列)的情况:

[8]:
x = np.linspace(0, 4 * np.pi, 100)
y = [np.sin(x), np.sin(2*x)]

plt.close('all')
f, (ax1, ax2) = plt.subplots(2)
ax1.plot(x, y[0])
ax2.plot(x, y[1])
plt.show()
_images/BasicPlot_18_0.png

对于多行多列的情况,可以利用 for 循环,并使用 axarr.ravel() 来展平矩阵。下列是一个两行两列的 4 子图例子,展示了 sharex/sharey 参数的使用:

[9]:
x = [np.linspace(0, k*2*np.pi, 100) for k in (1, 2)]
y = [
    np.sin(x[0]), 2*np.cos(x[0]),
    np.sin(2*x[1]), np.cos(2*x[1])
]

plt.close('all')
# 所有子图 x 轴共享,每一行内的子图 y 轴共享
f, axarr = plt.subplots(2, 2, sharex=True, sharey='row')
for i, ax in enumerate(axarr.ravel()):
    ax.plot(x[i//2], y[i])
plt.show()
_images/BasicPlot_20_0.png

由于子图操作还有很多细节,我将在后文中单独开启章节进行讨论。

保存绘图 f.savefig()

上述多图绘制的例子中,返回的 f, axarr 中的 f.savefig(fname, ...) 即可帮助我们保存图像到文件 fname。该函数的常用参数包括:

  • transparent=False:是否使用透明背景。

  • format:如果文件名 fname 中不包含扩展名,那么可以通过该参数指定。例如 jpg/jpeg, png, pdf, svg

  • bbox_inches:设置图像的 bounding box 数值。当设置为 'tight' 时会自动设置,以达到“减少图像外侧白边”的效果。题外话:如果仅是为了达到减少白边的效果,也可以尝试在 savefig() 之前使用 f.tight_layout() 进行处理。

下面是一个保存图片的例子:

[10]:
x = np.linspace(0, 2*np.pi, 100)
y = np.sin(x)

f, ax = plt.subplots()
ax.plot(x, y)

fig_file = os.path.join("pics", "saved_fig.png")  # 保存在 pics/saved_fig.png
f.savefig(fig_file, transparent=True, bbox_inches='tight')
_images/BasicPlot_23_0.png