Matplotlib中如何创建跨越多行多列的子图布局

Matplotlib中如何创建跨越多行多列的子图布局

参考:Make subplots span multiple grid rows and columns in Matplotlib

Matplotlib是Python中强大的数据可视化库,它提供了灵活的方式来创建复杂的图表布局。在本文中,我们将深入探讨如何在Matplotlib中创建跨越多行多列的子图布局。这种技术对于创建复杂的数据展示非常有用,可以让你在一个图形窗口中展示多个相关但又独立的图表,同时保持整体布局的美观和逻辑性。

1. 基础知识:理解Matplotlib的子图概念

在开始创建跨越多行多列的子图之前,我们需要先理解Matplotlib中的子图概念。子图是指在一个图形窗口中的独立绘图区域。通过使用子图,我们可以在同一个窗口中绘制多个图表。

以下是一个简单的创建子图的例子:

import matplotlib.pyplot as plt

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4))

ax1.plot([1, 2, 3, 4], [1, 4, 2, 3])
ax1.set_title('Plot 1 - how2matplotlib.com')

ax2.plot([1, 2, 3, 4], [1, 2, 4, 3])
ax2.set_title('Plot 2 - how2matplotlib.com')

plt.tight_layout()
plt.show()

Output:

Matplotlib中如何创建跨越多行多列的子图布局

在这个例子中,我们创建了一个包含两个并排的子图的图形。plt.subplots(1, 2)创建了一行两列的子图布局。

2. 使用GridSpec创建复杂布局

要创建跨越多行多列的子图,我们需要使用Matplotlib的GridSpec功能。GridSpec允许我们更灵活地定义子图的位置和大小。

以下是一个使用GridSpec创建跨越多行多列子图的基本例子:

import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec

fig = plt.figure(figsize=(12, 8))
gs = gridspec.GridSpec(3, 3)

ax1 = fig.add_subplot(gs[0, :])
ax2 = fig.add_subplot(gs[1, :-1])
ax3 = fig.add_subplot(gs[1:, -1])
ax4 = fig.add_subplot(gs[-1, 0])
ax5 = fig.add_subplot(gs[-1, -2])

ax1.set_title('Span all columns - how2matplotlib.com')
ax2.set_title('Span 2 columns - how2matplotlib.com')
ax3.set_title('Span 2 rows - how2matplotlib.com')
ax4.set_title('Single cell 1 - how2matplotlib.com')
ax5.set_title('Single cell 2 - how2matplotlib.com')

plt.tight_layout()
plt.show()

Output:

Matplotlib中如何创建跨越多行多列的子图布局

在这个例子中,我们创建了一个3×3的网格,然后定义了5个子图,其中一些跨越了多个网格单元。

3. 使用add_gridspec方法

从Matplotlib 3.1版本开始,我们可以使用figure.add_gridspec()方法来创建GridSpec对象。这提供了一种更简洁的语法来创建复杂的子图布局。

import matplotlib.pyplot as plt

fig = plt.figure(figsize=(12, 8))
gs = fig.add_gridspec(3, 3)

ax1 = fig.add_subplot(gs[0, :])
ax2 = fig.add_subplot(gs[1, :-1])
ax3 = fig.add_subplot(gs[1:, -1])
ax4 = fig.add_subplot(gs[-1, 0])
ax5 = fig.add_subplot(gs[-1, -2])

ax1.set_title('Span all columns - how2matplotlib.com')
ax2.set_title('Span 2 columns - how2matplotlib.com')
ax3.set_title('Span 2 rows - how2matplotlib.com')
ax4.set_title('Single cell 1 - how2matplotlib.com')
ax5.set_title('Single cell 2 - how2matplotlib.com')

plt.tight_layout()
plt.show()

Output:

Matplotlib中如何创建跨越多行多列的子图布局

这个例子的结果与前一个例子相同,但语法更加简洁。

4. 调整子图之间的间距

