Matplotlib中创建多个子图的全面指南
参考:How to Create Multiple Subplots in Matplotlib
Matplotlib是Python中最流行的数据可视化库之一,它提供了强大的工具来创建各种类型的图表和绘图。在数据分析和科学研究中,我们经常需要在同一个图形窗口中展示多个相关的图表。这就需要用到Matplotlib中的子图(subplots)功能。本文将全面介绍如何在Matplotlib中创建和使用多个子图,帮助你更好地展示和比较数据。
1. 子图的基本概念
在Matplotlib中,子图是指在一个图形窗口中创建的多个独立的绘图区域。每个子图可以包含自己的坐标轴、标题和数据。使用子图可以让我们在一个窗口中同时展示多个相关的图表,方便比较和分析数据。
创建子图的最基本方法是使用plt.subplot()
函数。这个函数接受三个参数:行数、列数和子图索引。例如:
import matplotlib.pyplot as plt
import numpy as np
# 创建数据
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
# 创建2x1的子图布局
plt.subplot(2, 1, 1)
plt.plot(x, y1)
plt.title('Sine Wave - how2matplotlib.com')
plt.subplot(2, 1, 2)
plt.plot(x, y2)
plt.title('Cosine Wave - how2matplotlib.com')
plt.tight_layout()
plt.show()
Output:
在这个例子中,我们创建了一个2行1列的子图布局。第一个子图显示正弦波,第二个子图显示余弦波。plt.tight_layout()
函数用于自动调整子图之间的间距,使布局更加紧凑和美观。
2. 使用plt.subplots()创建子图
虽然plt.subplot()
函数很有用,但当我们需要创建大量子图时,使用plt.subplots()
函数会更加方便。这个函数可以一次性创建多个子图,并返回图形对象和子图数组。
import matplotlib.pyplot as plt
import numpy as np
# 创建数据
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
y3 = np.tan(x)
y4 = np.exp(x)
# 创建2x2的子图布局
fig, axs = plt.subplots(2, 2, figsize=(10, 8))
axs[0, 0].plot(x, y1)
axs[0, 0].set_title('Sine - how2matplotlib.com')
axs[0, 1].plot(x, y2)
axs[0, 1].set_title('Cosine - how2matplotlib.com')
axs[1, 0].plot(x, y3)
axs[1, 0].set_title('Tangent - how2matplotlib.com')
axs[1, 1].plot(x, y4)
axs[1, 1].set_title('Exponential - how2matplotlib.com')
plt.tight_layout()
plt.show()
Output:
在这个例子中,我们创建了一个2×2的子图布局,并在每个子图中绘制了不同的函数。figsize
参数用于设置整个图形的大小。
3. 自定义子图布局
有时候,我们可能需要更灵活的子图布局。Matplotlib提供了GridSpec
类来实现复杂的子图布局。
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import numpy as np
# 创建数据
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
y3 = np.tan(x)
# 创建自定义布局
fig = plt.figure(figsize=(12, 8))
gs = gridspec.GridSpec(2, 2)
ax1 = fig.add_subplot(gs[0, :])
ax2 = fig.add_subplot(gs[1, 0])
ax3 = fig.add_subplot(gs[1, 1])
ax1.plot(x, y1)
ax1.set_title('Sine Wave - how2matplotlib.com')
ax2.plot(x, y2)
ax2.set_title('Cosine Wave - how2matplotlib.com')
ax3.plot(x, y3)
ax3.set_title('Tangent Wave - how2matplotlib.com')
plt.tight_layout()
plt.show()
Output:
在这个例子中,我们使用GridSpec
创建了一个自定义布局,其中顶部是一个跨越两列的大子图,底部是两个小子图。
4. 共享坐标轴
当我们需要比较多个子图中的数据时,共享坐标轴可以使比较更加直观。Matplotlib允许我们在创建子图时指定共享的x轴或y轴。
import matplotlib.pyplot as plt
import numpy as np
# 创建数据
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.sin(2*x)
y3 = np.sin(3*x)
# 创建共享x轴的子图
fig, (ax1, ax2, ax3) = plt.subplots(3, 1, sharex=True, figsize=(8, 10))
ax1.plot(x, y1)
ax1.set_title('sin(x) - how2matplotlib.com')
ax2.plot(x, y2)
ax2.set_title('sin(2x) - how2matplotlib.com')
ax3.plot(x, y3)
ax3.set_title('sin(3x) - how2matplotlib.com')
plt.tight_layout()
plt.show()
Output:
在这个例子中,我们创建了三个垂直排列的子图,它们共享x轴。这样可以很容易地比较不同频率的正弦波。
5. 调整子图间距
默认情况下,Matplotlib会自动设置子图之间的间距。但有时我们可能需要手动调整间距以获得更好的布局效果。我们可以使用plt.subplots_adjust()
函数来实现这一点。
import matplotlib.pyplot as plt
import numpy as np
# 创建数据
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
# 创建子图并调整间距
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(8, 8))
ax1.plot(x, y1)
ax1.set_title('Sine Wave - how2matplotlib.com')
ax2.plot(x, y2)
ax2.set_title('Cosine Wave - how2matplotlib.com')
plt.subplots_adjust(hspace=0.5) # 增加垂直间距
plt.show()
Output:
在这个例子中,我们使用plt.subplots_adjust()
函数增加了子图之间的垂直间距。hspace
参数控制垂直间距,wspace
参数控制水平间距。
6. 不同大小的子图
有时我们可能需要创建不同大小的子图。这可以通过设置子图的相对大小来实现。
import matplotlib.pyplot as plt
import numpy as np
# 创建数据
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
y3 = np.tan(x)
# 创建不同大小的子图
fig = plt.figure(figsize=(12, 8))
gs = fig.add_gridspec(2, 2)
ax1 = fig.add_subplot(gs[0, :])
ax2 = fig.add_subplot(gs[1, 0])
ax3 = fig.add_subplot(gs[1, 1])
ax1.plot(x, y1)
ax1.set_title('Sine Wave (Large) - how2matplotlib.com')
ax2.plot(x, y2)
ax2.set_title('Cosine Wave - how2matplotlib.com')
ax3.plot(x, y3)
ax3.set_title('Tangent Wave - how2matplotlib.com')
plt.tight_layout()
plt.show()
Output:
在这个例子中,我们创建了一个大的子图和两个小的子图。大子图占据了整个顶部行,而两个小子图分别占据底部行的左右两列。
7. 嵌套子图
Matplotlib还支持创建嵌套的子图,即在一个子图内部再创建子图。这可以用于展示更复杂的数据关系。
import matplotlib.pyplot as plt
import numpy as np
# 创建数据
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
# 创建主图和子图
fig = plt.figure(figsize=(12, 8))
ax1 = fig.add_subplot(121)
ax2 = fig.add_subplot(122)
# 在右侧子图中创建嵌套子图
inner_ax1 = ax2.inset_axes([0.1, 0.1, 0.4, 0.4])
inner_ax2 = ax2.inset_axes([0.55, 0.55, 0.4, 0.4])
ax1.plot(x, y1)
ax1.set_title('Sine Wave - how2matplotlib.com')
ax2.plot(x, y2)
ax2.set_title('Cosine Wave with Nested Plots - how2matplotlib.com')
inner_ax1.plot(x[:50], y1[:50], 'r')
inner_ax1.set_title('First Half - how2matplotlib.com')
inner_ax2.plot(x[50:], y2[50:], 'g')
inner_ax2.set_title('Second Half - how2matplotlib.com')
plt.tight_layout()
plt.show()
Output:
在这个例子中,我们在右侧的子图中创建了两个嵌套的小子图,分别显示了余弦波的前半部分和后半部分。
8. 3D子图
Matplotlib不仅支持2D子图,还支持3D子图。我们可以在一个图形窗口中同时展示2D和3D的子图。
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
# 创建数据
x = np.linspace(-5, 5, 100)
y = np.linspace(-5, 5, 100)
X, Y = np.meshgrid(x, y)
Z = np.sin(np.sqrt(X**2 + Y**2))
# 创建2D和3D子图
fig = plt.figure(figsize=(12, 6))
ax1 = fig.add_subplot(121)
ax2 = fig.add_subplot(122, projection='3d')
# 2D等高线图
c = ax1.contourf(X, Y, Z)
ax1.set_title('2D Contour - how2matplotlib.com')
fig.colorbar(c, ax=ax1)
# 3D表面图
surf = ax2.plot_surface(X, Y, Z, cmap='viridis')
ax2.set_title('3D Surface - how2matplotlib.com')
fig.colorbar(surf, ax=ax2, shrink=0.5, aspect=5)
plt.tight_layout()
plt.show()
Output:
在这个例子中,我们创建了一个2D等高线图和一个3D表面图,展示了同一数据的不同视角。
9. 子图中的多个图表
在一个子图中,我们可以绘制多个图表。这对于比较不同数据集或展示数据的不同方面非常有用。
import matplotlib.pyplot as plt
import numpy as np
# 创建数据
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
y3 = np.exp(-x/10)*np.sin(x)
# 创建子图
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
# 在左侧子图中绘制多个图表
ax1.plot(x, y1, label='sin(x)')
ax1.plot(x, y2, label='cos(x)')
ax1.set_title('Trigonometric Functions - how2matplotlib.com')
ax1.legend()
# 在右侧子图中绘制主图和插图
ax2.plot(x, y3)
ax2.set_title('Damped Sine Wave - how2matplotlib.com')
# 创建插图
inset_ax = ax2.inset_axes([0.6, 0.6, 0.35, 0.35])
inset_ax.plot(x[:30], y3[:30])
inset_ax.set_title('Zoom - how2matplotlib.com')
plt.tight_layout()
plt.show()
Output:
在这个例子中,左侧子图展示了正弦和余弦函数,右侧子图展示了一个衰减的正弦波,并包含一个放大的插图。
10. 子图中的不同类型图表
Matplotlib允许我们在不同的子图中使用不同类型的图表,这对于全面展示数据的各个方面非常有用。
import matplotlib.pyplot as plt
import numpy as np
# 创建数据
x = np.linspace(0, 10, 100)
y = np.sin(x)
categories = ['A', 'B', 'C', 'D', 'E']
values = [23, 45, 56, 78, 32]
# 创建2x2的子图布局
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(12, 10))
# 线图
ax1.plot(x, y)
ax1.set_title('Line Plot - how2matplotlib.com')
# 散点图
ax2.scatter(x[::10], y[::10])
ax2.set_title('Scatter Plot - how2matplotlib.com')
# 柱状图
ax3.bar(categories, values)
ax3.set_title('Bar Plot - how2matplotlib.com')
# 饼图
ax4.pie(values, labels=categories, autopct='%1.1f%%')
ax4.set_title('Pie Chart - how2matplotlib.com')
plt.tight_layout()
plt.show()
Output:
在这个例子中,我们在四个子图中分别创建了线图、散点图、柱状图和饼图,展示了Matplotlib支持的不同图表类型。
11. 子图中的图像显示
除了绘制图表,Matplotlib还支持在子图中显示图像。这对于图像处理和计算机视觉任务非常有用。
import matplotlib.pyplot as plt
import numpy as np
# 创建示例图像数据
image1 = np.random.rand(100, 100)
image2 = np.random.rand(100, 100)
# 创建2x1的子图布局
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 5))
# 显示第一张图像
im1 = ax1.imshow(image1, cmap='viridis')
ax1.set_title('Image 1 - how2matplotlib.com')
fig.colorbar(im1, ax=ax1)
# 显示第二张图像
im2 = ax2.imshow(image2, cmap='plasma')
ax2.set_title('Image 2 - how2matplotlib.com')
fig.colorbar(im2, ax=ax2)
plt.tight_layout()
plt.show()
Output:
在这个例子中,我们创建了两个随机的图像数据,并在两个子图中分别显示它们。我们还为每个图像添加了颜色条,以显示像素值的范围。
12. 子图中的极坐标图
Matplotlib还支持在子图中创建极坐标图,这对于显示周期性数据或方向数据非常有用。
import matplotlib.pyplot as plt
import numpy as np
# 创建数据
r = np.linspace(0, 2, 100)
theta = 4 * np.pi * r
# 创建1x2的子图布局
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5), subplot_kw=dict(projection='polar'))
# 第一个极坐标图
ax1.plot(theta, r)
ax1.set_title('Spiral - how2matplotlib.com')
# 第二个极坐标图(玫瑰图)
ax2.bar(np.linspace(0, 2*np.pi, 8, endpoint=False), np.random.rand(8), width=0.8)
ax2.set_title('Rose Plot - how2matplotlib.com')
plt.tight_layout()
plt.show()
Output:
在这个例子中,我们创建了两个极坐标子图。左侧子图显示了一个螺旋线,右侧子图显示了一个玫瑰图(也称为极坐标柱状图)。
13. 子图中的动画
Matplotlib还支持创建动画,我们可以在子图中展示动态变化的数据。
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
# 创建数据
x = np.linspace(0, 2*np.pi, 100)
y = np.sin(x)
# 创建图形和子图
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
line1, = ax1.plot(x, y)
line2, = ax2.plot(x, y)
ax1.set_title('Sine Wave - how2matplotlib.com')
ax2.set_title('Cosine Wave - how2matplotlib.com')
# 定义动画函数
def animate(i):
line1.set_ydata(np.sin(x + i/10))
line2.set_ydata(np.cos(x + i/10))
return line1, line2
# 创建动画
anim = animation.FuncAnimation(fig, animate, frames=100, interval=50, blit=True)
plt.tight_layout()
plt.show()
Output:
在这个例子中,我们创建了两个子图,分别显示正弦波和余弦波。通过动画,我们可以看到这两个波形随时间变化的过程。
14. 子图中的误差线
在科学绘图中,我们经常需要显示数据的误差范围。Matplotlib提供了绘制误差线的功能,可以在子图中轻松实现。
import matplotlib.pyplot as plt
import numpy as np
# 创建数据
x = np.linspace(0, 10, 50)
y1 = np.sin(x)
y2 = np.cos(x)
error1 = np.random.normal(0, 0.1, len(x))
error2 = np.random.normal(0, 0.1, len(x))
# 创建2x1的子图布局
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8))
# 绘制带误差线的图表
ax1.errorbar(x, y1, yerr=error1, capsize=5, label='Sine')
ax1.set_title('Sine with Error Bars - how2matplotlib.com')
ax1.legend()
ax2.errorbar(x, y2, yerr=error2, fmt='o', capsize=5, label='Cosine')
ax2.set_title('Cosine with Error Bars - how2matplotlib.com')
ax2.legend()
plt.tight_layout()
plt.show()
在这个例子中,我们在两个子图中分别绘制了带有误差线的正弦波和余弦波。误差线显示了数据点的不确定性范围。
15. 子图中的填充区域
有时我们需要强调图表中的某些区域,这可以通过填充来实现。Matplotlib提供了多种方法来填充图表的区域。
import matplotlib.pyplot as plt
import numpy as np
# 创建数据
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
# 创建2x1的子图布局
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8))
# 填充曲线下方区域
ax1.plot(x, y1)
ax1.fill_between(x, y1, where=(y1 > 0), alpha=0.3)
ax1.set_title('Filled Sine Wave - how2matplotlib.com')
# 填充两条曲线之间的区域
ax2.plot(x, y1, label='Sine')
ax2.plot(x, y2, label='Cosine')
ax2.fill_between(x, y1, y2, where=(y1 > y2), alpha=0.3)
ax2.set_title('Filled Area Between Curves - how2matplotlib.com')
ax2.legend()
plt.tight_layout()
plt.show()
Output:
在这个例子中,上面的子图填充了正弦波曲线上方的区域,下面的子图填充了正弦波和余弦波之间的区域。
16. 子图中的双轴图
有时我们需要在同一个子图中显示具有不同范围或单位的数据。这可以通过创建双轴图来实现。
import matplotlib.pyplot as plt
import numpy as np
# 创建数据
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.exp(x/10)
# 创建图形和主轴
fig, ax1 = plt.subplots(figsize=(10, 6))
# 绘制第一条曲线
color = 'tab:blue'
ax1.set_xlabel('X-axis')
ax1.set_ylabel('Sine', color=color)
ax1.plot(x, y1, color=color)
ax1.tick_params(axis='y', labelcolor=color)
# 创建第二个Y轴
ax2 = ax1.twinx()
color = 'tab:orange'
ax2.set_ylabel('Exponential', color=color)
ax2.plot(x, y2, color=color)
ax2.tick_params(axis='y', labelcolor=color)
plt.title('Dual Axis Plot - how2matplotlib.com')
plt.tight_layout()
plt.show()
Output:
在这个例子中,我们在同一个子图中绘制了正弦函数和指数函数,使用不同的Y轴来表示它们的值范围。
17. 子图中的对数刻度
在处理跨越多个数量级的数据时,使用对数刻度可以更好地展示数据的变化。Matplotlib允许我们轻松地为子图设置对数刻度。
import matplotlib.pyplot as plt
import numpy as np
# 创建数据
x = np.logspace(0, 5, 100)
y1 = x**2
y2 = x**3
# 创建2x1的子图布局
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8))
# 线性刻度
ax1.plot(x, y1, label='y = x^2')
ax1.plot(x, y2, label='y = x^3')
ax1.set_title('Linear Scale - how2matplotlib.com')
ax1.legend()
# 对数刻度
ax2.loglog(x, y1, label='y = x^2')
ax2.loglog(x, y2, label='y = x^3')
ax2.set_title('Log-Log Scale - how2matplotlib.com')
ax2.legend()
plt.tight_layout()
plt.show()
Output:
在这个例子中,我们创建了两个子图来比较线性刻度和对数刻度的效果。对数刻度使得跨越多个数量级的数据更容易观察和比较。
18. 子图中的箱线图
箱线图是一种用于显示数据分布的有效方法。我们可以在子图中创建箱线图来比较不同数据集的统计特征。
import matplotlib.pyplot as plt
import numpy as np
# 创建数据
data1 = np.random.normal(0, 1, 1000)
data2 = np.random.normal(2, 1.5, 1000)
data3 = np.random.normal(-1, 2, 1000)
# 创建1x2的子图布局
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
# 垂直箱线图
ax1.boxplot([data1, data2, data3], labels=['Data 1', 'Data 2', 'Data 3'])
ax1.set_title('Vertical Box Plot - how2matplotlib.com')
# 水平箱线图
ax2.boxplot([data1, data2, data3], labels=['Data 1', 'Data 2', 'Data 3'], vert=False)
ax2.set_title('Horizontal Box Plot - how2matplotlib.com')
plt.tight_layout()
plt.show()
Output:
在这个例子中,我们创建了两个子图,分别显示垂直和水平的箱线图,用于比较三个不同的数据集的分布情况。
19. 子图中的热力图
热力图是一种用颜色来表示数值大小的图表类型,适合用于显示矩阵数据或相关性分析结果。
import matplotlib.pyplot as plt
import numpy as np
# 创建数据
data1 = np.random.rand(10, 10)
data2 = np.random.rand(10, 10)
# 创建1x2的子图布局
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
# 第一个热力图
im1 = ax1.imshow(data1, cmap='viridis')
ax1.set_title('Heatmap 1 - how2matplotlib.com')
fig.colorbar(im1, ax=ax1)
# 第二个热力图
im2 = ax2.imshow(data2, cmap='plasma')
ax2.set_title('Heatmap 2 - how2matplotlib.com')
fig.colorbar(im2, ax=ax2)
plt.tight_layout()
plt.show()
Output:
在这个例子中,我们创建了两个子图,每个子图都显示了一个热力图。热力图使用不同的颜色来表示数据的大小,颜色条显示了颜色与数值的对应关系。
20. 子图中的极坐标直方图
极坐标直方图是一种特殊类型的图表,它将传统的直方图展示在极坐标系中。这种图表对于显示周期性数据或方向数据特别有用。
import matplotlib.pyplot as plt
import numpy as np
# 创建数据
data1 = np.random.normal(0, 2, 1000)
data2 = np.random.normal(0, 3, 1000)
# 创建1x2的子图布局
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5), subplot_kw=dict(projection='polar'))
# 第一个极坐标直方图
ax1.hist(data1, bins=16, bottom=4)
ax1.set_title('Polar Histogram 1 - how2matplotlib.com')
# 第二个极坐标直方图
ax2.hist(data2, bins=16, bottom=4)
ax2.set_title('Polar Histogram 2 - how2matplotlib.com')
plt.tight_layout()
plt.show()
Output:
在这个例子中,我们创建了两个极坐标子图,每个子图都显示了一个极坐标直方图。这种图表形式可以有效地展示数据的分布和方向性。
总结
在本文中,我们全面探讨了如何在Matplotlib中创建和使用多个子图。我们涵盖了从基本的子图创建到高级的自定义布局,从2D图表到3D图表,从静态图表到动画。以下是本文涉及的主要内容:
- 子图的基本概念和创建方法
- 使用plt.subplots()创建子图
- 自定义子图布局
- 共享坐标轴
- 调整子图间距
- 创建不同大小的子图
- 嵌套子图
- 3D子图
- 在子图中绘制多个图表
- 在子图中使用不同类型的图表
- 在子图中显示图像
- 极坐标子图
- 子图中的动画
- 子图中的误差线
- 子图中的填充区域
- 双轴图
- 对数刻度
- 箱线图
- 热力图
- 极坐标直方图
通过掌握这些技巧,你可以创建更加丰富、信息量更大的可视化图表,更好地展示和分析你的数据。记住,创建有效的数据可视化不仅需要技术skills,还需要对数据有深入的理解,以及清晰的表达意图。
在实际应用中,你可能需要根据具体的数据和需求来选择合适的子图类型和布局。不要害怕尝试不同的组合和设置,因为有时候最有洞察力的可视化可能来自于意想不到的图表组合。
最后,虽然Matplotlib提供了强大的子图功能,但在处理大量数据或需要高度交互性的场景时,你可能还需要考虑使用其他专门的数据可视化库,如Seaborn、Plotly或Bokeh。这些库在Matplotlib的基础上提供了更高级的功能和更美观的默认样式。
无论如何,掌握Matplotlib中的子图创建和使用是数据科学和可视化领域的重要技能。通过不断练习和实验,你将能够创建出既美观又富有洞察力的数据可视化作品。