为什么使用Matplotlib绘图如此缓慢?

132

我目前正在评估不同的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)

以下内容可能会有帮助:https://dev59.com/bG445IYBdhLWcg3wLXIK - NPE
2
@aix - Glumpy在那个例子中只是帮助快速显示图像数据。但在这种情况下没有帮助。 - Joe Kington
1
尝试更改后端。请参阅我的答案:https://dev59.com/02ox5IYBdhLWcg3wzXel#30655528。或者关于后端的常见问题解答:http://matplotlib.org/faq/usage_faq.html#what-is-a-backend - dberm22
使用 fig.canvas.draw_idle() 而不是 fig.canvas.draw() 对我有用。 - Nirmal
5个回答

136

首先,尽管这不会改变性能,考虑优化你的代码,类似于以下方式:

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可能不是一个很好的选择。它面向出版质量的图形,而不是实时显示。 但是,有很多方法可以加速此示例。 这个效果如此之慢,主要有两个原因。 1)调用fig.canvas.draw()会重新绘制所有内容。 它是瓶颈。 在您的情况下,您不需要重新绘制诸如轴边界、刻度标签等的内容。 2)在您的情况下,有许多子图和大量的刻度标签。 这些需要很长时间才能绘制。 这两者都可以通过使用blitting来解决。 为了有效地进行blitting,您必须使用特定于后端的代码。 实际上,如果您真的担心平滑动画,通常会将matplotlib绘图嵌入某种GUI工具包中,因此这不是什么问题。 然而,如果不了解更多关于您正在做什么的信息,我无法在这方面为您提供帮助。 尽管如此,仍有一种与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帧每秒的速度。

为了使这更加方便,最近版本的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()

4
这是生成图像的链接 http://i.imgur.com/aBRFz.png 这可能是由于我的显卡造成的人为瑕疵吗? - memyself
8
在 i.imgur.com/aBRFz.png 中,我看到的与我自己看到的是一样的,直到我把背景捕获移到 fig.show() 下方。 - Michael Browne
7
不错,但是 animation 函数似乎是按照 interval 时间间隔更新图表的,那如果我只想在有新数据时更新它呢? - Alcott
我刚刚运行了你的解决方案中的第一个代码示例,它没有显示任何动画或动态更新的图表,我的意思是在执行代码后,图表只是出现了,为什么? - Alcott
1
@JoeKington 我在哪里可以获取有关加速matplotlib实时更新数据技术的更多信息? - Bidon
显示剩余6条评论

43

1
我非常喜欢使用http://pyqtgraph.org/documentation/来处理实时流数据。Luke做得很好! - droid192

15
首先,Joe Kington's answer 提供了非常好的使用gui中立方法的建议,您一定要采纳他的建议(特别是关于Blitting),并付诸实践。有关此方法的更多信息,请阅读Matplotlib Cookbook
然而,非GUI中立(GUI偏向?)的方法对于加速绘图至关重要。换句话说,backend 对于绘图速度非常重要。
在从matplotlib导入任何其他内容之前,请将这两行放在最前面:
import matplotlib
matplotlib.use('GTKAgg') 

当然,除了GTKAgg之外,还有各种选项可供使用,但根据前面提到的食谱,这是最快的。请参阅有关后端的链接以获取更多选项。

这只适用于Windows,你知道如何让它在Mac上运行吗?之所以它是Windows特定的,是因为pygtk是Windows特定的。 - user308827
2
pygtk并不是特定于Windows的。事实上,在Windows下让它工作起来非常麻烦(如果可能的话,我已经放弃了)。 - Joseph Redfern
@np8 不过,这种方法有一个缺点:将后端设置为"Agg"无法显示绘图。根据手册,"Agg是一种非交互式后端,只能写入文件"。 - Daniel K.
@DanielK. 没错。我想我有一个脚本创建了许多图并将它们保存到文件中。对于交互式会话,需要一个交互式后端。 - Niko Pasanen
@DanielK.没错。我记得我有一个脚本可以创建多个图表并将它们保存到文件中。对于交互式会话,我们需要一个交互式的后端。 - Niko Föhr
显示剩余3条评论

9
对于由Joe Kington提出的第一个解决方案(.copy_from_bbox & .draw_artist & canvas.blit),我必须在fig.canvas.draw()行之后捕获背景,否则背景没有效果,我得到了你提到的相同结果。如果您将其放在fig.show()之后,它仍然不像Michael Browne所建议的那样工作。
因此,只需将背景线放在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]

5
你应该编辑他的回答,而不是发布一个新的回答。 - endolith

0
这可能不适用于你们中的许多人,但我通常在Linux下操作我的计算机,所以默认情况下我将我的matplotlib图保存为PNG和SVG格式。在Linux下这个方法很好用,但在我的Windows 7系统上(使用MiKTeX、Python(x,y)或Anaconda),速度非常慢,几乎无法忍受。因此,我添加了以下代码,这样在Windows系统上就能正常运行了:
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')

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接