Matplotlib ile plan yapmak neden bu kadar yavaş?


101

Şu anda farklı python çizim kitaplıklarını değerlendiriyorum. Şu anda matplotlib'i deniyorum ve performanstan oldukça hayal kırıklığına uğradım. Aşağıdaki örnek SciPy örneklerinden değiştirildi ve bana saniyede yalnızca ~ 8 kare veriyor!

Bunu hızlandırmanın herhangi bir yolu var mı yoksa farklı bir çizim kitaplığı mı seçmeliyim?

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)

Aşağıdakiler ilgili olabilir: stackoverflow.com/questions/5003094/…
NPE

2
@aix - Glumpy sadece bu örnekte yardımcı oldu çünkü hızlı bir şekilde görüntü verilerini görüntülemeyle uğraşıyordu. Bu durumda yardımcı olmayacak.
Joe Kington

1
Arka ucu değiştirmeyi deneyin. Cevabıma bakın: stackoverflow.com/a/30655528/2066079 . veya arka
uçlarla

Yanıtlar:


116

Öncelikle, (bu performansı hiç değiştirmeyecek olsa da) şuna benzer şekilde kodunuzu temizlemeyi düşünün:

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)

Yukarıdaki örnekle yaklaşık 10 fps elde ediyorum.

Kısa bir not, tam kullanım durumunuza bağlı olarak, matplotlib harika bir seçim olmayabilir. Gerçek zamanlı görüntülemeye değil, yayın kalitesinde rakamlara yöneliktir.

Ancak, bu örneği hızlandırmak için yapabileceğiniz birçok şey var.

Bunun bu kadar yavaş olmasının iki ana nedeni var.

1) Çağrı her şeyifig.canvas.draw() yeniden çizer . Bu senin darboğazın. Sizin durumunuzda, eksen sınırları, işaret etiketleri vb. Gibi şeyleri yeniden çizmenize gerek yoktur.

2) Sizin durumunuzda, çok sayıda işaret etiketi olan çok sayıda alt nokta vardır. Bunları çizmek uzun zaman alıyor.

Her ikisi de blitting kullanılarak düzeltilebilir.

Verimli bir şekilde blitting yapmak için arka uca özel kod kullanmanız gerekir. Pratikte, pürüzsüz animasyonlar konusunda gerçekten endişeleniyorsanız, genellikle matplotlib grafiklerini bir çeşit gui araç setine yerleştiriyorsunuzdur, bu yüzden bu bir sorun değil.

Ancak, ne yaptığınız hakkında biraz daha fazla bilgi sahibi olmadan size yardımcı olamam.

Yine de, bunu yapmanın hala makul derecede hızlı olan, gui-nötr bir yolu var.

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)

Bu bana ~ 200 fps veriyor.

Bunu biraz daha kullanışlı hale getirmek için animationsmatplotlib'in son sürümlerinde bir modül var .

Örnek olarak:

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

kodunuz gerçekten çok hızlı, ancak ben eksen başına 2000 satır ile sonuçlandım! bir şekilde "line.set_ydata" güncellemek yerine yeni bir satır oluşturuyor - yoksa arka plan temizlenmiyor mu? Artı, sürümünüz neden bu kadar hızlı? sadece "draw ()" bırakıp "ax.draw_artist" ile değiştirdiğiniz için mi?
memyself

Hangi örnekte? (Onları test ettim, ancak yanıta yanlış sürümü kopyalayıp yapıştırmak mümkündür.) Ayrıca matplotlib'in hangi sürümünü kullanıyorsunuz?
Joe Kington

4
işte ortaya çıkan görüntünün bağlantısı i.imgur.com/aBRFz.png bu, grafik kartımın neden olduğu bir yapı olabilir mi?
memyself

7
Arka plan görüntüsünü fig.show () 'un altına taşıyana kadar memyself'in i.imgur.com/aBRFz.png'de gördüğü şeyin aynısını görüyordum .
Michael Browne

4
Güzel, ancak animationarsa intervalzaman dilimine göre güncelleniyor gibi görünüyor , peki ya yeni veriler hazır olduğunda sadece güncellemek istersem?
Alcott

31

Matplotlib, yayın kalitesinde harika grafikler oluşturur, ancak hız için çok iyi optimize edilmemiştir. Hız göz önünde bulundurularak tasarlanmış çeşitli python plotlama paketleri vardır:


1
Gerçek zamanlı akış verileri için pyqtgraph.org/documentation'dan tam anlamıyla yararlanıyorum . great job luke
qrtLs

11

Başlangıç ​​olarak, Joe Kington'ın cevabı gui-nötr bir yaklaşım kullanarak çok iyi tavsiyeler veriyor ve kesinlikle onun tavsiyesini (özellikle Blitting hakkında) almalı ve uygulamaya koymalısınız. Bu yaklaşım hakkında daha fazla bilgi için Matplotlib Yemek Kitabını okuyun

Bununla birlikte, GUI-nötr olmayan (GUI-taraflı?) Yaklaşım, çizimi hızlandırmanın anahtarıdır. Başka bir deyişle, arka uç , hızı belirlemek için son derece önemlidir.

Matplotlib'den başka bir şey aktarmadan önce şu iki satırı koyun:

import matplotlib
matplotlib.use('GTKAgg') 

Tabii bunun yerine kullanabileceğiniz çeşitli seçenekler var GTKAgg, ancak daha önce bahsettiğimiz yemek kitabına göre bu en hızlı olanıydı. Daha fazla seçenek için arka uçlarla ilgili bağlantıya bakın.


Bu yalnızca pencerelerde çalışır, Mac'te çalışmasını sağlamanın bir yolunu biliyor musunuz? Windows'a özgü olmasının nedeni,
pygtk'in

2
pygtk, pencerelere özgü değildir. Aslında, Windows altında çalışmasını sağlamak çok büyük bir acı (mümkünse bile vazgeçtim.)
Joseph Redfern

7

Joe Kington (.copy_from_bbox & .draw_artist & canvas.blit) tarafından önerilen ilk çözüm için , fig.canvas.draw () satırından sonra arka planları yakalamak zorunda kaldım , aksi takdirde arka planın hiçbir etkisi olmadı ve ile aynı sonucu aldım bahsetmiştin. Fig.show () 'dan sonra koyarsanız, yine de Michael Browne tarafından önerildiği gibi çalışmaz.

Arka plan çizgisini canvas.draw () 'dan sonra koyun :

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

4
cevabını ayrı bir mesaj olarak göndermek yerine sadece düzenlemelisiniz
endolith

1

Bu birçoğunuz için geçerli olmayabilir, ancak genellikle bilgisayarlarımı Linux altında çalıştırıyorum, bu nedenle varsayılan olarak matplotlib çizimlerimi PNG ve SVG olarak kaydediyorum. Bu, Linux altında iyi çalışıyor, ancak Windows 7 kurulumlarımda [MiKTeX Python (x, y) veya Anaconda] üzerinde dayanılmaz derecede yavaş, bu yüzden bu kodu eklemeye başladım ve orada yine iyi çalışıyor:

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')
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.