如何将多个 Matplotlib 图形合并到一个统一的图形中

DDD
发布: 2025-10-15 09:13:01
原创
885人浏览过

如何将多个 matplotlib 图形合并到一个统一的图形中

本教程详细阐述了如何将多个独立的 Matplotlib `Figure` 对象,即便每个对象内部包含多个子图,整合到一个全新的、统一的图形中。核心策略是:从源 `Figure` 的每个 `Axes` 对象中提取绘图数据(如坐标点),然后在一个新创建的主 `Figure` 内设置子图布局,并将提取的数据重新绘制到这些新的子图上,从而实现集中展示与高效输出。

1. 理解问题与核心策略

数据可视化工作中,我们经常会遇到这样的场景:有多个独立的 Matplotlib Figure 对象,这些 Figure 可能由不同的函数生成,每个 Figure 内部甚至可能包含多个 Axes(子图)。我们的目标是将这些分散的 Figure 合并到一个单一的、统一的输出图形中,使每个原始 Figure 在新图中作为一个独立的子图区域呈现。

Matplotlib 库本身并没有提供直接“合并” Figure 对象的功能,因为 Figure 对象是画布的容器,包含其自身的属性和所有 Axes 对象。因此,最有效和推荐的策略是:

  1. 获取源 Figure 对象及其内部的 Axes 对象。
  2. 从每个 Axes 对象中提取其所绘制的数据。 这通常包括线条的 x、y 坐标,散点的坐标,条形图的高度等。
  3. 创建一个全新的主 Figure。
  4. 在新主 Figure 中创建适当数量的子图(Axes 对象), 形成所需的布局。
  5. 将从源 Axes 中提取的数据重新绘制到新主 Figure 的对应子图上。

通过这种数据提取与重绘的方法,我们可以灵活地控制合并后的图形布局和样式,确保最终输出的统一性和专业性。

2. 逐步实现:合并 Matplotlib 图形

下面将详细介绍如何通过 Python 和 Matplotlib 实现上述策略。

2.1 获取源 Figure 对象及其 Axes

假设我们有两个函数 generate_figure_1() 和 generate_figure_2(),它们分别返回一个 matplotlib.figure.Figure 对象。首先,我们需要捕获这些 Figure 对象,并获取它们内部的 Axes 列表。

import matplotlib.pyplot as plt
import numpy as np

# 模拟生成第一个 Figure 的函数
def generate_figure_1():
    fig = plt.figure(figsize=(6, 4))
    ax = fig.add_subplot(111)
    x = np.linspace(0, 10, 100)
    y = np.sin(x)
    ax.plot(x, y, label='Sine Wave')
    ax.set_title('Figure 1: Sine Wave')
    ax.legend()
    plt.close(fig) # 关闭原始Figure,避免显示
    return fig

# 模拟生成第二个 Figure 的函数
def generate_figure_2():
    fig = plt.figure(figsize=(6, 4))
    ax1 = fig.add_subplot(211) # 两个子图
    ax2 = fig.add_subplot(212)
    x = np.linspace(0, 10, 100)
    y1 = np.cos(x)
    y2 = np.exp(-x/2) * np.sin(5*x)
    ax1.plot(x, y1, 'r--', label='Cosine Wave')
    ax2.plot(x, y2, 'g:', label='Damped Sine')
    ax1.set_title('Figure 2: Cosine Wave')
    ax2.set_title('Figure 2: Damped Sine')
    ax1.legend()
    ax2.legend()
    plt.tight_layout()
    plt.close(fig) # 关闭原始Figure,避免显示
    return fig

# 调用函数获取 Figure 对象
fig_1 = generate_figure_1()
fig_2 = generate_figure_2()

# 获取每个 Figure 中的 Axes 对象列表
axes_from_fig1 = fig_1.axes
axes_from_fig2 = fig_2.axes

print(f"Figure 1 包含 {len(axes_from_fig1)} 个 Axes。")
print(f"Figure 2 包含 {len(axes_from_fig2)} 个 Axes。")
登录后复制

2.2 从 Axes 中提取绘图数据

对于线条图(Line2D 对象),我们可以通过 ax.lines 属性访问所有线条,然后使用 get_xdata() 和 get_ydata() 方法提取数据。对于其他类型的图(如散点图、条形图等),数据提取方法会有所不同,可能需要访问 ax.collections (散点图) 或 ax.patches (条形图) 并调用相应的 get_offsets() 或 get_height() 等方法。本教程主要关注线条图的合并。

图像转图像AI
图像转图像AI

利用AI轻松变形、风格化和重绘任何图像

图像转图像AI 65
查看详情 图像转图像AI
# 存储所有待合并的 Axes 的数据
all_plot_data = []

# 提取 Figure 1 的数据
for ax in axes_from_fig1:
    for line in ax.lines:
        x_data = line.get_xdata()
        y_data = line.get_ydata()
        label = line.get_label()
        color = line.get_color()
        linestyle = line.get_linestyle()
        all_plot_data.append({'x': x_data, 'y': y_data, 'label': label, 'color': color, 'linestyle': linestyle, 'title': ax.get_title()})

