2017-03-03 6 views
3

An ungeraden Tagen der Woche verstehe ich fast mehrdimensionale Indexierung in numpy. Numpy hat eine Funktion 'take' die scheint zu tun, was ich will, aber mit dem zusätzlichen Bonus, dass ich kontrollieren kann, was passiert, wenn die Indizierung außerhalb des Bereichs ist Speziell habe ich ein 3-dimensionales Array als die Lookup-Tabellenumpy mehrdimensionale Indizierung und die Funktion 'take'

fragen
lut = np.ones([13,13,13],np.bool) 

und ein 2x2-Array von 3-langen Vektoren als Indizes in die Tabelle

arr = np.arange(12).reshape([2,2,3]) % 13 

IIUC zu wirken, war, wenn ich lut[arr] dann schreiben arr als 2x2x3 Matrix von Zahlen behandelt wird, und wenn diese verwendet werden, Als Indizes in lut geben sie jeweils ein 13x13-Array zurück. Dies erklärt, warum lut[arr].shape is (2, 2, 3, 13, 13).

Ich kann es tun, was ich durch das Schreiben will

lut[ arr[:,:,0],arr[:,:,1],arr[:,:,2] ] #(is there a better way to write this?) 

und jetzt sind die drei Begriffe, als ob sie ein 2x2-Array von Tupeln und lut[<tuple>] erzeugt ein einzelnes Element aus lut herzustellen gezippt wurden. Das Endergebnis ist ein 2x2 Array von Einträgen von lut, genau was ich will.

Ich habe die Dokumentation für die Funktion ‚nehmen‘ ...

Diese Funktion macht das Gleiche wie „fancy“ Indizierung (Indizierung Arrays Arrays); Es kann jedoch einfacher sein zu verwenden, wenn Sie Elemente entlang einer bestimmten Achse benötigen.

und

Achse: int, optional
Die Achse, über die Werte auszuwählen.

Vielleicht naiv, dachte ich, dass axis=2 Einstellung ich drei Werte als 3-Tupel zu verwenden, erhalten würde die Suche durchzuführen, aber tatsächlich

np.take(lut,arr).shape = (2, 2, 3) 
np.take(lut,arr,axis=0).shape = (2, 2, 3, 13, 13) 
np.take(lut,arr,axis=1).shape = (13, 2, 2, 3, 13) 
np.take(lut,arr,axis=2).shape = (13, 13, 2, 2, 3) 

so ist es klar, ich verstehe nicht, was los ist auf. Kann mir jemand zeigen, wie ich erreichen kann, was ich will?

Antwort

0

Das ursprüngliche Problem ein Nachschlagen in einer Tabelle zu tun versucht, aber einige der Indizes waren außerhalb der Grenzen, und ich wollte das Verhalten steuern, wenn das ist passiert.

import numpy as np 
lut = np.ones((5,7,11),np.int) # a 3-dimensional lookup table 
print("lut.shape = ",lut.shape) # (5,7,11) 

# valid points are in the interior with value 99, 
# invalid points are on the faces with value 0 
lut[:,:,:] = 0 
lut[1:-1,1:-1,1:-1] = 99 

# set up an array of indexes with many of them too large or too small 
start = -35 
arr = np.arange(start,2*11*3+start,1).reshape(2,11,3) 

# This solution has the advantage that I can understand what is going on 
# and so I can amend it if I need to 

# split arr into tuples along axis=2 
arrchannels = arr[:,:,0],arr[:,:,1],arr[:,:,2] 

# convert into a flat array but clip the values 
ravelledarr = np.ravel_multi_index(arrchannels, lut.shape, mode='clip') 

# and now turn back into a list of numpy arrays 
# (not an array of the original shape) 
clippedarr = np.unravel_index(ravelledarr, lut.shape) 
print(clippedarr[0].shape,"*",len(clippedarr)) # produces (2, 11) * 3 

# and now I can do the lookup with the indexes clipped to fit 
print(lut[clippedarr]) 

# these are more succinct but opaque ways of doing the same 
# due to @Divakar and @hjpauli respectively 
print(np.take(lut, np.ravel_multi_index(arr.T, lut.shape, mode='clip')).T) 
print(lut.flat[np.ravel_multi_index(arr.T, lut.shape, mode='clip')].T) 

Die eigentliche Anwendung war, dass ich ein RGB-Bild enthält einig gemasertes Holz mit einigen Markierungen auf sie hatte und ich hatte einen Patch davon identifiziert. Ich wollte den Satz von Pixeln in diesem Patch nehmen und alle Punkte im gesamten Bild markieren, die zu einem von ihnen passten. Eine 256x256x256 Existenztabelle ist zu groß, daher habe ich einen Clustering-Algorithmus für die Pixel aus dem Patch ausgeführt und für jeden Cluster Existenztabellen eingerichtet (die Farben aus dem Patch bildeten einen schlanken Thread durch rgb- oder hsv-Raum, also die Boxen um die Cluster waren klein).

Ich mache die Existenz Tabellen etwas größer als benötigt und fülle jedes Gesicht mit False.

