为啥用 Matplotlib 绘图这么慢?

Posted

技术标签:

【中文标题】为啥用 Matplotlib 绘图这么慢?【英文标题】:why is plotting with Matplotlib so slow?为什么用 Matplotlib 绘图这么慢? 【发布时间】:2012-02-15 20:55:40 【问题描述】:

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

【问题讨论】:

以下可能是相关的:***.com/questions/5003094/… @aix - Glumpy 仅在该示例中有所帮助,因为他正在处理快速显示图像数据。在这种情况下它没有帮助。 尝试更改后端。看我的回答:***.com/a/30655528/2066079。或者这个关于后端的常见问题解答:matplotlib.org/faq/usage_faq.html#what-is-a-backend 使用 fig.canvas.draw_idle() 而不是 fig.canvas.draw() 对我有用。 【参考方案1】:

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

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,您必须使用特定于后端的代码。在实践中,如果你真的很担心流畅的动画,你通常会在某种 gui 工具包中嵌入 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)

这给了我 ~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()

【讨论】:

您的代码确实非常快,但是我最终每个轴有 2000 行!不知何故“line.set_ydata”创建了一个新行而不是更新它 - 或者背景只是没有被清除?另外,为什么你的版本这么快?仅仅因为您删除了“draw()”并将其替换为“ax.draw_artist”? 这是生成图像的链接i.imgur.com/aBRFz.png这可能是我的显卡造成的伪影吗? 我在 i.imgur.com/aBRFz.png 中看到的内容与我自己在 i.imgur.com/aBRFz.png 中看到的相同,直到我将背景捕获移动到 下面 fig.show()。 很好,但是animation 似乎会在interval 一段时间内更新绘图,如果我只想在新数据准备好时更新它怎么办? @JoeKington 我在哪里可以得到更多关于这些加速matplotlib实时更新数据的技术的信息?【参考方案2】:

Matplotlib 制作了出色的出版质量图形,但在速度方面没有得到很好的优化。 有多种 Python 绘图包在设计时考虑到了速度:

http://vispy.org http://pyqtgraph.org/ http://docs.enthought.com/chaco/ http://pyqwt.sourceforge.net/ [ 编辑:pyqwt 不再维护;之前的维护者推荐 pyqtgraph ]​​i> http://code.google.com/p/guiqwt/

【讨论】:

我非常喜欢pyqtgraph.org/documentation 的实时流数据。卢克干得好【参考方案3】:

首先,Joe Kington's answer 使用 gui 中立的方法提供了非常好的建议,您绝对应该采纳他的建议(尤其是关于 Blitting)并将其付诸实践。有关此方法的更多信息,请阅读Matplotlib Cookbook

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

在从 matplotlib 导入任何其他内容之前放置这两行:

import matplotlib
matplotlib.use('GTKAgg') 

当然,除了GTKAgg,还有很多选项可以使用,但是根据之前提到的cookbook,这是最快的。有关更多选项,请参阅有关后端的链接。

【讨论】:

这只能在 Windows 上工作,你知道让它在 Mac 上工作的方法吗?它是 windows 特定的原因是 pygtk 是 windows 特定的 pygtk 不是特定于 Windows 的。事实上,让它在 Windows 下工作是一个巨大的痛苦(如果可能的话,我已经放弃了。)【参考方案4】:

对于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】:

这可能不适用于你们中的许多人,但我通常在 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')

【讨论】:

以上是关于为啥用 Matplotlib 绘图这么慢?的主要内容,如果未能解决你的问题,请参考以下文章

python---matplotlib

Matplotlib 为啥我点击后绘图功能会在按钮内绘制?

尝试使用 matplotlib 内联绘图时,为啥在 jupyter notebook 中出现 NonGuiException?

怎样用python画图,为啥代码写好运行时错误?

为啥wordpress反应这么慢

为啥带有基于谓词的期望的 XCTest 这么慢?