在创建跨越多行多列的子图时,调整子图之间的间距对于优化布局非常重要。我们可以使用gridspec_kw参数来调整这些间距。

import matplotlib.pyplot as plt

fig, axs = plt.subplots(3, 3, figsize=(12, 8), 
                        gridspec_kw={'hspace': 0.4, 'wspace': 0.3})

axs[0, 0].set_title('Row 0, Col 0 - how2matplotlib.com')
axs[0, 1].set_title('Row 0, Col 1 - how2matplotlib.com')
axs[0, 2].set_title('Row 0, Col 2 - how2matplotlib.com')
axs[1, 0].set_title('Row 1, Col 0 - how2matplotlib.com')
axs[1, 1].set_title('Row 1, Col 1 - how2matplotlib.com')
axs[1, 2].set_title('Row 1, Col 2 - how2matplotlib.com')
axs[2, 0].set_title('Row 2, Col 0 - how2matplotlib.com')
axs[2, 1].set_title('Row 2, Col 1 - how2matplotlib.com')
axs[2, 2].set_title('Row 2, Col 2 - how2matplotlib.com')

plt.tight_layout()
plt.show()

Output:

Matplotlib中如何创建跨越多行多列的子图布局

在这个例子中,hspace控制子图之间的垂直间距,wspace控制水平间距。

5. 创建不规则的网格布局

有时,我们可能需要创建更加不规则的网格布局。GridSpec允许我们定义具有不同宽度和高度的行和列。

import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec

fig = plt.figure(figsize=(12, 8))
gs = gridspec.GridSpec(3, 3, width_ratios=[1, 2, 1], height_ratios=[1, 2, 1])

ax1 = fig.add_subplot(gs[0, :])
ax2 = fig.add_subplot(gs[1, :-1])
ax3 = fig.add_subplot(gs[1:, -1])
ax4 = fig.add_subplot(gs[-1, 0])
ax5 = fig.add_subplot(gs[-1, -2])

ax1.set_title('Span all columns - how2matplotlib.com')
ax2.set_title('Span 2 columns - how2matplotlib.com')
ax3.set_title('Span 2 rows - how2matplotlib.com')
ax4.set_title('Single cell 1 - how2matplotlib.com')
ax5.set_title('Single cell 2 - how2matplotlib.com')

plt.tight_layout()
plt.show()

Output:

Matplotlib中如何创建跨越多行多列的子图布局

在这个例子中,我们使用width_ratiosheight_ratios参数来定义不同宽度和高度的列和行。

6. 嵌套GridSpec

对于更复杂的布局,我们可以使用嵌套的GridSpec。这允许我们在一个子图区域内创建另一个网格布局。

import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec

fig = plt.figure(figsize=(12, 8))
gs0 = gridspec.GridSpec(1, 2, figure=fig)

gs00 = gridspec.GridSpecFromSubplotSpec(2, 2, subplot_spec=gs0[0])
gs01 = gridspec.GridSpecFromSubplotSpec(3, 3, subplot_spec=gs0[1])

ax1 = fig.add_subplot(gs00[0, 0])
ax2 = fig.add_subplot(gs00[0, 1])
ax3 = fig.add_subplot(gs00[1, :])

ax4 = fig.add_subplot(gs01[:-1, :])
ax5 = fig.add_subplot(gs01[-1, :-1])
ax6 = fig.add_subplot(gs01[-1, -1])

ax1.set_title('Nested 1 - how2matplotlib.com')
ax2.set_title('Nested 2 - how2matplotlib.com')
ax3.set_title('Nested 3 - how2matplotlib.com')
ax4.set_title('Nested 4 - how2matplotlib.com')
ax5.set_title('Nested 5 - how2matplotlib.com')
ax6.set_title('Nested 6 - how2matplotlib.com')

plt.tight_layout()
plt.show()

Output:

Matplotlib中如何创建跨越多行多列的子图布局

