为什么绘制Matplotlib这么慢呢?
我目前正在评估不同的Python绘图库。 现在我正在尝试matplotlib,我对这个performance感到非常失望。 下面的例子是从SciPy例子中修改的,只给了我每秒〜8帧!
任何加快速度的方法,或者我应该select一个不同的阴谋库?
from pylab import * import time ion() fig = figure() ax1 = fig.add_subplot(611) ax2 = fig.add_subplot(612) ax3 = fig.add_subplot(613) ax4 = fig.add_subplot(614) ax5 = fig.add_subplot(615) ax6 = fig.add_subplot(616) x = arange(0,2*pi,0.01) y = sin(x) line1, = ax1.plot(x, y, 'r-') line2, = ax2.plot(x, y, 'g-') line3, = ax3.plot(x, y, 'y-') line4, = ax4.plot(x, y, 'm-') line5, = ax5.plot(x, y, 'k-') line6, = ax6.plot(x, y, 'p-') # turn off interactive plotting - speeds things up by 1 Frame / second plt.ioff() tstart = time.time() # for profiling for i in arange(1, 200): line1.set_ydata(sin(x+i/10.0)) # update the data line2.set_ydata(sin(2*x+i/10.0)) line3.set_ydata(sin(3*x+i/10.0)) line4.set_ydata(sin(4*x+i/10.0)) line5.set_ydata(sin(5*x+i/10.0)) line6.set_ydata(sin(6*x+i/10.0)) draw() # redraw the canvas print 'FPS:' , 200/(time.time()-tstart)
首先,(尽pipe这根本不会改变性能)考虑清理你的代码,类似这样:
import matplotlib.pyplot as plt import numpy as np import time x = np.arange(0, 2*np.pi, 0.01) y = np.sin(x) fig, axes = plt.subplots(nrows=6) styles = ['r-', 'g-', 'y-', 'm-', 'k-', 'c-'] lines = [ax.plot(x, y, style)[0] for ax, style in zip(axes, styles)] fig.show() tstart = time.time() for i in xrange(1, 20): for j, line in enumerate(lines, start=1): line.set_ydata(np.sin(j*x + i/10.0)) fig.canvas.draw() print 'FPS:' , 20/(time.time()-tstart)
以上面的例子,我得到了10fps左右。
只是一个快速的说明,根据你的具体用例,matplotlib可能不是一个好的select。 它面向出版品质的数字,而不是实时显示。
但是,为了加速这个例子,你可以做很多事情。
有两个主要的原因,这是为什么这样慢。
1)调用fig.canvas.draw()
重绘一切 。 这是你的瓶颈。 在你的情况下,你不需要重新绘制轴边界,刻度标签等等。
2)在你的情况下,有很多的小插曲有很多刻度标签。 这些花费很长时间。
这两个可以通过使用blitting来修复。
要有效地进行blitting,你必须使用后端特定的代码。 实际上,如果你真的担心stream畅的animation效果,那么通常你会将matplotlib图块embedded到某种GUI工具箱中,所以这不是什么问题。
但是,如果不了解更多关于你在做什么,我不能帮你。
尽pipe如此,还是有一个中立的做法,这个方法还是相当快的。
import matplotlib.pyplot as plt import numpy as np import time x = np.arange(0, 2*np.pi, 0.1) y = np.sin(x) fig, axes = plt.subplots(nrows=6) fig.show() # We need to draw the canvas before we start animating... fig.canvas.draw() styles = ['r-', 'g-', 'y-', 'm-', 'k-', 'c-'] def plot(ax, style): return ax.plot(x, y, style, animated=True)[0] lines = [plot(ax, style) for ax, style in zip(axes, styles)] # Let's capture the background of the figure backgrounds = [fig.canvas.copy_from_bbox(ax.bbox) for ax in axes] tstart = time.time() for i in xrange(1, 2000): items = enumerate(zip(lines, axes, backgrounds), start=1) for j, (line, ax, background) in items: fig.canvas.restore_region(background) line.set_ydata(np.sin(j*x + i/10.0)) ax.draw_artist(line) fig.canvas.blit(ax.bbox) print 'FPS:' , 2000/(time.time()-tstart)
这给了我〜200fps。
为了使这个更方便一些,最近版本的matplotlib中有一个animations
模块。
举个例子:
import matplotlib.pyplot as plt import matplotlib.animation as animation import numpy as np x = np.arange(0, 2*np.pi, 0.1) y = np.sin(x) fig, axes = plt.subplots(nrows=6) styles = ['r-', 'g-', 'y-', 'm-', 'k-', 'c-'] def plot(ax, style): return ax.plot(x, y, style, animated=True)[0] lines = [plot(ax, style) for ax, style in zip(axes, styles)] def animate(i): for j, line in enumerate(lines, start=1): line.set_ydata(np.sin(j*x + i/10.0)) return lines # We'd normally specify a reasonable "interval" here... ani = animation.FuncAnimation(fig, animate, xrange(1, 200), interval=0, blit=True) plt.show()
Matplotlib制作出色的出版品质的graphics,但是对于速度并没有很好的优化。 有各种各样的python绘图包,devise时考虑到速度:
- http://pyqwt.sourceforge.net/
[编辑:pyqwt不再维护; 以前的维护者正在推荐pyqtgraph] - http://code.google.com/p/guiqwt/
- http://code.enthought.com/projects/chaco/
- http://www.pyqtgraph.org/
首先, 乔·金顿的回答提供了非常好的build议,使用一种中立的方法,你一定要听取他的build议(特别是关于Blitting)并将其付诸实践。 有关此方法的更多信息,请阅读Matplotlib Cookbook
然而,非GUI中立(GUI偏向?)方法是加速绘图的关键。 换句话说, 后端对绘制速度非常重要。
在从matplotlib导入任何其他内容之前,先放置这两行:
import matplotlib matplotlib.use('GTKAgg')
当然,有不同的select可以使用,而不是GTKAgg
,但根据之前提到的食谱,这是最快的。 查看关于后端的链接以获取更多选项。
对于Joe Kington (.copy_from_bbox&.draw_artist&canvas.blit)提出的第一个解决scheme,我必须在fig.canvas.draw()行之后捕获背景,否则背景没有任何效果,而且得到的结果与你提到。 如果你把它放在fig.show()之后,它仍然不能像Michael Browne所build议的那样工作。
所以只需在canvas.draw() 之后放置背景线:
[...] fig.show() # We need to draw the canvas before we start animating... fig.canvas.draw() # Let's capture the background of the figure backgrounds = [fig.canvas.copy_from_bbox(ax.bbox) for ax in axes]
这可能不适用于你们许多人,但我通常在Linux下操作我的电脑,所以默认情况下,我将我的matplotlib图保存为PNG和SVG。 这在Linux下工作正常,但在我的Windows 7安装[MiKTeX在Python(x,y)或Anaconda]下运行速度令人难以忍受,所以我采取了添加此代码,并在那里再次正常工作:
import platform # Don't save as SVG if running under Windows. # # Plot code goes here. # fig.savefig('figure_name.png', dpi = 200) if platform.system() != 'Windows': # In my installations of Windows 7, it takes an inordinate amount of time to save # graphs as .svg files, so on that platform I've disabled the call that does so. # The first run of a script is still a little slow while everything is loaded in, # but execution times of subsequent runs are improved immensely. fig.savefig('figure_name.svg')