导读
Matplotlib 是一个 Python 的 2D绘图库,它以各种硬拷贝格式和跨平台的交互式环境生成出版质量级别的图形。通过 Matplotlib,开发者可以仅需要几行代码,便可以生成绘图,直方图,功率谱,条形图,错误图,散点图等。
以下内容来自「Github」,为《PythonDataScienceHandbook[1]》(Python 数据科学手册[2])第四章「Matplotlib」介绍部分。全部内容都在以下环境演示通过:
numpy:1.18.5
pandas:1.0.5
matplotlib:3.2.1
对于图表来说,最简单的莫过于作出一个单一函数 的图像。本节中我们首先来介绍创建这种类型图表。本节和后续小节中,我们都会使用下面的代码将我们需要的包载入到 notebook 中:
- %matplotlib inline
- import matplotlib.pyplot as plt
- plt.style.use('seaborn-whitegrid')
- import numpy as np
对于所有的 Matplotlib 图表来说,我们都需要从创建图形和维度开始。图形和维度可以使用下面代码进行最简形式的创建:
- fig = plt.figure()
- ax = plt.axes()

在 Matplotlib 中,图形(类plt.Figure的一个实例)可以被认为是一个包括所有维度、图像、文本和标签对象的容器。维度(类plt.Axes的一个实例)就是你上面看到的图像,一个有边界的格子包括刻度和标签,最终还有我们画在上面的图表元素。在本书中,我们会使用变量名fig来指代图形对象,以及变量名ax来指代维度变量。
一旦我们创建了维度,我们可以使用ax.plot方法将数据绘制在图表上。下面是一个简单的正弦函数图形:
- fig = plt.figure()
- ax = plt.axes()
-
- x = np.linspace(0, 10, 1000)
- ax.plot(x, np.sin(x));

同样的,我们可以使用 pylab 接口(MATLAB 风格的接口)帮我们在后台自动创建这两个对象:
plt.plot(x, np.sin(x));

如果我们需要在同一幅图形中绘制多根线条,只需要多次调用plot函数即可:
- plt.plot(x, np.sin(x))
- plt.plot(x, np.cos(x));

这就是在 Matplotlib 中绘制简单函数图像的所有接口了。下面我们深入了解一下控制坐标轴和线条外观的细节。
你可能第一个想到需要进行调整的部分就是线条的颜色和风格。plt.plot()函数接受额外的参数可以用来指定它们。通过指定color关键字参数可以调整颜色,这个字符串类型参数基本上能用来代表任何你能想到的颜色。可以通过多种方式指定颜色参数:
所有 HTML 颜色名称可以在这里[3]找到。
- plt.plot(x, np.sin(x - 0), color='blue') # 通过颜色名称指定
- plt.plot(x, np.sin(x - 1), color='g') # 通过颜色简写名称指定(rgbcmyk)
- plt.plot(x, np.sin(x - 2), color='0.75') # 介于0-1之间的灰阶值
- plt.plot(x, np.sin(x - 3), color='#FFDD44') # 16进制的RRGGBB值
- plt.plot(x, np.sin(x - 4), color=(1.0,0.2,0.3)) # RGB元组的颜色值,每个值介于0-1
- plt.plot(x, np.sin(x - 5), color='chartreuse'); # 能支持所有HTML颜色名称值

如果没有指定颜色,Matplotlib 会在一组默认颜色值中循环使用来绘制每一条线条。
类似的,通过linestyle关键字参数可以指定线条的风格:
- plt.plot(x, x + 0, linestyle='solid')
- plt.plot(x, x + 1, linestyle='dashed')
- plt.plot(x, x + 2, linestyle='dashdot')
- plt.plot(x, x + 3, linestyle='dotted');
-
- # 还可以用形象的符号代表线条风格
- plt.plot(x, x + 4, linestyle='-') # 实线
- plt.plot(x, x + 5, linestyle='--') # 虚线
- plt.plot(x, x + 6, linestyle='-.') # 长短点虚线
- plt.plot(x, x + 7, linestyle=':'); # 点线

如果你喜欢更简洁的代码,这些linestyle和color参数能够合并成一个非关键字参数,传递给plt.plot()函数:
- plt.plot(x, x + 0, '-g') # 绿色实线
- plt.plot(x, x + 1, '--c') # 天青色虚线
- plt.plot(x, x + 2, '-.k') # 黑色长短点虚线
- plt.plot(x, x + 3, ':r'); # 红色点线

上面的单字母颜色码是 RGB 颜色系统以及 CMYK 颜色系统的缩写,被广泛应用在数字化图像的颜色系统中。
还有很多其他的关键字参数可以对折线图的外观进行精细调整;可以通过在 IPython 中使用帮助工具查看plt.plot()函数的文档来获得更多细节内容。
Matplotlib 会自动选择非常合适的坐标轴范围来绘制你的图像,但是有些情况下你也需要自己进行相关调整。使用plt.xlim()和plt.ylim()函数可以调整坐标轴的范围:
- plt.plot(x, np.sin(x))
-
- plt.xlim(-1, 11)
- plt.ylim(-1.5, 1.5);

