matplotlib的savefig是线程安全的吗?

3 投票
1 回答
2236 浏览
提问于 2025-04-17 14:04

我有一个内部使用的分布式计算库,我们经常用它来进行并行计算。在处理过程中,数据会被分成不同的部分,然后每个部分会加载数据、进行计算,最后会有一个“保存”的步骤。通常,这个步骤是把数据写入数据库表中。

不过,对于一个特定的任务,我需要每个处理的输出是一个包含数据图表的 .png 文件。总共有 95 个处理,所以我需要 95 个 .png 文件。

在我的“保存”步骤中(每个处理都会执行这个步骤),我有一些非常简单的代码,它使用 matplotlib 的 boxplot 函数来制作箱线图,并且用 savefig 函数把图保存为一个 .png 文件,文件名是根据该处理使用的特定数据生成的独特名称。

然而,我偶尔会看到输出结果,似乎有两个或多个数据集被写入了同一个输出文件,尽管它们的名字是独特的。

matplotlib 在制作箱线图或保存图形时是否使用临时文件保存?如果是的话,它是否总是使用相同的临时文件名(这样就可能导致覆盖冲突)?我用 strace 运行了我的处理,但没有看到明显的来自 matplotlib 的临时文件写入。

我该如何确保这个过程是线程安全的?我确实希望能够并行进行文件保存,因为我想大幅增加输出的 .png 文件数量,所以先存储所有数据再逐个执行绘图/保存的方式是非常不理想的。

我无法重现我们使用的完整并行基础设施,但下面是用于创建图形句柄的函数,以及用于保存图形的函数。为了问题的讨论,你可以假设线程安全与我们的分布式库无关。我们知道问题不在我们的代码上,因为我们的代码已经使用多年进行多进程工作,没有出现过这样的线程问题(尤其是对于我们无法直接控制的东西,比如 matplotlib 的临时文件)。

import pandas
import numpy as np
import matplotlib.pyplot as plt

def plot_category_data(betas, category_name):
    """
    Function to organize beta data by date into vectors and pass to box plot
    code for producing a single chart of multi-period box plots.
    """
    beta_vector_list = []
    yms = np.sort(betas.yearmonth.unique())
    for ym in yms:
        beta_vector_list.append(betas[betas.yearmonth==ym].Beta.values.flatten().tolist())
    ###

    plot_output = plt.boxplot(beta_vector_list)
    axs = plt.gcf().gca()
    axs.set_xticklabels(betas.FactorDate.unique(), rotation=40, horizontalalignment='right')
    axs.set_xlabel("Date")
    axs.set_ylabel("Beta")
    axs.set_title("%s Beta to BMI Global"%(category_name))
    axs.set_ylim((-1.0, 3.0))

    return plot_output
### End plot_category_data

def save(self):
    """
    Make calls to store the plot to the desired output file.
    """
    out_file = self.output_path + "%s.png"%(self.category_name)
    fig = plt.gcf()
    fig.set_figheight(6.5)
    fig.set_figwidth(10)
    fig.savefig(out_file, bbox_inches='tight', dpi=150)
    print "Finished and stored output file %s"%(out_file)
    return None
### End save

1 个回答

1

在你的两个函数里,你都在用 plt.gcf()。我建议你每次绘图的时候都用 plt.figure() 来生成一个新的图形,并且明确地引用这个新的图形,这样就能完全避免这个问题了。

撰写回答