这个例子展示了如何创建一个复杂的嵌套布局,其中左侧是一个2×2的网格,右侧是一个3×3的网格。

7. 使用subplot2grid创建跨越多行多列的子图

除了GridSpec,Matplotlib还提供了subplot2grid函数,它可以用来创建跨越多行多列的子图。

import matplotlib.pyplot as plt

fig = plt.figure(figsize=(12, 8))

ax1 = plt.subplot2grid((3, 3), (0, 0), colspan=3)
ax2 = plt.subplot2grid((3, 3), (1, 0), colspan=2)
ax3 = plt.subplot2grid((3, 3), (1, 2), rowspan=2)
ax4 = plt.subplot2grid((3, 3), (2, 0))
ax5 = plt.subplot2grid((3, 3), (2, 1))

ax1.set_title('Span all columns - how2matplotlib.com')
ax2.set_title('Span 2 columns - how2matplotlib.com')
ax3.set_title('Span 2 rows - how2matplotlib.com')
ax4.set_title('Single cell 1 - how2matplotlib.com')
ax5.set_title('Single cell 2 - how2matplotlib.com')

plt.tight_layout()
plt.show()

Output:

Matplotlib中如何创建跨越多行多列的子图布局

在这个例子中,subplot2grid函数的第一个参数指定网格的大小,第二个参数指定子图的起始位置,colspanrowspan参数用于指定子图跨越的列数和行数。

8. 使用add_axes方法创建自定义位置的子图

对于需要更精确控制子图位置和大小的情况,我们可以使用fig.add_axes方法。这个方法允许我们使用相对坐标来定位子图。

import matplotlib.pyplot as plt

fig = plt.figure(figsize=(12, 8))

ax1 = fig.add_axes([0.1, 0.7, 0.8, 0.2])
ax2 = fig.add_axes([0.1, 0.3, 0.5, 0.3])
ax3 = fig.add_axes([0.7, 0.3, 0.2, 0.5])
ax4 = fig.add_axes([0.1, 0.1, 0.2, 0.1])
ax5 = fig.add_axes([0.4, 0.1, 0.2, 0.1])

ax1.set_title('Custom position 1 - how2matplotlib.com')
ax2.set_title('Custom position 2 - how2matplotlib.com')
ax3.set_title('Custom position 3 - how2matplotlib.com')
ax4.set_title('Custom position 4 - how2matplotlib.com')
ax5.set_title('Custom position 5 - how2matplotlib.com')

plt.show()

Output:

Matplotlib中如何创建跨越多行多列的子图布局

在这个例子中,add_axes方法的参数是一个列表,包含四个值:[left, bottom, width, height],这些值都是相对于整个图形的比例。

9. 创建具有共享轴的子图

在某些情况下,我们可能希望创建共享x轴或y轴的子图。这可以通过sharexsharey参数来实现。

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 2*np.pi, 400)
y = np.sin(x**2)

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

ax1.plot(x, y)
ax1.set_title('Shared X-axis - how2matplotlib.com')

ax2.plot(x, y**2)
ax2.set_title('Shared X-axis - how2matplotlib.com')

ax3.plot(x, y**3)
ax3.set_title('Shared X-axis - how2matplotlib.com')

plt.tight_layout()
plt.show()

Output:

Matplotlib中如何创建跨越多行多列的子图布局

在这个例子中,三个子图共享同一个x轴,这意味着当你在一个子图上缩放或平移x轴时,其他子图的x轴也会相应地改变。

10. 创建具有不同比例的子图

有时,我们可能需要在同一个图形中创建具有不同比例的子图。这可以通过为每个子图单独设置aspect参数来实现。

import matplotlib.pyplot as plt
import numpy as np

fig = plt.figure(figsize=(12, 8))
gs = fig.add_gridspec(2, 2)

ax1 = fig.add_subplot(gs[0, 0])
ax2 = fig.add_subplot(gs[0, 1])
ax3 = fig.add_subplot(gs[1, :])

