Matplotlib,有哪些替代savefig()的方式来提高保存CString对象的性能?
我想加快把我的图表保存为图片的过程。目前,我是创建一个cString对象,然后用savefig把图表保存到这个对象里。但是,我真的非常希望能找到更好的方法来保存这个图片。因为我需要做这个操作很多次,而savefig命令非常慢;肯定有更好的办法。我听说过可以把它保存为未压缩的原始图片,但我不知道怎么做。如果能换成其他更快的方式,我也不在乎agg。
也就是说:
RAM = cStringIO.StringIO()
CHART = plt.figure(....
**code for creating my chart**
CHART.savefig(RAM, format='png')
我一直在使用matplotlib和FigureCanvasAgg这个后端。
谢谢!
2 个回答
我也需要快速生成很多图表。我发现使用多进程可以提高绘图的速度,尤其是当可用的核心数量增加时。例如,如果在一个进程中生成100个图表需要10秒钟,那么如果把这个任务分配到4个核心上,大约只需要3秒钟。
如果你只是想要一个原始的缓冲区,可以试试 fig.canvas.print_rgb
、fig.canvas.print_raw
等等(这两者的区别在于 raw
是 rgba 格式,而 rgb
是 rgb 格式。还有 print_png
、print_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 秒!显然,压缩图像会有很多额外的开销。
总之,这可能是一个不必要复杂的例子……我想我只是想避免真正的工作……