You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多进程生成AxesSubplot后合并绘制Matplotlib图形异常问题

问题分析与解决方案

一、多进程Matplotlib子图失效的核心原因

你遇到的问题本质是Matplotlib的Figure/Axes对象无法跨进程安全传递和修改。当你把Axes对象传给子进程时,实际上传递的是对象的序列化副本,子进程里对ax.pcolormesh的修改只会作用在这个副本上,主进程中的原始Figure完全感知不到这些变化——这就是为什么你能打印出QuadMesh对象,但最终保存的图是空的。

二、正确的多进程绘图方案

针对你的场景,有两种可行的优化思路,核心是避免跨进程传递Matplotlib对象:

方案1:多进程预处理数据,主进程统一绘图

如果pcolormesh的耗时主要来自数据计算(比如生成spectrogram的data矩阵),而非绘图渲染,那么可以把数据生成逻辑放到多进程,主进程拿到所有处理好的数据后,再一次性完成绘图。

示例代码:

import matplotlib.pyplot as plt
import numpy as np
from multiprocessing import Pool

# 把数据生成逻辑抽出来,放到多进程执行
def generate_spectrogram_data(size):
    data = np.arange(size * size).reshape((size, size))
    y = np.arange(0, 10)
    x = np.arange(0, 10)
    return (data, x, y)

def do_it_multiprocessed_data():
    size = 10
    num_subplots = 10  # 2行5列共10个子图
    
    # 多进程生成所有子图的数据
    with Pool(3) as p:
        # 这里可以传入不同的参数,生成不同的子图数据
        data_sets = p.map(generate_spectrogram_data, [size]*num_subplots)
    
    # 主进程统一绘图
    fig, axes = plt.subplots(nrows=2, ncols=5, figsize=(15,6))
    axes_list = [item for sublist in axes for item in sublist]
    
    for ax, (data, x, y) in zip(axes_list, data_sets):
        ax.pcolormesh(x, y, data)
    
    plt.tight_layout()
    plt.savefig("test_multiprocess_data.png")

if __name__ == "__main__":
    do_it_multiprocessed_data()

方案2:子进程独立绘制子图并保存为临时文件,主进程拼接

如果pcolormesh的耗时主要来自绘图渲染,可以让每个子进程独立绘制单个子图(创建自己的Figure/Axes),保存为临时PNG文件,然后主进程把这些PNG作为图像导入到总Figure的子图中。

示例代码:

import matplotlib.pyplot as plt
import numpy as np
from multiprocessing import Pool
import tempfile
import os
from functools import partial

def draw_single_subplot(temp_path, size, idx):
    # 子进程创建独立的Figure和Axes
    fig, ax = plt.subplots(figsize=(3,3))
    data = np.arange(size * size).reshape((size, size))
    y = np.arange(0, 10)
    x = np.arange(0, 10)
    ax.pcolormesh(x, y, data)
    # 保存到临时文件
    fig.savefig(os.path.join(temp_path, f"subplot_{idx}.png"))
    plt.close(fig)  # 必须关闭,避免内存泄漏
    return f"subplot_{idx}.png"

def do_it_multiprocessed_render():
    size = 10
    num_subplots = 10
    
    # 创建临时目录存储子图
    with tempfile.TemporaryDirectory() as temp_dir:
        with Pool(3) as p:
            # 传入临时目录路径、size和子图索引
            func = partial(draw_single_subplot, temp_dir, size)
            subplot_files = p.map(func, range(num_subplots))
        
        # 主进程拼接子图
        fig, axes = plt.subplots(nrows=2, ncols=5, figsize=(15,6))
        axes_list = [item for sublist in axes for item in sublist]
        
        for ax, file_name in zip(axes_list, subplot_files):
            img = plt.imread(os.path.join(temp_dir, file_name))
            ax.imshow(img)
            ax.axis('off')  # 关闭子图的坐标轴,因为已经在子图里画过了
        
        plt.tight_layout()
        plt.savefig("test_multiprocess_render.png")

if __name__ == "__main__":
    do_it_multiprocessed_render()

三、关于imshow的y轴对数刻度问题

你提到用imshow遇到y轴对数刻度的问题,这里给出解决方法:

imshow默认使用像素坐标作为坐标轴,要实现对数刻度,需要:

  1. 用extent参数指定数据的实际x/y范围
  2. 设置y轴为对数刻度
  3. 调整坐标轴的原点(imshow默认origin='upper',而对数轴通常是从下到上递增,所以建议设置origin='lower')

示例代码:

import matplotlib.pyplot as plt
import numpy as np

size = 10
data = np.arange(size * size).reshape((size, size))
# 假设y轴是对数范围,比如从1到100
y = np.logspace(0, 2, size)
x = np.arange(0, 10)

fig, ax = plt.subplots()
# extent参数格式:[xmin, xmax, ymin, ymax]
ax.imshow(data, extent=[x.min(), x.max(), y.min(), y.max()], origin='lower', aspect='auto')
ax.set_yscale('log')
# 手动设置刻度标签,让显示更友好
ax.set_yticks([1, 10, 100])
ax.set_yticklabels(['1', '10', '100'])

plt.savefig("imshow_log_y.png")

这样就能在imshow中实现正确的y轴对数刻度了。

内容的提问来源于stack exchange,提问作者n0n0bstan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 06:42:39