# 提取 Figure 2 的数据
for ax in axes_from_fig2:
    for line in ax.lines:
        x_data = line.get_xdata()
        y_data = line.get_ydata()
        label = line.get_label()
        color = line.get_color()
        linestyle = line.get_linestyle()
        all_plot_data.append({'x': x_data, 'y': y_data, 'label': label, 'color': color, 'linestyle': linestyle, 'title': ax.get_title()})

print(f"共提取到 {len(all_plot_data)} 组绘图数据。")
登录后复制

2.3 创建新的主 Figure 和子图布局

现在,我们将创建一个新的 Figure 对象,并根据需要设置子图布局。例如,如果我们要将两个原始 Figure 合并为两个独立的子图,我们可以创建一个 1x2 或 2x1 的布局。这里我们选择 2x2 的布局,将 fig_1 的内容放在左上角,fig_2 的两个子图内容放在右上角和左下角,右下角留空。

# 创建一个新的主 Figure 和 Axes 布局
# 这里我们选择2行2列,以便放置所有提取的数据
fig_combined, axs = plt.subplots(2, 2, figsize=(12, 8))
axs = axs.flatten() # 将二维数组的 Axes 展平,方便索引
登录后复制

2.4 将数据重新绘制到新的子图上

遍历提取到的数据,并将其绘制到新创建的 Axes 对象上。我们需要确保数据被绘制到正确的子图位置。

# 确保有足够的子图来容纳所有数据
if len(all_plot_data) > len(axs):
    print("警告:新的子图数量不足以容纳所有提取的数据。")

# 将数据绘制到新的子图上
# 假设我们知道哪个数据对应哪个原始图,并希望按特定顺序放置
# 示例中,我们将Figure 1的唯一线条放在第一个子图
# Figure 2的两个线条分别放在第二个和第三个子图
# 第四个子图留空或用于其他目的

# Figure 1 的数据(只有一个Axes,一个line)
if len(all_plot_data) > 0:
    data_f1 = all_plot_data[0]
    axs[0].plot(data_f1['x'], data_f1['y'], label=data_f1['label'], color=data_f1['color'], linestyle=data_f1['linestyle'])
    axs[0].set_title(f"Combined: {data_f1['title']}")
    axs[0].legend()
    axs[0].grid(True)

# Figure 2 的第一个 Axes 的数据(一个line)
if len(all_plot_data) > 1:
    data_f2_ax1 = all_plot_data[1]
    axs[1].plot(data_f2_ax1['x'], data_f2_ax1['y'], label=data_f2_ax1['label'], color=data_f2_ax1['color'], linestyle=data_f2_ax1['linestyle'])
    axs[1].set_title(f"Combined: {data_f2_ax1['title']}")
    axs[1].legend()
    axs[1].grid(True)

# Figure 2 的第二个 Axes 的数据(一个line)
if len(all_plot_data) > 2:
    data_f2_ax2 = all_plot_data[2]
    axs[2].plot(data_f2_ax2['x'], data_f2_ax2['y'], label=data_f2_ax2['label'], color=data_f2_ax2['color'], linestyle=data_f2_ax2['linestyle'])
    axs[2].set_title(f"Combined: {data_f2_ax2['title']}")
    axs[2].legend()
    axs[2].grid(True)

# 可以选择性地隐藏或清空未使用的子图
axs[3].set_visible(False) # 隐藏第四个子图
# 或者 axs[3].axis('off')

# 调整布局,防止重叠
fig_combined.suptitle('Combined Matplotlib Figures', fontsize=16)
plt.tight_layout(rect=[0, 0.03, 1, 0.95]) # 调整布局,为suptitle留出空间
登录后复制

2.5 可选:配置新子图属性

在新子图上重新绘制数据后,您可以根据需要设置每个子图的标题、x/y 轴标签、图例、网格等属性,以增强可读性和信息量。这在上面的示例代码中已经有所体现。

2.6 保存最终合并图

完成所有绘制和配置后,使用 plt.savefig() 方法保存最终的合并图形。dpi 参数可以控制图像的清晰度。

# 保存最终的合并图形
plt.savefig("combined_matplotlib_figures.png", dpi=300)

# 显示合并后的图形
plt.show()

# 关闭所有 Figure,释放内存
plt.close('all')
登录后复制

3. 示例代码

以下是一个完整的示例代码,演示了如何将两个独立的 Matplotlib Figure 合并到一个新的 Figure 中。

import matplotlib.pyplot as plt
import numpy as np

# --- 模拟生成原始 Figure 的函数 ---
def generate_figure_1():
    """生成一个包含单条正弦曲线的 Figure。"""
    fig = plt.figure(figsize=(6, 4))
    ax = fig.add_subplot(111)
    x = np.linspace(0, 10, 100)
    y = np.sin(x)
    ax.plot(x, y, label='Sine Wave', color='blue', linestyle='-')
    ax.set_title('Original Figure 1: Sine Wave')
    ax.set_xlabel('X-axis')
    ax.set_ylabel('Y-axis')
    ax.legend()
    plt.close(fig) # 关闭原始 Figure,避免在合并前显示
    return fig

