Obecnie oceniam różne biblioteki kreślące w języku Python. W tej chwili próbuję matplotlib i jestem dość rozczarowany wydajnością. Poniższy przykład jest zmodyfikowany na podstawie przykładów SciPy i daje mi tylko ~ 8 klatek na sekundę!
Jakieś sposoby na przyspieszenie tego, czy powinienem wybrać inną bibliotekę drukowania?
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)
python
matplotlib
ja sam
źródło
źródło
Odpowiedzi:
Po pierwsze (chociaż nie zmieni to wcale wydajności) rozważ wyczyszczenie kodu, podobnie do tego:
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)
W powyższym przykładzie uzyskuję około 10 klatek na sekundę.
Krótka uwaga, w zależności od konkretnego przypadku użycia, matplotlib może nie być świetnym wyborem. Jest zorientowany na dane o jakości publikacyjnej, a nie wyświetlanie w czasie rzeczywistym.
Jest jednak wiele rzeczy, które możesz zrobić, aby przyspieszyć ten przykład.
Istnieją dwa główne powody, dla których jest to tak powolne.
1) Wywołanie
fig.canvas.draw()
przerysowuje wszystko . To twoje wąskie gardło. W twoim przypadku nie musisz ponownie rysować rzeczy, takich jak granice osi, etykiety znaczników itp.2) W twoim przypadku jest wiele wątków pobocznych z wieloma etykietami tików. Te rysowanie zajmuje dużo czasu.
Oba można naprawić za pomocą blittingu.
Aby efektywnie korzystać z blittingu, musisz użyć kodu specyficznego dla zaplecza. W praktyce, jeśli naprawdę martwisz się płynnymi animacjami, i tak zazwyczaj osadzasz wykresy matplotlib w jakimś zestawie narzędzi GUI, więc nie stanowi to większego problemu.
Jednak nie wiedząc więcej o tym, co robisz, nie mogę Ci w tym pomóc.
Niemniej jednak istnieje sposób, w jaki można to zrobić, neutralny pod względem GUI, który jest nadal dość szybki.
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)
To daje ~ 200 fps.
Aby było to trochę wygodniejsze,
animations
w najnowszych wersjach matplotlib dostępny jest moduł.Jako przykład:
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()
źródło
animation
wydaje się, że aktualizuje wykres wedługinterval
okresu, co jeśli chcę go zaktualizować, gdy nowe dane są gotowe?Matplotlib tworzy świetną grafikę o jakości publikacyjnej, ale nie jest zbyt dobrze zoptymalizowany pod kątem szybkości. Istnieje wiele pakietów do drukowania w Pythonie, które zostały zaprojektowane z myślą o szybkości:
[edit: pyqwt nie jest już obsługiwane; poprzedni opiekun poleca pyqtgraph]
źródło
Na początek odpowiedź Joe Kingtona zawiera bardzo dobrą radę, stosując podejście neutralne pod względem gui i zdecydowanie powinieneś skorzystać z jego rady (szczególnie dotyczącej Blittingu) i zastosować ją w praktyce. Więcej informacji na temat tego podejścia można znaleźć w książce kucharskiej Matplotlib
Jednak podejście nie-neutralne dla GUI (obciążone GUI?) Jest kluczem do przyspieszenia kreślenia. Innymi słowy, zaplecze jest niezwykle ważne dla szybkości kreślenia.
Umieść te dwie linie, zanim zaimportujesz cokolwiek innego z matplotlib:
import matplotlib matplotlib.use('GTKAgg')
Oczywiście zamiast tego można użyć różnych opcji
GTKAgg
, ale według wspomnianej wcześniej książki kucharskiej była to najszybsza. Zobacz link o backendach, aby uzyskać więcej opcji.źródło
W przypadku pierwszego rozwiązania zaproponowanego przez Joe Kingtona (.copy_from_bbox & .draw_artist & canvas.blit) musiałem uchwycić tła po linii fig.canvas.draw (), w przeciwnym razie tło nie miało żadnego efektu i otrzymałem taki sam wynik jak wspomniałeś. Jeśli umieścisz go po fig.show (), nadal nie będzie działał tak, jak zaproponował Michael Browne.
Więc po prostu umieść linię tła po płótnie.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]
źródło
Może to nie dotyczyć wielu z was, ale zwykle pracuję na moich komputerach pod Linuksem, więc domyślnie zapisuję moje wykresy w matplotlib jako PNG i SVG. Działa to dobrze pod Linuksem, ale jest nieznośnie powolne w moich instalacjach Windows 7 [MiKTeX pod Pythonem (x, y) lub Anaconda], więc zacząłem dodawać ten kod i wszystko działa dobrze:
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')
źródło