为什么和 Matplotlib 一起密谋这么慢?

我目前正在评估不同的 Python 绘图库。现在我正在尝试 matplotlib,我对它的表现非常失望。下面的例子是从 SciPy examples修改过来的,只给了我大约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)
172613 次浏览

首先,(尽管这根本不会改变性能)考虑清理您的代码,类似于下面这样:

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个 fps。

简单说明一下,根据您的确切用例,matplotlib 可能不是一个很好的选择。它的目标是出版质量的数字,而不是实时显示。

但是,您可以做很多事情来加快这个示例的速度。

这么慢有两个主要原因。

1)调用 fig.canvas.draw()重新绘制 一切。这是你的瓶颈。在您的情况下,您不需要重新绘制像轴边界、勾选标签等东西。

2)在你的例子中,有很多次要情节和很多刻度标签。这些要花很长时间才能画出来。

这两个问题都可以通过使用位移来解决。

要有效地进行位块处理,必须使用特定于后端的代码。在实践中,如果您真的担心平滑的动画,那么您通常会将 matplotlib 图形嵌入到某种类型的 GUI 工具包中,所以这不是什么大问题。

但是,如果我不知道你在做什么,我就帮不了你。

尽管如此,还是有一种归属中立的方法,这种方法仍然相当快。

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 fps。

To make this a bit more convenient, there's an animations module in recent versions of matplotlib.

举个例子:

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 提供了很好的出版质量图形,但是在速度方面没有得到很好的优化。 有各种各样的蟒蛇绘图软件包,它们的设计都考虑到了速度:

对于 乔 · 金顿提出的第一个解决方案(。Copy _ from _ bbox & .我必须捕捉背景 之后的 fig.cabas.pull ()线,否则背景没有任何效果,我得到了和你提到的一样的结果。如果你把它放在 fig.show ()后面,它仍然不能像 Michael Browne 提出的那样工作。

所以只需要把背景线 < em > 在 之后放到画布上:

[...]
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]

To start, Joe Kington's answer provides very good advice using a gui-neutral approach, and you should definitely take his advice (especially about Blitting) and put it into practice. More info on this approach, read the Matplotlib 食谱

但是,非 GUI 中性(GUI 偏好?) 方法是加快绘图的关键。换句话说,后端对于绘制速度非常重要。

在从 matplotlib 导入任何其他内容之前,请使用以下两行:

import matplotlib
matplotlib.use('GTKAgg')

当然,有各种各样的选项可以代替 GTKAgg使用,但是根据之前提到的烹饪书,这是最快的。有关更多选项,请参见有关后端的链接。

这可能不适用于你们中的很多人,但是我通常在 Linux 下操作我的计算机,所以默认情况下我将 matplotlib 图保存为 PNG 和 SVG。这在 Linux 下运行得很好,但在我的 Windows 7安装中(在 Python (x,y)或 Anaconda 下的 MiKTeX)速度慢得令人难以忍受,所以我已经开始添加这段代码,在那里一切又都运行得很好:

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')