Matplotlib,有哪些替代savefig()的方式来提高保存CString对象的性能?

22 投票
2 回答
15982 浏览
提问于 2025-04-16 14:11

我想加快把我的图表保存为图片的过程。目前,我是创建一个cString对象,然后用savefig把图表保存到这个对象里。但是,我真的非常希望能找到更好的方法来保存这个图片。因为我需要做这个操作很多次,而savefig命令非常慢;肯定有更好的办法。我听说过可以把它保存为未压缩的原始图片,但我不知道怎么做。如果能换成其他更快的方式,我也不在乎agg。

也就是说:

RAM = cStringIO.StringIO()

CHART = plt.figure(.... 
**code for creating my chart**

CHART.savefig(RAM, format='png')

我一直在使用matplotlib和FigureCanvasAgg这个后端。

谢谢!

2 个回答

3

我也需要快速生成很多图表。我发现使用多进程可以提高绘图的速度,尤其是当可用的核心数量增加时。例如,如果在一个进程中生成100个图表需要10秒钟,那么如果把这个任务分配到4个核心上,大约只需要3秒钟。

40

如果你只是想要一个原始的缓冲区,可以试试 fig.canvas.print_rgbfig.canvas.print_raw 等等(这两者的区别在于 raw 是 rgba 格式,而 rgb 是 rgb 格式。还有 print_pngprint_ps 等等)。

这样做会使用 fig.dpi 的值,而不是 savefig 默认的 dpi 值(100 dpi)。不过,即使比较 fig.canvas.print_raw(f)fig.savefig(f, format='raw', dpi=fig.dpi)print_canvas 的版本也只是 稍微快一点 不明显快,因为它不需要像 savefig 默认那样重置坐标轴的颜色等。

不过,大部分时间花在以原始格式保存图形上,实际上就是在绘制图形,这个是没办法避免的。

总之,作为一个无聊但有趣的例子,可以看看下面的内容:

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

plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111)
num = 50
max_dim = 10
x = max_dim / 2 * np.ones(num)
s, c = 100 * np.random.random(num), np.random.random(num)
scat = ax.scatter(x,x,s,c)
ax.axis([0,max_dim,0,max_dim])
ax.set_autoscale_on(False)

for i in xrange(1000):
    xy = np.random.random(2*num).reshape(num,2) - 0.5
    offsets = scat.get_offsets() + 0.3 * xy
    offsets.clip(0, max_dim, offsets)
    scat.set_offsets(offsets)
    scat._sizes += 30 * (np.random.random(num) - 0.5)
    scat._sizes.clip(1, 300, scat._sizes)
    fig.canvas.draw()

布朗运动动画

如果我们看一下原始绘制时间:

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

fig = plt.figure()
ax = fig.add_subplot(111)
num = 50
max_dim = 10
x = max_dim / 2 * np.ones(num)
s, c = 100 * np.random.random(num), np.random.random(num)
scat = ax.scatter(x,x,s,c)
ax.axis([0,max_dim,0,max_dim])
ax.set_autoscale_on(False)

for i in xrange(1000):
    xy = np.random.random(2*num).reshape(num,2) - 0.5
    offsets = scat.get_offsets() + 0.3 * xy
    offsets.clip(0, max_dim, offsets)
    scat.set_offsets(offsets)
    scat._sizes += 30 * (np.random.random(num) - 0.5)
    scat._sizes.clip(1, 300, scat._sizes)
    fig.canvas.draw()

在我的机器上,这大约需要 25 秒。

如果我们把一个原始的 RGBA 缓冲区直接放到 cStringIO 缓冲区中,实际上会稍微快一点,大约 22 秒(这只有在我使用交互式后端时才成立!否则会是一样的):

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

fig = plt.figure()
ax = fig.add_subplot(111)
num = 50
max_dim = 10
x = max_dim / 2 * np.ones(num)
s, c = 100 * np.random.random(num), np.random.random(num)
scat = ax.scatter(x,x,s,c)
ax.axis([0,max_dim,0,max_dim])
ax.set_autoscale_on(False)

for i in xrange(1000):
    xy = np.random.random(2*num).reshape(num,2) - 0.5
    offsets = scat.get_offsets() + 0.3 * xy
    offsets.clip(0, max_dim, offsets)
    scat.set_offsets(offsets)
    scat._sizes += 30 * (np.random.random(num) - 0.5)
    scat._sizes.clip(1, 300, scat._sizes)
    ram = cStringIO.StringIO()
    fig.canvas.print_raw(ram)
    ram.close()

如果我们把这个和使用 savefig,并设置相同的 dpi 来比较:

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

fig = plt.figure()
ax = fig.add_subplot(111)
num = 50
max_dim = 10
x = max_dim / 2 * np.ones(num)
s, c = 100 * np.random.random(num), np.random.random(num)
scat = ax.scatter(x,x,s,c)
ax.axis([0,max_dim,0,max_dim])
ax.set_autoscale_on(False)

for i in xrange(1000):
    xy = np.random.random(2*num).reshape(num,2) - 0.5
    offsets = scat.get_offsets() + 0.3 * xy
    offsets.clip(0, max_dim, offsets)
    scat.set_offsets(offsets)
    scat._sizes += 30 * (np.random.random(num) - 0.5)
    scat._sizes.clip(1, 300, scat._sizes)
    ram = cStringIO.StringIO()
    fig.savefig(ram, format='raw', dpi=fig.dpi)
    ram.close()

这大约需要 23.5 秒。基本上,savefig 只是设置了一些默认参数,然后调用 print_raw,所以差别不大。

现在,如果我们把原始图像格式和压缩图像格式(比如 png)进行比较,就会看到更明显的差别:

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

fig = plt.figure()
ax = fig.add_subplot(111)
num = 50
max_dim = 10
x = max_dim / 2 * np.ones(num)
s, c = 100 * np.random.random(num), np.random.random(num)
scat = ax.scatter(x,x,s,c)
ax.axis([0,max_dim,0,max_dim])
ax.set_autoscale_on(False)

for i in xrange(1000):
    xy = np.random.random(2*num).reshape(num,2) - 0.5
    offsets = scat.get_offsets() + 0.3 * xy
    offsets.clip(0, max_dim, offsets)
    scat.set_offsets(offsets)
    scat._sizes += 30 * (np.random.random(num) - 0.5)
    scat._sizes.clip(1, 300, scat._sizes)
    ram = cStringIO.StringIO()
    fig.canvas.print_png(ram)
    ram.close()

这大约需要 52 秒!显然,压缩图像会有很多额外的开销。

总之,这可能是一个不必要复杂的例子……我想我只是想避免真正的工作……

撰写回答