def generate_figure_2():
    """生成一个包含两个子图的 Figure,每个子图包含一条曲线。"""
    fig = plt.figure(figsize=(6, 8)) # 稍微高一点,因为有两个子图
    ax1 = fig.add_subplot(211) # 第一个子图
    ax2 = fig.add_subplot(212) # 第二个子图

    x = np.linspace(0, 10, 100)
    y1 = np.cos(x)
    y2 = np.exp(-x/2) * np.sin(5*x)

    ax1.plot(x, y1, 'r--', label='Cosine Wave')
    ax1.set_title('Original Figure 2, Subplot 1: Cosine Wave')
    ax1.set_xlabel('X-axis')
    ax1.set_ylabel('Y-axis')
    ax1.legend()
    ax1.grid(True)

    ax2.plot(x, y2, 'g:', label='Damped Sine Wave')
    ax2.set_title('Original Figure 2, Subplot 2: Damped Sine')
    ax2.set_xlabel('X-axis')
    ax2.set_ylabel('Y-axis')
    ax2.legend()
    ax2.grid(True)

    plt.tight_layout() # 调整子图布局
    plt.close(fig) # 关闭原始 Figure
    return fig

# --- 步骤 1: 获取源 Figure 对象并提取 Axes ---
fig_source_1 = generate_figure_1()
fig_source_2 = generate_figure_2()

axes_from_fig1 = fig_source_1.axes
axes_from_fig2 = fig_source_2.axes

# --- 步骤 2: 从 Axes 中提取绘图数据 ---
all_plot_data = []

# 提取 Figure 1 的数据
for ax in axes_from_fig1:
    for line in ax.lines:
        all_plot_data.append({
            'x': line.get_xdata(),
            'y': line.get_ydata(),
            'label': line.get_label(),
            'color': line.get_color(),
            'linestyle': line.get_linestyle(),
            'title': ax.get_title() # 记录原始子图标题
        })

# 提取 Figure 2 的数据
for ax in axes_from_fig2:
    for line in ax.lines:
        all_plot_data.append({
            'x': line.get_xdata(),
            'y': line.get_ydata(),
            'label': line.get_label(),
            'color': line.get_color(),
            'linestyle': line.get_linestyle(),
            'title': ax.get_title()
        })

# --- 步骤 3: 创建新的主 Figure 和子图布局 ---
# 我们有 1 + 2 = 3 组数据,所以创建一个 2x2 的布局,其中一个子图可能留空
fig_combined, axs = plt.subplots(2, 2, figsize=(14, 10))
axs = axs.flatten() # 将 Axes 数组展平为一维,方便索引

# --- 步骤 4: 将数据重新绘制到新的子图上 ---
# 确保有足够的子图来容纳所有数据
if len(all_plot_data) > len(axs):
    print("警告:新的子图数量不足以容纳所有提取的数据。")

for i, data_item in enumerate(all_plot_data):
    if i < len(axs): # 确保不越界
        current_ax = axs[i]
        current_ax.plot(data_item['x'], data_item['y'],
                        label=data_item['label'],
                        color=data_item['color'],
                        linestyle=data_item['linestyle'])
        current_ax.set_title(f"Combined Plot {i+1}: {data_item['title'].replace('Original ', '')}")
        current_ax.set_xlabel('X-axis')
        current_ax.set_ylabel('Y-axis')
        current_ax.legend()
        current_ax.grid(True)

# --- 步骤 5: 配置未使用的子图 (可选) ---
# 如果有未使用的子图,可以隐藏它们
for i in range(len(all_plot_data), len(axs)):
    axs[i].axis('off') # 隐藏未使用的子图的轴和边框

# --- 步骤 6: 调整整体布局和添加总标题 ---
fig_combined.suptitle('Combined Matplotlib Figures Tutorial', fontsize=18, y=1.02)
plt.tight_layout(rect=[0, 0.03, 1, 0.98]) # 调整布局,为总标题留出空间

# --- 步骤 7: 保存最终合并图并显示 ---
plt.savefig("combined_figures_tutorial.png", dpi=300, bbox_inches='tight')
plt.show()

# 关闭所有 Figure,释放内存
plt.close('all')
登录后复制

4. 注意事项与总结

  • 直接合并的局限性: Matplotlib 的 Figure 对象是独立的画布实例,它们之间没有直接的“合并” API。因此,数据提取和重绘是实现此目标的标准方法。
  • 数据提取的通用性: 本教程主要展示了线条图的数据提取。对于散点图(ax.collections)、条形图(ax.patches)、图像(ax.images)或其他复杂的 Artist 对象,需要根据其类型调用不同的方法来获取数据或属性。例如,散点图可能需要 scatter.get_offsets(),条形图可能需要 bar.get_height() 和 bar.get_x()。
  • 布局规划: 在创建新的主 Figure 时,提前规划好子图的布局(行数、列数)至关重要,以确保所有原始图的内容都能得到妥善的展示。
  • 图例和标签: 重新绘制数据时,务必将原始图

以上就是如何将多个 Matplotlib 图形合并到一个统一的图形中的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号