perché è tracciato con Matplotlib così lento?

Attualmente sto valutando diverse python tramando librerie. Adesso sto cercando matplotlib e sono molto deluso per le prestazioni. Nell’esempio seguente viene modificata da SciPy esempi e mi dà solo ~ 8 fotogrammi al secondo!

Tutti i modi di accelerare questo o devo sceglierne una diversa tramando libreria?

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)
InformationsquelleAutor memyself | 2012-01-21

 

5 Replies
  1. 106

    Prima di tutto, (anche se questo non cambia il risultato) prendere in considerazione la pulizia del codice simile a questo:

    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)

    Con l’esempio di cui sopra, ho circa 10 fps.

    Solo una breve nota, a seconda di caso d’uso, matplotlib non può essere una grande scelta. È orientato verso pubblicazione-figure di qualità, non la visualizzazione in tempo reale.

    Tuttavia, ci sono un sacco di cose che si possono fare per accelerare questo esempio fino.

    Ci sono due ragioni principali per cui questo è il più lento è.

    1), la Chiamata di fig.canvas.draw() ridisegna tutto. È il tuo collo di bottiglia. Nel tuo caso, non c’è bisogno di ri-disegnare le cose come gli assi, limiti, tick etichette, etc.

    2) Nel tuo caso, ci sono un sacco di sottotrame, con un sacco di zecca etichette. Questi prendono molto tempo per disegnare.

    Entrambi questi può essere risolto utilizzando il blitting.

    Fare il blitting in modo efficiente, dovrai usare il backend-codice specifico. In pratica, se siete davvero preoccupati per animazioni fluide, di solito sei incorporamento matplotlib trame in una sorta di gui toolkit, in ogni caso, quindi questo non è molto di un problema.

    Tuttavia, senza sapere un po ‘ di più su quello che stai facendo, non ti posso aiutare lì.

    Tuttavia, c’è un gui-neutro modo di fare che è ancora abbastanza veloce.

    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)

    Questo mi dà ~200fps.

    Per rendere questo un po ‘ più conveniente, c’è un animations modulo che nelle versioni più recenti di matplotlib.

    Come esempio:

    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()
    • il tuo codice è molto veloce, però alla fine l’ho 2000 righe per asse! in qualche modo “linea.set_ydata” crea una nuova linea invece di aggiornare o è lo sfondo basta che non venga cancellato? Inoltre, perché è la tua versione, in modo molto più veloce? solo perché si caduto “draw()” e sostituito con “ax.draw_artist”?
    • Che esempio? (Li ho provati, ma è possibile copia-incollato la versione sbagliata la risposta). Inoltre, che versione di matplotlib stai usando?
    • Voglio dire, il secondo esempio. Io uso matplotlib versione 1.0.1.
    • Come per perché è così molto più veloce, sì, è fondamentalmente perché non sto disegnando l’intera area di lavoro, solo la linea stessa.
    • Hmm… Il secondo esempio funziona perfettamente per me… io sto usando una versione di sviluppo di matplotlib al momento, però… ho usato la stessa strategia un sacco di volte in passato, anche se normalmente mi piacerebbe registrare il disegno come un inattivo evento in gtk o tk. Ho al momento, ma io di dare un’occhiata più tardi. Probabilmente sto facendo qualcosa che accade solo a lavorare con l’ultima versione, ma non con le versioni precedenti. La strategia generale sarà certamente lavorare su versioni precedenti (ed è la scelta migliore, se non hai il modulo per animazione).
    • ecco un link per l’immagine risultante i.imgur.com/aBRFz.png questo potrebbe essere un artefatto causato dalla mia scheda grafica?
    • Stavo vedendo la stessa cosa che memyself è stato vedere in i.imgur.com/aBRFz.png fino a quando ho spostato il background di acquisizione sotto fig.show().
    • Bello, ma animation sembra di aggiornare la trama da interval periodo di tempo, che cosa se voglio solo aggiornare nuovi dati è pronto?
    • Ho appena eseguito il primo esempio di codice nella vostra soluzione, non mostra alcun animazione o di aggiornamento dinamico trama, voglio dire, la trama appena mostrato, dopo la cottura il codice, perché?
    • Ho scritto un PyQt applicazione di una volta utilizzando matplotlib per la visualizzazione dei dati, ed era liscia, veloce ed efficiente, anche la qualità dell’immagine è ok, per la pubblicazione di qualità non userei mai un png immagine, o simili, è per questo che esiste tikz pgfplots sono così bello, che è davvero di alta qualità. Come per matplotlib è diventato così lento che è inutilizzabile, ho disinstallato subito e tornare a gnuplot.

  2. 21

    Matplotlib rende grande pubblicazione qualità grafica, ma non è molto ben ottimizzato per la velocità.
    Ci sono una varietà di python tramando pacchetti sono progettati con velocità in mente:

  3. 10

    Per iniziare, Joe Kington risposta fornisce ottimi consigli utilizzando una gui approccio neutrale, e si dovrebbe sicuramente prendere il suo consiglio (in particolare circa il Blitting) e di metterla in pratica. Maggiori informazioni su questo approccio, leggere le Matplotlib Ricettario

    Tuttavia, la non-GUI-neutro (GUI-di parte?) approccio è la chiave per accelerare il tramando. In altre parole, il backend è estremamente importante per la trama velocità.

    Mettere queste due righe prima di importare niente altro da matplotlib:

    import matplotlib
    matplotlib.use('GTKAgg') 

    Naturalmente, ci sono varie opzioni per utilizzare al posto di GTKAgg, ma secondo il ricettario detto prima, questo è stato il più veloce. Vedere il link relativo backend per ulteriori opzioni.

    • Questo funziona solo su windows, però, conoscete un modo per farlo funzionare su Mac. Il motivo è specifico di windows è che pygtk è specifico di windows
    • pygtk non è specifico di windows. In realtà, è un dolore enorme ottenere lavorando sotto Windows (se è ancora possibile, ci ho rinunciato.)
  4. 5

    Per la prima soluzione proposta da Joe Kington ( .copy_from_bbox & .draw_artist & tela.blit), ho dovuto catturare gli sfondi dopo fig.tela.draw() riga, altrimenti lo sfondo ha avuto alcun effetto, e ho ottenuto lo stesso risultato che hai menzionato. Se lo metti dopo la fig.show() ancora non funziona, come proposto da Michael Browne.

    Quindi basta mettere lo sfondo della riga di dopo tela.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]
    • si dovrebbe modificare la sua risposta invece di pubblicare come una separata
  5. 1

    Questo non può essere applicato a molti di voi, ma io sono di solito operativo del mio computer sotto Linux, quindi di default salvare la mia matplotlib trame, come PNG e SVG. Questo funziona bene su Linux, ma è insopportabilmente lento sul mio Windows 7 impianti [MiKTeX in Python(x,y) o Anaconda], così ho preso l’aggiunta di questo codice, e le cose funzionano bene sopra c’è di nuovo:

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

Lascia un commento