x = np.linspace(0, 2*np.pi, 100)
y = np.sin(x)

ax1.plot(x, y)
ax1.set_title('Default aspect - how2matplotlib.com')

ax2.plot(x, y)
ax2.set_aspect('equal')
ax2.set_title('Equal aspect - how2matplotlib.com')

ax3.plot(x, y)
ax3.set_aspect(0.5)
ax3.set_title('Custom aspect - how2matplotlib.com')

plt.tight_layout()
plt.show()

Output:

Matplotlib中如何创建跨越多行多列的子图布局

在这个例子中,我们创建了三个子图,每个子图都有不同的纵横比。## 11. 使用constrained_layout自动调整布局

constrained_layout是Matplotlib提供的一个自动布局调整功能,它可以自动处理子图之间的间距和标签重叠问题。

import matplotlib.pyplot as plt
import numpy as np

fig, axs = plt.subplots(3, 3, figsize=(12, 10), constrained_layout=True)

for ax in axs.flat:
    ax.plot(np.random.rand(50))
    ax.set_title('Subplot - how2matplotlib.com')
    ax.set_xlabel('X Label')
    ax.set_ylabel('Y Label')

fig.suptitle('Constrained Layout Example - how2matplotlib.com', fontsize=16)
plt.show()

Output:

Matplotlib中如何创建跨越多行多列的子图布局

在这个例子中,我们创建了一个3×3的子图网格,并使用constrained_layout=True来自动调整布局。这样可以确保所有的标签和标题都不会重叠。

12. 创建不规则形状的子图

有时,我们可能需要创建一些不规则形状的子图布局。这可以通过组合不同大小的子图来实现。

import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec

fig = plt.figure(figsize=(12, 8))
gs = gridspec.GridSpec(3, 3)

ax1 = fig.add_subplot(gs[0, :2])
ax2 = fig.add_subplot(gs[0, 2])
ax3 = fig.add_subplot(gs[1:, 0])
ax4 = fig.add_subplot(gs[1:, 1:])

ax1.set_title('Irregular shape 1 - how2matplotlib.com')
ax2.set_title('Irregular shape 2 - how2matplotlib.com')
ax3.set_title('Irregular shape 3 - how2matplotlib.com')
ax4.set_title('Irregular shape 4 - how2matplotlib.com')

plt.tight_layout()
plt.show()

Output:

Matplotlib中如何创建跨越多行多列的子图布局

这个例子创建了四个不同大小和形状的子图,展示了如何使用GridSpec来创建复杂的布局。

13. 在子图中嵌入子图

Matplotlib允许我们在一个子图中嵌入另一个子图,这对于创建图中图(inset)非常有用。

import matplotlib.pyplot as plt
import numpy as np

fig, ax = plt.subplots(figsize=(10, 8))

# 主图
x = np.linspace(0, 10, 100)
y = np.sin(x)
ax.plot(x, y)
ax.set_title('Main plot with inset - how2matplotlib.com')

# 嵌入的子图
axins = ax.inset_axes([0.6, 0.1, 0.3, 0.3])
axins.plot(x, y)
axins.set_xlim(4, 6)
axins.set_ylim(-1, 1)
axins.set_xticklabels([])
axins.set_yticklabels([])

ax.indicate_inset_zoom(axins)

plt.show()

Output:

Matplotlib中如何创建跨越多行多列的子图布局

这个例子在主图中创建了一个放大的区域,展示了如何使用inset_axes方法来创建嵌入的子图。

14. 创建具有不同比例的子图组

有时我们可能需要创建一组子图,其中每个子图具有不同的大小。这可以通过调整GridSpec的参数来实现。

import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec

fig = plt.figure(figsize=(12, 8))
gs = gridspec.GridSpec(2, 2, width_ratios=[2, 1], height_ratios=[1, 2])

