2016-12-08 5 views
2

Ich hoffe, einen Weg zu finden, um die folgende Situation zu optimieren. Ich habe ein großes Konturplot mit imshow von Matplotlib erstellt. Ich möchte dann dieses Konturdiagramm verwenden, um eine große Anzahl von PNG-Bildern zu erstellen, wobei jedes Bild ein kleiner Abschnitt des Konturenbildes ist, indem die X- und Y-Grenzen und das Seitenverhältnis geändert werden.matplotlib savefig leistung, spart mehrere png in loop

In der Schleife ändern sich also keine Plotdaten, nur die Achsengrenzen und das Seitenverhältnis ändern sich zwischen den einzelnen Png-Bildern.

Das folgende MWE erstellt 70 Png Bilder in einem Ordner "Feigen", die die vereinfachte Idee zeigen. Etwa 80% der Laufzeit werden von fig.savefig('figs/'+filename) eingenommen.

Ich habe ohne zu kommen mit einer Verbesserung in den folgenden sah:

  • Eine Alternative zu matplotlib mit einem Fokus auf Geschwindigkeit - ich habe gekämpft keine Beispiele/Dokumentation der Kontur/Oberfläche Plots finden mit ähnlichen Anforderungen
  • Multiprocessing - Ähnliche Fragen, die ich hier gesehen habe, scheinen fig = plt.figure() und ax.imshow innerhalb der Schleife aufgerufen werden, da Feige und Axt nicht gebeizt werden kann. In meinem Fall ist dies teurer als Geschwindigkeitsverbesserungen, die durch die Implementierung von Multiprocessing erreicht werden.

Ich würde alle Einsichten oder Vorschläge, die Sie haben könnten, schätzen.

import numpy as np 
import matplotlib as mpl 
mpl.use('agg') 
import matplotlib.pyplot as plt 
import time, os 

def make_plot(x, y, fix, ax): 
    aspect = np.random.random(1)+y/2.0-x 
    xrand = np.random.random(2)*x 
    xlim = [min(xrand), max(xrand)] 
    yrand = np.random.random(2)*y 
    ylim = [min(yrand), max(yrand)] 
    filename = '{:d}_{:d}.png'.format(x,y) 

    ax.set_aspect(abs(aspect[0])) 
    ax.set_xlim(xlim) 
    ax.set_ylim(ylim) 
    fig.savefig('figs/'+filename) 

if not os.path.isdir('figs'): 
    os.makedirs('figs') 
data = np.random.rand(25, 25) 

fig = plt.figure() 
ax = fig.add_axes([0., 0., 1., 1.]) 
# in the real case, imshow is an expensive calculation which can't be put inside the loop 
ax.imshow(data, interpolation='nearest') 

tstart = time.clock() 
for i in range(1, 8): 
    for j in range(3, 13): 
     make_plot(i, j, fig, ax) 

print('took {:.2f} seconds'.format(time.clock()-tstart)) 

Antwort

2

Da die Einschränkung in diesem Fall ist der Aufruf plt.savefig() kann es nicht viel optimiert werden. Intern wird die Figur von Grund auf neu erstellt und das dauert eine Weile. Wenn Sie möglicherweise die Anzahl der zu zeichnenden Scheitelpunkte reduzieren, kann dies die Zeit ein wenig reduzieren.

Die Zeit, um Ihren Code auf meinem Computer (Win 8, i5 mit 4 Kernen 3,5 GHz) zu laufen, ist 2,5 Sekunden. Das scheint nicht schlecht zu sein. Man kann eine kleine Verbesserung erhalten, indem man Multiprocessing verwendet.

Ein Hinweis zu Multiprocessing: Es mag überraschend erscheinen, dass die Verwendung der Zustandsmaschine von pyplot innerhalb multiprocessing überhaupt funktionieren sollte. Aber es tut. Und in diesem Fall muss man, da jedes Bild auf derselben Figur und demselben Achsenobjekt basiert, nicht einmal neue Figuren und Achsen erstellen.

Ich modifizierte eine answer I gave here vor einer Weile für Ihren Fall und die Gesamtzeit ist ungefähr mit Multiprocessing und 5 Prozesse auf 4 Kernen halbiert. Ich habe einen Barplot angehängt, der den Effekt von Multiprocessing zeigt.

import numpy as np 
#import matplotlib as mpl 
#mpl.use('agg') # use of agg seems to slow things down a bit 
import matplotlib.pyplot as plt 
import multiprocessing 
import time, os 

def make_plot(d): 
    start = time.clock() 
    x,y=d 
    #using aspect in this way causes a warning for me 
    #aspect = np.random.random(1)+y/2.0-x 
    xrand = np.random.random(2)*x 
    xlim = [min(xrand), max(xrand)] 
    yrand = np.random.random(2)*y 
    ylim = [min(yrand), max(yrand)] 
    filename = '{:d}_{:d}.png'.format(x,y) 
    ax = plt.gca() 
    #ax.set_aspect(abs(aspect[0])) 
    ax.set_xlim(xlim) 
    ax.set_ylim(ylim) 
    plt.savefig('figs/'+filename) 
    stop = time.clock() 
    return np.array([x,y, start, stop]) 

if not os.path.isdir('figs'): 
    os.makedirs('figs') 
data = np.random.rand(25, 25) 

fig = plt.figure() 
ax = fig.add_axes([0., 0., 1., 1.]) 
ax.imshow(data, interpolation='nearest') 


some_list = [] 
for i in range(1, 8): 
    for j in range(3, 13): 
     some_list.append((i,j)) 


if __name__ == "__main__": 
    multiprocessing.freeze_support() 
    tstart = time.clock() 
    print tstart 
    num_proc = 5 
    p = multiprocessing.Pool(num_proc) 

    nu = p.map(make_plot, some_list) 

    tooktime = 'Plotting of {} frames took {:.2f} seconds' 
    tooktime = tooktime.format(len(some_list), time.clock()-tstart) 
    print tooktime 
    nu = np.array(nu) 

    plt.close("all") 
    fig, ax = plt.subplots(figsize=(8,5)) 
    plt.suptitle(tooktime) 
    ax.barh(np.arange(len(some_list)), nu[:,3]-nu[:,2], 
      height=np.ones(len(some_list)), left=nu[:,2], align="center") 
    ax.set_xlabel("time [s]") 
    ax.set_ylabel("image number") 
    ax.set_ylim([-1,70]) 
    plt.tight_layout() 
    plt.savefig(__file__+".png") 
    plt.show() 

enter image description here

Verwandte Themen