2016-11-28 5 views
3

In einer großen Code-Basis, ich np.broadcast_to bin mit Arrays übertragen (nur einfache Beispiele hier verwenden):Un-Sende Numpy Arrays

In [1]: x = np.array([1,2,3]) 

In [2]: y = np.broadcast_to(x, (2,1,3)) 

In [3]: y.shape 
Out[3]: (2, 1, 3) 

An anderer Stelle im Code, ich Drittanbieter-Funktionen verwenden, die arbeiten können in einer vektorisierten Weise auf Numpy Arrays, aber das sind nicht ufuncs. Diese Funktionen verstehen das Broadcasting nicht, was bedeutet, dass das Aufrufen einer solchen Funktion für Arrays wie y ineffizient ist. Lösungen wie Numpys vectorize sind auch nicht gut, denn während sie Rundfunk verstehen, führen sie eine for Schleife über die Array-Elemente ein, die dann sehr ineffizient ist.

Idealerweise möchte ich in der Lage sein, eine Funktion zu haben, die wir z. , die ein Array mit einer minimalen Form zurückgibt, das bei Bedarf wieder auf die volle Größe übertragen werden kann. So z.B .:

In [4]: z = unbroadcast(y) 

In [5]: z.shape 
Out[5]: (1, 1, 3) 

Ich kann dann die Fremd Funktionen auf z laufe, dann überträgt das Ergebnis zurück an y.shape.

Gibt es eine Möglichkeit zu implementieren, die auf Numpys öffentlicher API basiert? Wenn nicht, gibt es irgendwelche Hacks, die das gewünschte Ergebnis bringen würden?

+0

Etwas wie 'y [None, 0]'? – Divakar

+0

Was meinst du mit "minimale Form"? Wird die minimale Form in N Dimensionen, die von 'unbroadcast' zurückgegeben wird, immer' (1, 1, ..., 1) '(oder sogar' (1,) ') sein? –

+0

Ich meine die minimale Form, die immer noch alle erforderlichen Daten enthält, um sie an das vollständige Array zurückzusenden. Im obigen Beispiel ist '' z.shape' '' '(1,1,3)' 'not' '(1,1,1)' '. – astrofrog

Antwort

3

Dies zu Ihrer eigenen Lösung wahrscheinlich äquivalent ist, nur ein bisschen mehr gebaut -im. Es verwendet as_strided in numpy.lib.stride_tricks:

import numpy as np 
from numpy.lib.stride_tricks import as_strided 

x = np.arange(16).reshape(2,1,8,1) # shape (2,1,8,1) 
y = np.broadcast_to(x,(2,3,8,5)) # shape (2,3,8,5) broadcast 

def unbroadcast(arr): 
    #determine unbroadcast shape 
    newshape = np.where(np.array(arr.strides) == 0,1,arr.shape) # [2,1,8,1], thanks to @Divakar 
    return as_strided(arr,shape=newshape) # strides are automatically set here 

z = unbroadcast(x) 
np.all(z==x) # is True 

Beachten Sie, dass in meiner ursprünglichen Antwort, die ich nicht eine Funktion und der resultierenden z Array hatte (64,0,8,0) als strides, definiert habe, während der Eingang (64,64,8,8) hat. In der aktuellen Version hat das zurückgegebene Array z identische Schritte wie x. Ich denke, dass das Übergeben und Zurückgeben des Arrays die Erstellung einer Kopie erzwingt. Wie auch immer, wir könnten die Schritte immer manuell in as_strided setzen, um unter allen Umständen identische Arrays zu erhalten, aber dies scheint im obigen Setup nicht notwendig zu sein.

+1

Oder 'np.where (np.array (y.trides) == 0,1, y.shape)' für die Nachrichtenform? – Divakar

+0

@Divakar richtig, ich vergesse 'np.where' :) Offensichtlich ist das die numpy-idiomatische Version, danke. –

+1

Sehr schöne Verbesserung meiner Lösung - danke! – astrofrog

4

Ich habe eine mögliche Lösung, also werde es hier posten (aber wenn jemand eine bessere hat, bitte zögern Sie nicht zu antworten!). Eine Lösung ist das strides Argument von Arrays zu überprüfen, die 0 entlang ausgestrahlt Dimensionen sein:

def unbroadcast(array): 
    slices = [] 
    for i in range(array.ndim): 
     if array.strides[i] == 0: 
      slices.append(slice(0, 1)) 
     else: 
      slices.append(slice(None)) 
    return array[slices] 

Das gibt:

In [14]: unbroadcast(y).shape 
Out[14]: (1, 1, 3)