ax1 = fig.add_subplot(gs[0, 0])
ax2 = fig.add_subplot(gs[0, 1])
ax3 = fig.add_subplot(gs[1, 0])
ax4 = fig.add_subplot(gs[1, 1])

ax1.set_title('Larger width - how2matplotlib.com')
ax2.set_title('Smaller width - how2matplotlib.com')
ax3.set_title('Larger height - how2matplotlib.com')
ax4.set_title('Default size - how2matplotlib.com')

plt.tight_layout()
plt.show()

Output:

Matplotlib中如何创建跨越多行多列的子图布局

在这个例子中,我们使用width_ratiosheight_ratios参数来创建具有不同大小的子图。

15. 创建圆形子图

虽然大多数子图都是矩形的,但Matplotlib也允许我们创建圆形的子图,这对于某些特殊的可视化需求可能很有用。

import matplotlib.pyplot as plt
import numpy as np

fig, ax = plt.subplots(figsize=(8, 8))

circle = plt.Circle((0.5, 0.5), 0.4, transform=ax.transAxes, facecolor='lightgrey', edgecolor='black')
ax.add_artist(circle)

theta = np.linspace(0, 2*np.pi, 100)
r = 0.4
x = 0.5 + r * np.cos(theta)
y = 0.5 + r * np.sin(theta)

ax.plot(x, y, transform=ax.transAxes)
ax.set_aspect('equal')
ax.axis('off')

ax.text(0.5, 0.5, 'Circular Subplot\nhow2matplotlib.com', ha='center', va='center', transform=ax.transAxes)

plt.show()

Output:

Matplotlib中如何创建跨越多行多列的子图布局

这个例子展示了如何创建一个圆形的子图区域,并在其中绘制内容。

16. 创建极坐标子图

对于某些类型的数据,使用极坐标系可能比笛卡尔坐标系更合适。Matplotlib允许我们轻松创建极坐标子图。

import matplotlib.pyplot as plt
import numpy as np

fig = plt.figure(figsize=(12, 5))

ax1 = fig.add_subplot(121, projection='polar')
ax2 = fig.add_subplot(122, projection='polar')

r = np.arange(0, 2, 0.01)
theta = 2 * np.pi * r

ax1.plot(theta, r)
ax1.set_title('Linear - how2matplotlib.com')

ax2.plot(theta, r**2)
ax2.set_title('Quadratic - how2matplotlib.com')

plt.tight_layout()
plt.show()

Output:

Matplotlib中如何创建跨越多行多列的子图布局

这个例子创建了两个极坐标子图,展示了如何在极坐标系中绘制不同的函数。

17. 创建3D子图

Matplotlib还支持创建3D子图,这对于可视化三维数据非常有用。

import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D

fig = plt.figure(figsize=(12, 5))

ax1 = fig.add_subplot(121, projection='3d')
ax2 = fig.add_subplot(122, projection='3d')

x = np.linspace(-5, 5, 100)
y = np.linspace(-5, 5, 100)
X, Y = np.meshgrid(x, y)
Z1 = np.sin(np.sqrt(X**2 + Y**2))
Z2 = np.cos(np.sqrt(X**2 + Y**2))

ax1.plot_surface(X, Y, Z1)
ax1.set_title('3D Surface 1 - how2matplotlib.com')

ax2.plot_surface(X, Y, Z2)
ax2.set_title('3D Surface 2 - how2matplotlib.com')

plt.tight_layout()
plt.show()

Output:

Matplotlib中如何创建跨越多行多列的子图布局

这个例子创建了两个3D子图,展示了如何绘制三维表面图。

18. 创建具有共享颜色条的子图

当我们有多个子图需要使用相同的颜色映射时,创建一个共享的颜色条可以使图表更加简洁和易读。

import matplotlib.pyplot as plt
import numpy as np

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))

data1 = np.random.rand(10, 10)
data2 = np.random.rand(10, 10)

im1 = ax1.imshow(data1, cmap='viridis')
ax1.set_title('Subplot 1 - how2matplotlib.com')

