2016-03-22 4 views
2

Betrachten Sie das folgende Beispiel in Python 2.7. Wir haben eine beliebige Funktion f(), die zwei eindimensionale numpy Arrays zurückgibt. Beachten Sie, dass f() im Allgemeinen Arrays unterschiedlicher Größe zurückgeben kann und dass die Größe von der Eingabe abhängen kann.Erstellen von zwei verketteten Arrays aus einem Generator

Jetzt möchten wir map auf f() aufrufen und die Ergebnisse in zwei separate neue Arrays verketten.

import numpy as np 

def f(x): 
    return np.arange(x),np.ones(x,dtype=int) 

inputs = np.arange(1,10) 
result = map(f,inputs) 
x = np.concatenate([i[0] for i in result]) 
y = np.concatenate([i[1] for i in result]) 

Dies ergibt das beabsichtigte Ergebnis. Da das Ergebnis jedoch viel Speicher belegen kann, ist es möglicherweise vorzuziehen, einen Generator zu verwenden, indem Sie imap statt map aufrufen.

from itertools import imap 
result = imap(f,inputs) 
x = np.concatenate([i[0] for i in result]) 
y = np.concatenate([i[1] for i in result]) 

jedoch ergibt dies einen Fehler, da der Generator an der Stelle leer ist, wo wir y berechnen.

Gibt es eine Möglichkeit, den Generator nur einmal zu verwenden und immer noch diese beiden verketteten Arrays zu erstellen? Ich suche nach einer Lösung ohne eine for-Schleife, da es ziemlich ineffizient ist, Arrays wiederholt zu verketten/anzufügen.

Vielen Dank im Voraus.

+0

Haben Sie bedeuten, dass 'i [0]' und 'i [1]' verschiedene Formen haben kann? –

+0

@SergeiLebedev Im Allgemeinen ja. In meinem Beispiel haben sie die gleiche Form. – Forzaa

+0

Ich sehe nicht den Vorteil der Verwendung eines Generators für "Ergebnis". Sie erstellen für den Aufruf "np.concatenate" noch zwei Listen gleicher Größe wie "result". Wenn also zwei Listen zulässig sind, können Sie einfach in einer For-Schleife über 'result' iterieren und die Listen gleichzeitig aktualisieren. –

Antwort

3

Gibt es eine Möglichkeit, den Generator nur einmal zu verwenden und immer noch diese beiden verketteten Arrays zu erstellen?

Ja, kann ein Generator mit tee geklont werden:

import itertools 
a, b = itertools.tee(result) 

x = np.concatenate([i[0] for i in a]) 
y = np.concatenate([i[1] for i in b]) 

jedoch tee mit Hilfe nicht mit der Speichernutzung in Ihrem Fall. Die obige Lösung würde erfordern, 5 N Speicher auszuführen:

  • N für die Zwischenspeicherung des Generators innen tee,
  • 2 N für die Listen Comprehensions innen np.concatenate Anrufe,
  • 2 N für die verkettete Arrays.

Natürlich könnten wir besser machen, indem die tee fallen:

x_acc = [] 
y_acc = [] 
for x_i, y_i in result: 
    x_acc.append(x_i) 
    y_acc.append(y_i) 

x = np.concatenate(x_acc) 
y = np.concatenate(y_acc) 

Dieses mehr N abrasiert, so dass 4 N. weitere Mittel gehen die Zwischenlisten löschen und preallocating x und y. Beachten Sie, dass Sie müssen nicht wissen, die genaue Größen der Arrays, nur die oberen Grenzen:

x = np.empty(capacity) 
y = np.empty(capacity) 
right = 0 
for x_i, y_i in result: 
    left = right 
    right += len(x_i) # == len(y_i) 
    x[left:right] = x_i 
    y[left:right] = y_i 

x = x[:right].copy() 
y = y[:right].copy() 

In der Tat, Sie nicht einmal eine obere Schranke müssen. Einfach sicher, dass x und y groß genug sind, um das neue Element aufzunehmen:

for x_i, y_i in result: 
    # ... 
    if right >= len(x): 
     # It would be slightly trickier for >1D, but the idea 
     # remains the same: alter the 0-the dimension to fit 
     # the new item. 
     new_capacity = max(right, len(x)) * 1.5 
     x = x.resize(new_capacity) 
     y = y.resize(new_capacity) 
Verwandte Themen