如果某些情况下你希望将坐标轴反向,你可以通过上面的函数实现,将参数顺序颠倒即可:
- plt.plot(x, np.sin(x))
-
- plt.xlim(10, 0)
- plt.ylim(1.2, -1.2);

相关的函数还有plt.axis()(注意:这不是plt.axes()函数,函数名称是 i 而不是 e)。这个函数可以在一个函数调用中就完成 x 轴和 y 轴范围的设置,传递一个[xmin, xmax, ymin, ymax]的列表参数即可:
- plt.plot(x, np.sin(x))
- plt.axis([-1, 11, -1.5, 1.5]);

当然plt.axis()函数不仅能设置范围,还能像下面代码一样将坐标轴压缩到刚好足够绘制折线图像的大小:
- plt.plot(x, np.sin(x))
- plt.axis('tight');

还可以通过设置'equal'参数设置x轴与y轴使用相同的长度单位:
- plt.plot(x, np.sin(x))
- plt.axis('equal');

更多关于设置 axis 属性的内容请查阅plt.axis函数的文档字符串。
本节最后介绍一下在折线图上绘制标签:标题、坐标轴标签和简单的图例。
标题和坐标轴标签是最简单的这类标签,Matplotlib 提供了函数用来方便的设置它们:
- plt.plot(x, np.sin(x))
- plt.title("A Sine Curve")
- plt.xlabel("x")
- plt.ylabel("sin(x)");

这些标签的位置、大小和风格可以通过上面函数的可选参数进行设置。参阅 Matplotlib 在线文档和这些函数的文档字符串可以获得更多的信息。
当一幅图中绘制了多条折线时,如果能够绘制一个线条对应的图例能让图表更加清晰。Matplotlib 也内建了函数来快速创建图例。估计你也猜到了,通过plt.legend()函数可以实现这个需求。虽然有很多种正确的方法来指定图例,作者认为最简单的方法是通过在绘制每条线条时指定对应的label关键字参数来使用这个函数:
- plt.plot(x, np.sin(x), '-g', label='sin(x)')
- plt.plot(x, np.cos(x), ':b', label='cos(x)')
- plt.axis('equal')
-
- plt.legend();

上图可见,plt.legend()函数绘制的图例线条与图中的折线无论风格和颜色都保持一致。查阅plt.legend文档字符串可以获得更多相关信息;我们在[自定义图表图例]一节中也会讨论更高级的图例应用。
虽然大多数的plt函数都可以直接转换为ax的方法进行调用(例如plt.plot() → ax.plot(),plt.legend() → ax.legend()等),但是并不是所有的命令都能应用这种情况。特别是用于设置极值、标签和标题的函数都有一定的改变。下表列出了将 MATLAB 风格的函数转换为面向对象的方法的区别:
plt.xlabel() → ax.set_xlabel()
plt.ylabel() → ax.set_ylabel()
plt.xlim() → ax.set_xlim()
plt.ylim() → ax.set_ylim()
plt.title() → ax.set_title()
在面向对象接口中,与其逐个调用上面的方法来设置属性,更常见的使用ax.set()方法来一次性设置所有的属性:
- ax = plt.axes()
- ax.plot(x, np.sin(x))
- ax.set(xlim=(0, 10), ylim=(-2, 2),
- xlabel='x', ylabel='sin(x)',
- title='A Simple Plot');

另一种常用的图表类型是简单散点图,它是折线图的近亲。不像折线图,图中的点连接起来组成连线,散点图中的点都是独立分布的点状、圆圈或其他形状。本节开始我们也是首先将需要用到的图表工具和函数导入到 notebook 中:
- %matplotlib inline
- import matplotlib.pyplot as plt
- plt.style.use('seaborn-whitegrid')
- import numpy as np
plt.plot 绘制散点图在上一节中,我们介绍了plt.plot/ax.plot方法绘制折线图。这两个方法也可以同样用来绘制散点图:
- x = np.linspace(0, 10, 30)
- y = np.sin(x)
-
- plt.plot(x, y, 'o', color='black');

传递给函数的第三个参数是使用一个字符代表的图表绘制点的类型。就像你可以使用'-'或'--'来控制线条的风格那样,点的类型风格也可以使用短字符串代码来表示。所有可用的符号可以通过plt.plot文档或 Matplotlib 在线文档进行查阅。大多数的代码都是非常直观的,我们使用下面的例子可以展示那些最通用的符号:
- rng = np.random.RandomState(0)
- for marker in ['o', '.', ',', 'x', '+', 'v', '^', '<', '>', 's', 'd']:
- plt.plot(rng.rand(5), rng.rand(5), marker,
- label="marker='{0}'".format(marker))
- plt.legend(numpoints=1)
- plt.xlim(0, 1.8);

而且这些符号代码可以和线条、颜色代码一起使用,这会在折线图的基础上绘制出散点:
plt.plot(x, y, '-ok');