im2 = ax2.imshow(data2, cmap='viridis')
ax2.set_title('Subplot 2 - how2matplotlib.com')

fig.colorbar(im1, ax=[ax1, ax2], label='Shared Colorbar')

plt.tight_layout()
plt.show()

Output:

Matplotlib中如何创建跨越多行多列的子图布局

这个例子创建了两个子图,它们共享同一个颜色条,这对于比较两个数据集非常有用。

19. 创建具有不同坐标系的子图组合

Matplotlib允许我们在同一个图形中组合不同类型的坐标系,这对于展示不同类型的数据非常有用。

import matplotlib.pyplot as plt
import numpy as np

fig = plt.figure(figsize=(12, 8))

gs = fig.add_gridspec(2, 2)

ax1 = fig.add_subplot(gs[0, 0])
ax2 = fig.add_subplot(gs[0, 1], projection='polar')
ax3 = fig.add_subplot(gs[1, 0], projection='3d')
ax4 = fig.add_subplot(gs[1, 1])

# 笛卡尔坐标系
x = np.linspace(0, 10, 100)
ax1.plot(x, np.sin(x))
ax1.set_title('Cartesian - how2matplotlib.com')

# 极坐标系
theta = np.linspace(0, 2*np.pi, 100)
ax2.plot(theta, np.sin(4*theta))
ax2.set_title('Polar - how2matplotlib.com')

# 3D坐标系
X, Y = np.meshgrid(range(10), range(10))
Z = np.sin(X) + np.cos(Y)
ax3.plot_surface(X, Y, Z)
ax3.set_title('3D - how2matplotlib.com')

# 对数坐标系
ax4.loglog(x, x**2)
ax4.set_title('Log-Log - how2matplotlib.com')

plt.tight_layout()
plt.show()

Output:

Matplotlib中如何创建跨越多行多列的子图布局

这个例子展示了如何在一个图形中组合笛卡尔坐标系、极坐标系、3D坐标系和对数坐标系。

20. 创建动态子图布局

有时,我们可能需要根据数据的数量动态创建子图。以下是一个根据数据列表长度动态创建子图的例子:

import matplotlib.pyplot as plt
import numpy as np

def create_dynamic_subplots(data_list):
    n = len(data_list)
    cols = int(np.ceil(np.sqrt(n)))
    rows = int(np.ceil(n / cols))

    fig, axs = plt.subplots(rows, cols, figsize=(4*cols, 4*rows))
    axs = axs.flatten()  # 将多维数组转为一维

    for i, data in enumerate(data_list):
        axs[i].plot(data)
        axs[i].set_title(f'Subplot {i+1} - how2matplotlib.com')

    # 隐藏多余的子图
    for j in range(i+1, len(axs)):
        axs[j].axis('off')

    plt.tight_layout()
    plt.show()

# 示例使用
data_list = [np.random.rand(50) for _ in range(7)]
create_dynamic_subplots(data_list)

这个例子展示了如何根据数据列表的长度动态创建子图布局,并自动调整图形大小和布局。

总结:

在本文中,我们深入探讨了Matplotlib中创建跨越多行多列子图布局的各种方法和技巧。我们从基本的子图概念开始,逐步深入到更复杂的布局技术,包括使用GridSpec、subplot2grid、add_axes等方法。我们还介绍了如何创建共享轴的子图、不规则形状的子图、嵌套子图,以及如何处理不同类型的坐标系。

通过掌握这些技术,你将能够创建复杂而富有表现力的图表布局,更好地展示和比较多个数据集。无论是创建简单的并排图表,还是复杂的仪表板式布局,Matplotlib都提供了强大而灵活的工具来满足你的需求。

记住,创建有效的可视化不仅仅是about技术实现,还需要考虑数据的本质和你想要传达的信息。合理使用这些布局技术可以帮助你更清晰、更有效地展示你的数据和分析结果。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程