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

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

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

1. 理解问题与核心策略

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

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

获取源 Figure 对象及其内部的 Axes 对象。从每个 Axes 对象中提取其所绘制的数据。 这通常包括线条的 x、y 坐标,散点的坐标,条形图的高度等。创建一个全新的主 Figure。在新主 Figure 中创建适当数量的子图(Axes 对象), 形成所需的布局。将从源 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 pltimport 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.axesaxes_from_fig2 = fig_2.axesprint(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 合并为两个独立的子图,我们可以创建一个 1×2 或 2×1 的布局。这里我们选择 2×2 的布局,将 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 pltimport 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 figdef 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.axesaxes_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 图形合并到一个统一的图形中的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/594789.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年11月10日 17:38:09
下一篇 2025年11月10日 17:42:12

相关推荐

  • 如何解决本地图片在使用 mask JS 库时出现的跨域错误?

    如何跨越localhost使用本地图片? 问题: 在本地使用mask js库时,引入本地图片会报跨域错误。 解决方案: 要解决此问题,需要使用本地服务器启动文件,以http或https协议访问图片,而不是使用file://协议。例如: python -m http.server 8000 然后,可以…

    2025年12月24日
    200
  • 旋转长方形后,如何计算其相对于画布左上角的轴距?

    绘制长方形并旋转,计算旋转后轴距 在拥有 1920×1080 画布中,放置一个宽高为 200×20 的长方形,其坐标位于 (100, 100)。当以任意角度旋转长方形时,如何计算它相对于画布左上角的 x、y 轴距? 以下代码提供了一个计算旋转后长方形轴距的解决方案: const x = 200;co…

    2025年12月24日
    000
  • 旋转长方形后,如何计算它与画布左上角的xy轴距?

    旋转后长方形在画布上的xy轴距计算 在画布中添加一个长方形,并将其旋转任意角度,如何计算旋转后的长方形与画布左上角之间的xy轴距? 问题分解: 要计算旋转后长方形的xy轴距,需要考虑旋转对长方形宽高和位置的影响。首先,旋转会改变长方形的长和宽,其次,旋转会改变长方形的中心点位置。 求解方法: 计算旋…

    2025年12月24日
    000
  • 旋转长方形后如何计算其在画布上的轴距?

    旋转长方形后计算轴距 假设长方形的宽、高分别为 200 和 20,初始坐标为 (100, 100),我们将它旋转一个任意角度。根据旋转矩阵公式,旋转后的新坐标 (x’, y’) 可以通过以下公式计算: x’ = x * cos(θ) – y * sin(θ)y’ = x * …

    2025年12月24日
    000
  • 如何计算旋转后长方形在画布上的轴距?

    旋转后长方形与画布轴距计算 在给定的画布中,有一个长方形,在随机旋转一定角度后,如何计算其在画布上的轴距,即距离左上角的距离? 以下提供一种计算长方形相对于画布左上角的新轴距的方法: const x = 200; // 初始 x 坐标const y = 90; // 初始 y 坐标const w =…

    2025年12月24日
    200
  • CSS元素设置em和transition后,为何载入页面无放大效果?

    css元素设置em和transition后,为何载入无放大效果 很多开发者在设置了em和transition后,却发现元素载入页面时无放大效果。本文将解答这一问题。 原问题:在视频演示中,将元素设置如下,载入页面会有放大效果。然而,在个人尝试中,并未出现该效果。这是由于macos和windows系统…

    2025年12月24日
    200
  • 如何计算旋转后的长方形在画布上的 XY 轴距?

    旋转长方形后计算其画布xy轴距 在创建的画布上添加了一个长方形,并提供其宽、高和初始坐标。为了视觉化旋转效果,还提供了一些旋转特定角度后的图片。 问题是如何计算任意角度旋转后,这个长方形的xy轴距。这涉及到使用三角学来计算旋转后的坐标。 以下是一个 javascript 代码示例,用于计算旋转后长方…

    2025年12月24日
    000
  • 使用 Mask 导入本地图片时,如何解决跨域问题?

    跨域疑难:如何解决 mask 引入本地图片产生的跨域问题? 在使用 mask 导入本地图片时,你可能会遇到令人沮丧的跨域错误。为什么会出现跨域问题呢?让我们深入了解一下: mask 框架假设你以 http(s) 协议加载你的 html 文件,而当使用 file:// 协议打开本地文件时,就会产生跨域…

    2025年12月24日
    200
  • 移动端rem计算导致页面扭曲变动如何解决?

    解决移动端rem计算导致页面扭曲变动的问题 在移动端项目中使用rem作为根节点字体大小的计算方式时,可能会遇到页面首次打开时出现css扭曲变动的现象。这是因为根节点字体大小赋值后,会导致页面内容重绘。 解决方法: 将计算根节点字体大小的js代码移动到页面的最开头,放置在 标签内。 原理: 这样做可以…

    2025年12月24日
    200
  • Nuxt 移动端项目中 rem 计算导致 CSS 变形,如何解决?

    Nuxt 移动端项目中解决 rem 计算导致 CSS 变形 在 Nuxt 移动端项目中使用 rem 计算根节点字体大小时,可能会遇到一个问题:页面内容在字体大小发生变化时会重绘,导致 CSS 变形。 解决方案: 可将计算根节点字体大小的 JS 代码块置于页面最前端的 标签内,确保在其他资源加载之前执…

    2025年12月24日
    200
  • Nuxt 移动端项目使用 rem 计算字体大小导致页面变形,如何解决?

    rem 计算导致移动端页面变形的解决方法 在 nuxt 移动端项目中使用 rem 计算根节点字体大小时,页面会发生内容重绘,导致页面打开时出现样式变形。如何避免这种现象? 解决方案: 移动根节点字体大小计算代码到页面顶部,即 head 中。 原理: flexível.js 也遇到了类似问题,它的解决…

    2025年12月24日
    000
  • 如何避免使用rem计算造成页面变形?

    避免rem计算造成页面变形 在使用rem计算根节点字体大小时,可能会遇到页面在第一次打开时出现css扭曲变动的现象。这是因为在浏览器运行到计算根节点字体大小的代码时,页面内容已经开始展示,随后根节点字体大小的赋值操作会导致页面内容重绘,从而产生变形效果。 要避免这种情况,可以在页面的最前面,也就是h…

    2025年12月24日
    000
  • 网页布局中,使用 translate 转换元素位置的优势有哪些?

    为什么考虑使用 translate 而非定位属性更改元素位置 在网页布局中,我们通常使用元素的定位属性(如 left、right、top、bottom)来控制元素在文档流中的位置。然而,在某些情况下,我们可能考虑使用 translate 转换来改变元素位置。 使用 translate 的优势: 不会…

    2025年12月24日
    000
  • 为什么使用 `translate` 比修改定位改变元素位置更有效?

    为什么使用 translate 而不是修改定位来改变元素位置? 在某些情况下,使用 translate 而不是修改元素的定位来改变其位置更具优势。 原因如下: 减少重绘和重排:改变 transform 不会触发重排或重绘,只会触发复合。而修改元素定位可能会触发重排,代价更高。动画更平滑:使用 tra…

    2025年12月24日
    000
  • 浮动元素修改宽高,是否会触发布局调整?

    浮动元素自有其渲染之法,修改宽高影响布局否? 浮动元素的存在使文本内容对其环绕,倘若对其宽高频繁修改,是否会触发大规模的布局调整? 让我们从分层与渲染视角着手,进一步探究问题的答案。 从分层来看,浮动元素与其相邻元素处于同一层级。而从渲染角度观察,图像的绘制(paint)可被称作重绘,布局(layo…

    2025年12月24日
    000
  • 修改浮动元素宽高会触发重排吗?

    修改浮动元素宽高后是否会触发重排 众所周知,浮动元素会影响与其相邻文本内容的位置。那么,如果对一个浮动元素反复修改其宽高,会否引发大规模重排呢? 根据浏览器的分层机制和渲染流程,浮动元素与其相邻元素位于同一层。在分层渲染中,”paint”对应重绘,”layout&…

    2025年12月24日
    200
  • 反复修改浮动元素宽高会触发重排吗?

    修改浮动元素宽高对重排的影响 众所周知,当浮动元素出现时,相邻文本内容会环绕其排列。那么,反复修改浮动元素的宽高是否会触发重排呢? 影响布局,重排是必然 从渲染模型的角度来看,修改浮动元素的宽高将影响其布局,因为这改变了元素在文档流中的位置。具体来说,浮动元素的宽高修改将触发布局重排(layout)…

    2025年12月24日
    000
  • 修改浮动图片元素的宽高会触发重排吗?

    对浮动元素修改宽高的操作是否会触发重排 众所周知,设置浮动属性的图片元素会使相邻文本内容在其周围环绕。那么,如果对这样的图片元素反复修改宽高,是否会出现大规模的重排呢?答案是肯定的。 原因如下: 布局层级影响 从布局层级来看,浮动的图片元素与相邻文本内容处于同一层级。当修改图片元素的宽高时,相邻文本…

    2025年12月24日
    400
  • 正则表达式在文本验证中的常见问题有哪些?

    正则表达式助力文本输入验证 在文本输入框的验证中,经常遇到需要限定输入内容的情况。例如,输入框只能输入整数,第一位可以为负号。对于不会使用正则表达式的人来说,这可能是个难题。下面我们将提供三种正则表达式,分别满足不同的验证要求。 1. 可选负号,任意数量数字 如果输入框中允许第一位为负号,后面可输入…

    2025年12月24日
    000
  • 如何在 VS Code 中解决折叠代码复制问题?

    解决 VS Code 折叠代码复制问题 在 VS Code 中使用折叠功能可以帮助组织长代码,但使用复制功能时,可能会遇到只复制可见部分的问题。以下是如何解决此问题: 当代码被折叠时,可以使用以下简单操作复制整个折叠代码: 按下 Ctrl + C (Windows/Linux) 或 Cmd + C …

    2025年12月24日
    000

发表回复

登录后才能评论
关注微信