Sobald ich diese kleinen Existenztabellen eingerichtet habe, kann ich nun den Rest des Bildes für die Anpassung des Patches durch Nachschlagen jedes Pixels in den Tabellen testen und den Ausschnitt verwenden, um Pixel zu erzeugen, die normalerweise nicht in den Tabellen werden tatsächlich in ein Gesicht der Tabelle abgebildet (und erhalten den Wert 'Falsch')

2

wir die lineare Indizes berechnen und dann np.take verwenden -

np.take(lut, np.ravel_multi_index(arr.T, lut.shape)).T 

Wenn Sie Alternativen offen sind, wir die Indizes Array 2D umformen können, wandeln mit ihm in das Datenfeld zu Tupeln, Index, geben uns 1D, die 2D wieder neu gestaltet werden könnte -

lut[tuple(arr.reshape(-1,arr.shape[-1]).T)].reshape(arr.shape[:2]) 

Probelauf -

In [49]: lut = np.random.randint(11,99,(13,13,13)) 

In [50]: arr = np.arange(12).reshape([2,2,3]) 

In [51]: lut[ arr[:,:,0],arr[:,:,1],arr[:,:,2] ] # Original approach 
Out[51]: 
array([[41, 21], 
     [94, 22]]) 

In [52]: np.take(lut, np.ravel_multi_index(arr.T, lut.shape)).T 
Out[52]: 
array([[41, 21], 
     [94, 22]]) 

In [53]: lut[tuple(arr.reshape(-1,arr.shape[-1]).T)].reshape(arr.shape[:2]) 
Out[53]: 
array([[41, 21], 
     [94, 22]]) 

Wir können die Doppel Transponieren für den np.take Ansatz vermeiden, wie so -

In [55]: np.take(lut, np.ravel_multi_index(arr.transpose(2,0,1), lut.shape)) 
Out[55]: 
array([[41, 21], 
     [94, 22]]) 

Generalize multidimensionalen Arrays von generischen Dimensionen

Dies zu ndarrays von Generika nicht verallgemeinert werden könnte. von dimmt, wie so -

np.take(lut, np.ravel_multi_index(np.rollaxis(arr,-1,0), lut.shape)) 

Der tuple-based Ansatz ohne Änderung funktionieren sollte.

Hier ist ein Probelauf für die gleichen -

In [95]: lut = np.random.randint(11,99,(13,13,13,13)) 

In [96]: arr = np.random.randint(0,13,(2,3,4,4)) 

In [97]: lut[ arr[:,:,:,0] , arr[:,:,:,1],arr[:,:,:,2],arr[:,:,:,3] ] 
Out[97]: 
array([[[95, 11, 40, 75], 
     [38, 82, 11, 38], 
     [30, 53, 69, 21]], 

     [[61, 74, 33, 94], 
     [90, 35, 89, 72], 
     [52, 64, 85, 22]]]) 

In [98]: np.take(lut, np.ravel_multi_index(np.rollaxis(arr,-1,0), lut.shape)) 
Out[98]: 
array([[[95, 11, 40, 75], 
     [38, 82, 11, 38], 
     [30, 53, 69, 21]], 

     [[61, 74, 33, 94], 
     [90, 35, 89, 72], 
     [52, 64, 85, 22]]]) 
+0

Für das, was es wert ist, ist das 'np.take' gleichbedeutend mit der Indizierung' lut.flat': 'lut.flat [np.ravel_multi_index (arr.T, lut.shape)]. T' – hpaulj

+0

Danke an beides für deine Lösungen. Da ravel_multi_index Clipping erlaubt, muss ich überhaupt nicht "nehmen", also werde ich mit @hjpauls Änderung zu Divakars Lösung gehen –

0

Ich habe es nicht in 3-Dimensionen versucht. Aber in 2-Dimensionen bekomme ich das Ergebnis mag ich mit der Verwendung von numpy.take:

np.take(np.take(T,ix,axis=0), iy,axis=1) 

Vielleicht haben Sie die 3-Dimensionen erweitern.

Als Beispiel I mit zwei 1-dim-Arrays für die Indizes Ix und Iy, die 2-dimensionale Schablone für die diskrete Laplace-Gleichung adressieren kann,

ΔT = T[ix-1,iy] + T[ix+1, iy] + T[ix,iy-1] + T[ix,iy+1] - 4*T[ix,iy] 

für einen schlankeren Schreib Einführung:

def q(Φ,kx,ky): 
    return np.take(np.take(Φ,kx,axis=0), ky,axis=1) 

dann kann ich den folgenden python-Code mit numpy.take laufen:

nx = 6; ny= 10 
T = np.arange(nx*ny).reshape(nx, ny) 

ix = np.linspace(1,nx-2,nx-2,dtype=int) 
iy = np.linspace(1,ny-2,ny-2,dtype=int) 

ΔT = q(T,ix-1,iy) + q(T,ix+1,iy) + q(T,ix,iy-1) + q(T,ix,iy+1) - 4.0 * q(T,ix,iy) 
Verwandte Themen