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

如何在Julia多进程环境中创建子图并汇总结果?

分布式绘图:子图共享与汇总方案

问题背景

之前给每个进程单独创建图,但图数量过多不便对比,希望将它们排列为子图。

首次尝试与问题

尝试通过pmap让每个进程绘制一个子图,代码如下:

using Distributed
@everywhere using PyPlot

fig, ax = plt.subplots(1, 4)
pmap(i-> begin
ax[i].scatter([i, 2], [3, 4])
end, 1:4)

fig.savefig("test.png", transparent=false)

结果生成空的子图,原因是ax对象会被复制到每个进程,进程间无法共享该实例,修改的都是各自的副本而非主进程的子图对象。

尝试SharedArray共享的错误

试图用SharedArrays共享ax:

using SharedArrays
convert(SharedArray, ax)

触发错误:

ArgumentError: type of SharedArray elements must be bits types, got PyCall.PyObject

目前已采用的可行方案是:将绘图操作移出进程,先在进程中收集数据,再统一绘图。但好奇是否存在方法让每个进程创建图/子图后汇总到一起。


解答

为何直接共享Axes对象不可行

PyPlot的Axes是PyCall封装的Python对象,属于非bits类型,而SharedArray仅支持存储bits类型(可直接序列化到内存的简单类型),因此无法用SharedArray共享这类对象。此外,PyPlot的绘图上下文是进程本地的,跨进程操作会因Python的GIL(全局解释器锁)和进程状态隔离失效,这也是首次尝试生成空图的核心原因。

进程创建子图后汇总的可行思路

如果一定要实现进程绘制子图再汇总,有两种方向:

1. 进程生成图像数据,主进程拼接

每个进程单独创建绘图、完成绘制后,将图像的像素数据传回主进程,再由主进程拼接成子图布局。示例代码:

using Distributed, PyPlot, PyCall
@everywhere using PyPlot, PyCall

@everywhere function plot_subplot(i)
    fig, ax = plt.subplots()
    ax.scatter([i, 2], [3, 4])
    # 获取图像像素数组
    fig.canvas.draw()
    img_data = pycall(fig.canvas.tostring_rgb, PyCall.PyObject)
    img_arr = reshape(PyCall.Array(img_data), fig.canvas.get_width_height()..., 3)
    plt.close(fig) # 释放进程内的绘图资源
    return img_arr
end

# 收集所有进程的图像数据
subplot_imgs = pmap(plot_subplot, 1:4)

# 主进程拼接成子图
fig, axs = plt.subplots(1, 4, figsize=(12,3))
for (idx, img) in enumerate(subplot_imgs)
    axs[idx].imshow(img)
    axs[idx].axis("off") # 隐藏原坐标轴(图像已包含)
end
fig.savefig("combined_subplots.png")

缺点:生成的是像素图而非矢量图,放大后会模糊,且子图的坐标轴样式难以统一。

2. 使用纯Julia绘图库(如Makie)

Makie这类纯Julia绘图库的对象是原生Julia类型,支持跨进程传递绘图元素的描述信息(而非直接传递绘图对象),主进程可接收后组装成子图布局。但需要熟悉Makie的布局系统,与PyPlot的使用思路差异较大。

更推荐的方案:先收数据再绘图

你目前采用的「进程收集数据,主进程统一绘图」是最稳妥的方案,既能保证绘图样式(坐标轴、图例等)的一致性,又能避免跨进程共享绘图对象的各种问题。示例代码:

using Distributed, PyPlot
@everywhere function generate_plot_data(i)
    # 模拟进程内生成绘图数据
    x = [i, 2]
    y = [3, 4]
    return (x, y)
end

# 从所有进程收集数据
data_collection = pmap(generate_plot_data, 1:4)

# 主进程统一绘制子图
fig, axs = plt.subplots(1, 4, figsize=(12,3))
for (idx, (x, y)) in enumerate(data_collection)
    axs[idx].scatter(x, y)
    axs[idx].set_title("Subplot $idx")
end
fig.tight_layout()
fig.savefig("unified_subplots.png")

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 11:23:18