2012-01-21 482 views
65

我目前正在評估不同的python繪圖庫。現在我正在嘗試matplotlib,並且對性能感到非常失望。下面的例子是從SciPy examples修改過來的,每秒只給我8幀!爲什麼繪製Matplotlib這麼慢?

任何加快速度的方法,或者我應該選擇一個不同的繪圖庫?

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) 
+0

以下可能是相關的:http://stackoverflow.com/questions/5003094/how-can-i-speed-up-an-animation – NPE

+2

@aix - Glumpy只在這個例子中有所幫助,因爲他正在處理快速顯示圖像數據。這在這種情況下不起作用。 –

+1

嘗試更改後端。看到我的答案:http://stackoverflow.com/a/30655528/2066079。或關於後端的這個FAQ:http://matplotlib.org/faq/usage_faq.html#what-is-a-backend – dberm22

回答

85

首先,(雖然這將不會改變的性能)考慮清理你的代碼,與此類似:

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) 

這給了我〜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() 
+0

你的代碼確實非常快,但是我最終得到了每軸2000行!以某種方式「line.set_ydata」創建一個新行,而不是更新它 - 或者是背景只是沒有被清除?另外,爲什麼你的版本更快?只是因爲你放棄了「draw()」並將其替換爲「ax.draw_artist」? – memyself

+0

在哪個例子中? (我測試了它們,但是可能將錯誤的版本複製到答案中。)另外,您使用的是哪個版本的matplotlib? –

+0

我的意思是第二個例子。我使用matplotlib版本1.0.1。 – memyself

4

對於由Joe Kington(.copy_from_bbox & .draw_artist & canvas.blit)提出的第一溶液,我不得不捕捉背景之後 fig.canvas.draw()行,否則背景沒有效果,我得到了和你所提到的相同的結果。如果你把它放在fig.show()之後,它仍然不能像邁克爾布朗提出的那樣工作。

所以只要把背景線的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] 
+3

你應該編輯他的答案,而不是作爲一個單獨發佈 – endolith

9

要開始,Joe Kington's answer使用GUI中立的方法提供了非常好的建議,你一定要帶他建議(特別是關於Blitting)並將其付諸實踐。有關此方法的更多信息,請參閱Matplotlib Cookbook

但是,非GUI中性(GUI偏向?)方法是加快繪圖速度的關鍵。換句話說,backend對繪圖速度非常重要。

把這兩行從matplotlib導入之前別的:

import matplotlib 
matplotlib.use('GTKAgg') 

當然,也有使用,而不是GTKAgg各種選項,但根據前面提到的食譜,這是最快的。查看關於後端的鏈接以獲取更多選項。

+0

這隻適用於Windows,你知道一種方法,使其在Mac上工作。它是Windows特定的原因是pygtk是特定於Windows的 – user308827

+1

pygtk不是Windows特定的。事實上,讓它在Windows下工作是一個巨大的痛苦(如果它甚至有可能,我已經放棄了。) –

1

這可能不適用於你們許多人,但我通常在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')