为什么使用Matplotlib绘图这么慢?
我现在正在评估不同的Python绘图库。目前我在尝试使用matplotlib,但对它的性能感到有些失望。下面这个例子是从SciPy的示例修改而来的,结果我只能得到大约每秒8帧的速度!
有没有什么方法可以加快这个速度,或者我应该换一个绘图库呢?
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)
5 个回答
首先,Joe Kington的回答给出了很好的建议,他的方法不依赖于任何特定的图形用户界面(GUI),你一定要听他的建议(特别是关于Blitting的部分),并付诸实践。如果想了解更多这种方法的信息,可以查看Matplotlib Cookbook。
不过,非GUI中立(可能偏向某种GUI?)的方法对于加快绘图速度非常关键。换句话说,后端对绘图速度来说是极其重要的。
在你从matplotlib导入其他任何东西之前,先加上这两行:
import matplotlib
matplotlib.use('GTKAgg')
当然,除了GTKAgg
,还有其他各种选项可以使用,但根据之前提到的食谱,这个是最快的。想了解更多后端的选项,可以查看相关链接。
Matplotlib可以制作出很棒的高质量图形,但在速度上并不是特别优秀。
有一些专门为速度优化的Python绘图包,你可以试试:
- http://vispy.org
- http://pyqtgraph.org/
- http://docs.enthought.com/chaco/
- http://pyqwt.sourceforge.net/
[ 编辑:pyqwt现在不再维护;之前的维护者推荐使用pyqtgraph ] - http://code.google.com/p/guiqwt/
首先,虽然这样做不会改变性能,但可以考虑整理一下你的代码,像这样:
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)
根据上面的例子,我大约能达到10帧每秒。
顺便提一下,根据你的具体使用情况,matplotlib可能不是一个很好的选择。它更适合制作出版质量的图形,而不是实时显示。
不过,有很多方法可以加快这个例子的速度。
造成这个速度慢的主要有两个原因。
1) 调用 fig.canvas.draw()
会重新绘制所有内容。这是你的瓶颈。在你的情况下,你并不需要重新绘制像坐标轴边界、刻度标签等东西。
2) 在你的情况下,有很多子图和很多刻度标签。这些绘制起来需要很长时间。
这两个问题都可以通过使用“blitting”来解决。
要高效地使用“blitting”,你需要使用特定于后端的代码。实际上,如果你真的担心动画的流畅性,通常你会把matplotlib图嵌入到某种图形用户界面工具包中,所以这并不是个大问题。
不过,如果不太了解你在做什么,我就帮不了你了。
尽管如此,还有一种不依赖于具体图形界面的方式来实现,这种方式仍然相当快。
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)
这样我能达到大约200帧每秒。
为了让这个过程更方便,最近版本的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()