Matplotlib 填充两条线之间的区域

Beginner

This tutorial is from open-source community. Access the source code

简介

Matplotlib 是一种用于 Python 编程语言的数据可视化库。它用于在 Python 中创建静态、动画和交互式可视化。在本实验中,你将学习如何使用 Matplotlib 的 fill_between 函数来填充两条线之间的区域。

虚拟机使用提示

虚拟机启动完成后,点击左上角切换到“笔记本”标签,以访问 Jupyter Notebook 进行练习。

有时,你可能需要等待几秒钟让 Jupyter Notebook 完成加载。由于 Jupyter Notebook 的限制,操作验证无法自动化。

如果你在学习过程中遇到问题,请随时向 Labby 提问。课程结束后提供反馈,我们将立即为你解决问题。

基本用法

fill_between 函数可用于填充两条线之间的区域。参数 y1y2 可以是标量,表示在给定 y 值处的水平边界。如果只给出 y1,则 y2 默认值为 0。

x = np.arange(0.0, 2, 0.01)
y1 = np.sin(2 * np.pi * x)
y2 = 0.8 * np.sin(4 * np.pi * x)

fig, (ax1, ax2, ax3) = plt.subplots(3, 1, sharex=True, figsize=(6, 6))

ax1.fill_between(x, y1)
ax1.set_title('fill between y1 and 0')

ax2.fill_between(x, y1, 1)
ax2.set_title('fill between y1 and 1')

ax3.fill_between(x, y1, y2)
ax3.set_title('fill between y1 and y2')
ax3.set_xlabel('x')
fig.tight_layout()

fill_between 函数的第一个示例填充了曲线 y1 与 x 轴(即 y = 0)之间的区域。第二个示例填充了曲线 y1 与水平直线 y = 1 之间的区域。第三个示例填充了曲线 y1 与曲线 y2 之间的区域。

每个子图都有一个标题,第三个子图还设置了 x 轴标签。fig.tight_layout() 用于自动调整子图布局,以避免标签重叠。

置信带

fill_between 的一个常见应用是表示置信带。fill_between 使用颜色循环中的颜色作为填充颜色。因此,通过使用 alpha 使区域半透明来淡化颜色通常是个好做法。

N = 21
x = np.linspace(0, 10, 11)
y = [3.9, 4.4, 10.8, 10.3, 11.2, 13.1, 14.1,  9.9, 13.9, 15.1, 12.5]

## 拟合一条线性曲线并估计其 y 值及其误差。
a, b = np.polyfit(x, y, deg=1)
y_est = a * x + b
y_err = x.std() * np.sqrt(1/len(x) +
                          (x - x.mean())**2 / np.sum((x - x.mean())**2))

fig, ax = plt.subplots()
ax.plot(x, y_est, '-')
ax.fill_between(x, y_est - y_err, y_est + y_err, alpha=0.2)
ax.plot(x, y, 'o', color='tab:brown')

在这个示例中:

  1. 我们首先定义了数据点的数量 N,以及 xy 数组。
  2. 使用 np.polyfit 拟合了一条一次多项式曲线(线性曲线),得到系数 ab,并计算出估计的 yy_est
  3. 计算了估计值的误差 y_err
  4. 创建了一个图形和轴对象,绘制了拟合曲线 y_est
  5. 使用 fill_between 填充了拟合曲线上下误差范围内的区域,设置 alpha=0.2 使填充区域半透明。
  6. 最后绘制了原始数据点(用棕色圆点表示)。

选择性填充水平区域

参数 where 允许指定要填充的 x 范围。它是一个与 x 大小相同的布尔数组。只有连续 True 序列的 x 范围会被填充。因此,相邻 TrueFalse 值之间的范围永远不会被填充。所以,除非数据点的 x 距离足够精细以至于上述效果不明显,否则建议设置 interpolate=True

x = np.array([0, 1, 2, 3])
y1 = np.array([0.8, 0.8, 0.2, 0.2])
y2 = np.array([0, 0, 1, 1])

fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True)

ax1.set_title('interpolation=False')
ax1.plot(x, y1, 'o--')
ax1.plot(x, y2, 'o--')
ax1.fill_between(x, y1, y2, where=(y1 > y2), color='C0', alpha=0.3)
ax1.fill_between(x, y1, y2, where=(y1 < y2), color='C1', alpha=0.3)

ax2.set_title('interpolation=True')
ax2.plot(x, y1, 'o--')
ax2.plot(x, y2, 'o--')
ax2.fill_between(x, y1, y2, where=(y1 > y2), color='C0', alpha=0.3,
                 interpolate=True)
ax2.fill_between(x, y1, y2, where=(y1 <= y2), color='C1', alpha=0.3,
                 interpolate=True)
fig.tight_layout()

在上述代码中:

第一个子图(ax1)设置了 interpolation=False。它绘制了两条线 y1y2,然后根据 where=(y1 > y2)where=(y1 < y2) 条件分别填充了相应区域。

第二个子图(ax2)设置了 interpolation=True。同样绘制了两条线 y1y2,并根据相同的条件填充区域,但由于设置了 interpolate=True,填充效果会有所不同,相邻 TrueFalse 值之间的区域会被更平滑地处理。最后通过 fig.tight_layout() 调整图形布局。

选择性标记整个坐标轴上的水平区域

相同的选择机制可用于填充坐标轴的整个垂直高度。为了不受 y 轴范围的影响,我们添加一个变换,该变换将数据坐标中的 x 值和坐标轴坐标中的 y 值进行解释。以下示例标记了 y 数据高于给定阈值的区域。

fig, ax = plt.subplots()
x = np.arange(0, 4 * np.pi, 0.01)
y = np.sin(x)
ax.plot(x, y, color='black')

threshold = 0.75
ax.axhline(threshold, color='green', lw=2, alpha=0.7)
ax.fill_between(x, 0, 1, where=y > threshold,
                color='green', alpha=0.5, transform=ax.get_xaxis_transform())

在这个示例中:

  1. 创建了一个图形和坐标轴对象。
  2. 生成了从 0 到 4π 的 x 值数组,并计算了对应的 y = sin(x) 值。
  3. 绘制了黑色的曲线 y = sin(x)。
  4. 设置了阈值为 0.75,并绘制了一条绿色的水平参考线。
  5. 使用 fill_between 函数,通过 where=y > threshold 条件,在 x 轴数据范围内,从 y = 0 到 y = 1 的区域填充绿色,并且使用 ax.get_xaxis_transform() 进行坐标变换,使得填充区域不受 y 轴范围的影响。

总结

在本实验中,你学习了如何使用 Matplotlib 中的 fill_between 函数来填充两条线之间的区域。你还学习了如何选择性地填充水平区域以及标记整个坐标轴上的水平区域。