plt.plot还有很多额外的关键字参数用来指定广泛的线条和点的属性:
- plt.plot(x, y, '-p', color='gray',
- markersize=15, linewidth=4,
- markerfacecolor='white',
- markeredgecolor='gray',
- markeredgewidth=2)
- plt.ylim(-1.2, 1.2);

plt.plot函数的这种灵活性提供了很多的可视化选择。查阅plt.plot帮助文档获得完整的选项说明。
plt.scatter绘制散点图第二种更强大的绘制散点图的方法是使用plt.scatter函数,它的使用方法和plt.plot类似:
plt.scatter(x, y, marker='o');

plt.scatter和plt.plot的主要区别在于,plt.scatter可以针对每个点设置不同属性(大小、填充颜色、边缘颜色等),还可以通过数据集合对这些属性进行设置。
让我们通过一个随机值数据集绘制不同颜色和大小的散点图来说明。为了更好的查看重叠的结果,我们还使用了alpha关键字参数对点的透明度进行了调整:
- rng = np.random.RandomState(0)
- x = rng.randn(100)
- y = rng.randn(100)
- colors = rng.rand(100)
- sizes = 1000 * rng.rand(100)
-
- plt.scatter(x, y, c=colors, s=sizes, alpha=0.3,
- cmap='viridis')
- plt.colorbar(); # 显示颜色对比条

注意图表右边有一个颜色对比条(这里通过colormap()函数输出),图表中的点大小的单位是像素。使用这种方法,散点的颜色和大小都能用来展示数据信息,在希望展示多个维度数据集合的情况下很直观。
例如,当我们使用 Scikit-learn 中的鸢尾花数据集,里面的每个样本都是三种鸢尾花中的其中一种,并带有仔细测量的花瓣和花萼的尺寸数据:
- from sklearn.datasets import load_iris
- iris = load_iris()
- features = iris.data.T
-
- plt.scatter(features[0], features[1], alpha=0.2,
- s=100*features[3], c=iris.target, cmap='viridis')
- plt.xlabel(iris.feature_names[0])
- plt.ylabel(iris.feature_names[1]);

我们可以从上图中看出,可以通过散点图同时展示该数据集的四个不同维度:图中的(x, y)位置代表每个样本的花萼的长度和宽度,散点的大小代表每个样本的花瓣的宽度,而散点的颜色代表一种特定的鸢尾花类型。如上图的多种颜色和多种属性的散点图对于我们分析和展示数据集时都非常有帮助。
plot 和 scatter 对比:性能提醒除了上面说的plt.plot和plt.scatter对于每个散点不同属性的支持不同之外,还有别的因素影响对这两个函数的选择吗?对于小的数据集来说,两者并无差别,当数据集增长到几千个点时,plt.plot会明显比plt.scatter的性能要高。造成这个差异的原因是plt.scatter支持每个点使用不同的大小和颜色,因此渲染每个点时需要完成更多额外的工作。而plt.plot来说,每个点都是简单的复制另一个点产生,因此对于整个数据集来说,确定每个点的展示属性的工作仅需要进行一次即可。对于很大的数据集来说,这个差异会导致两者性能的巨大区别,因此,对于大数据集应该优先使用plt.plot函数。
对于任何的科学测量来说,精确计算误差与精确报告测量值基本上同等重要,如果不是更加重要的话。例如,设想我正在使用一些天文物理学观测值来估算哈勃常数,即本地观测的宇宙膨胀系数。我从一些文献中知道这个值大概是 71 (km/s)/Mpc,而我测量得到的值是 74 (km/s)/Mpc,。这两个值是否一致?在仅给定这些数据的情况下,这个问题的答案是,无法回答。
Mpc(百万秒差距)参见秒差距[4]
如果我们将信息增加一些,给出不确定性:最新的文献表示哈勃常数的值大约是 71 2.5 (km/s)/Mpc,我的测量值是 74 5 (km/s)/Mpc。这两个值是一致的吗?这就是一个可以准确回答的问题了。
在数据和结果的可视化中,有效地展示这些误差能使你的图表涵盖和提供更加完整的信息。
调用一个 Matplotlib 函数就能创建一个基础的误差条:
- %matplotlib inline
- import matplotlib.pyplot as plt
- plt.style.use('seaborn-whitegrid')
- import numpy as np
- x = np.linspace(0, 10, 50)
- dy = 0.8
- y = np.sin(x) + dy * np.random.randn(50)
-
- plt.errorbar(x, y, yerr=dy, fmt='.k');

这里的fmt参数是用来控制线条和点风格的代码,与plt.plot有着相同的语法,参见[简单的折线图]和[简单的散点图]。
除了上面的基本参数,errorbar函数还有很多参数可以用来精细调节图表输出。使用这些参数你可以很容易的个性化调整误差条的样式。作者发现通常将误差线条颜色调整为浅色会更加清晰,特别是在数据点比较密集的情况下:
plt.errorbar(x, y, yerr=dy, fmt='o', color='