Seltsame Indizierung mit numpy

27

Ich habe eine Variable x, die die Form hat (2,2,50,100).

Ich habe auch ein Array, y, das np.array ([0,10,20]) entspricht. Eine seltsame Sache passiert, wenn ich x [0,:,:, y] indiziere.

x = np.full((2,2,50,100),np.nan)
y = np.array([0,10,20])
print(x.shape)
(2,2,50,100)
print(x[:,:,:,y].shape)
(2,2,50,3)
print(x[0,:,:,:].shape)
(2,50,100)
print(x[0,:,:,y].shape)
(3,2,50)

Warum gibt der letzte (3,2,50) und nicht (2,50,3) aus?

Paul Scotti
quelle
Ich bin neu in Numpy, daher habe ich keine Antwort auf Ihre Frage. Um dies weiter zu untersuchen, schlage ich vor, ein kleineres Beispiel zu finden, das nur 2D oder 3D ist und höchstens 10 Elementen auf einer Achse entspricht.
Code-Apprentice

Antworten:

21

Auf diese Weise verwendet numpy die erweiterte Indizierung, um Array-Formen zu senden. Wenn Sie a 0für den ersten und yfür den letzten Index übergeben, sendet numpy 0die gleiche Form wie y. Folgende Äquivalenz gilt : x[0,:,:,y] == x[(0, 0, 0),:,:,y]. Hier ist ein Beispiel

import numpy as np

x = np.arange(120).reshape(2,3,4,5)
y = np.array([0,2,4])

np.equal(x[0,:,:,y], x[(0, 0, 0),:,:,y]).all()
# returns:
True

Da Sie nun effektiv zwei Indexsätze übergeben, verwenden Sie die erweiterte Indexierungs-API, um (in diesem Fall) Indexpaare zu bilden.

x[(0, 0, 0),:,:,y])

# equivalent to
[
  x[0,:,:,y[0]], 
  x[0,:,:,y[1]], 
  x[0,:,:,y[2]]
]

# equivalent to
rows = np.array([0, 0, 0])
cols = y
x[rows,:,:,cols]

# equivalent to
[
  x[r,:,:,c] for r, c in zip(rows, columns)
]

Welches hat eine erste Dimension, die der Länge von entspricht y. Das sehen Sie.

Schauen Sie sich als Beispiel ein Array mit 4 Dimensionen an, die im nächsten Abschnitt beschrieben werden:

x = np.arange(120).reshape(2,3,4,5)
y = np.array([0,2,4])

# x looks like:
array([[[[  0,   1,   2,   3,   4],    -+      =+
         [  5,   6,   7,   8,   9],     Sheet1  |
         [ 10,  11,  12,  13,  14],     |       |
         [ 15,  16,  17,  18,  19]],   -+       |
                                                Workbook1
        [[ 20,  21,  22,  23,  24],    -+       |
         [ 25,  26,  27,  28,  29],     Sheet2  |
         [ 30,  31,  32,  33,  34],     |       |
         [ 35,  36,  37,  38,  39]],   -+       |
                                                |
        [[ 40,  41,  42,  43,  44],    -+       |
         [ 45,  46,  47,  48,  49],     Sheet3  |
         [ 50,  51,  52,  53,  54],     |       |
         [ 55,  56,  57,  58,  59]]],  -+      =+


       [[[ 60,  61,  62,  63,  64],
         [ 65,  66,  67,  68,  69],
         [ 70,  71,  72,  73,  74],
         [ 75,  76,  77,  78,  79]],

        [[ 80,  81,  82,  83,  84],
         [ 85,  86,  87,  88,  89],
         [ 90,  91,  92,  93,  94],
         [ 95,  96,  97,  98,  99]],

        [[100, 101, 102, 103, 104],
         [105, 106, 107, 108, 109],
         [110, 111, 112, 113, 114],
         [115, 116, 117, 118, 119]]]])

x hat eine wirklich leicht verständliche sequentielle Form, mit der wir jetzt zeigen können, was passiert ...

Die erste Dimension entspricht 2 Excel-Arbeitsmappen, die zweite Dimension entspricht 3 Blättern in jeder Arbeitsmappe, die dritte Dimension entspricht 4 Zeilen pro Blatt und die letzte Dimension besteht aus 5 Werten für jede Zeile (oder Spalten pro Blatt).

Wenn man es so betrachtet und danach fragt x[0,:,:,0], lautet das Sprichwort: "Geben Sie mir in der ersten Arbeitsmappe für jedes Blatt, für jede Zeile den ersten Wert / die erste Spalte."

x[0,:,:,y[0]]
# returns:
array([[ 0,  5, 10, 15],
       [20, 25, 30, 35],
       [40, 45, 50, 55]])

# this is in the same as the first element in:
x[(0,0,0),:,:,y]

Aber jetzt mit erweiterter Indizierung können wir uns vorstellen, dass x[(0,0,0),:,:,y]"in der ersten Arbeitsmappe für jedes Blatt, für jede Zeile der yth-Wert / die Spalte angegeben wird. Ok, machen Sie es jetzt für jeden Wert von y".

x[(0,0,0),:,:,y]
# returns:
array([[[ 0,  5, 10, 15],
        [20, 25, 30, 35],
        [40, 45, 50, 55]],

       [[ 2,  7, 12, 17],
        [22, 27, 32, 37],
        [42, 47, 52, 57]],

       [[ 4,  9, 14, 19],
        [24, 29, 34, 39],
        [44, 49, 54, 59]]])

Wo es verrückt wird, ist, dass numpy gesendet wird, um dem zu entsprechen äußeren Dimensionen des Index-Arrays zu entsprechen. Wenn Sie also denselben Vorgang wie oben ausführen möchten, jedoch für BEIDE "Excel-Arbeitsmappen", müssen Sie keine Schleifen erstellen und verketten. Sie können ein Array einfach an die erste Dimension übergeben, es MUSS jedoch eine kompatible Form haben.

Das Übergeben einer Ganzzahl wird an gesendet y.shape == (3,) . Wenn Sie ein Array als ersten Index übergeben möchten, muss nur die letzte Dimension des Arrays kompatibel sein y.shape. Das heißt, die letzte Dimension des ersten Index muss entweder 3 oder 1 sein.

ix = np.array([[0], [1]])
x[ix,:,:,y].shape
# each row of ix is broadcast to length 3:
(2, 3, 3, 4)

ix = np.array([[0,0,0], [1,1,1]])
x[ix,:,:,y].shape
# this is identical to above:
(2, 3, 3, 4)

ix = np.array([[0], [1], [0], [1], [0]])
x[ix,:,:,y].shape
# ix is broadcast so each row of ix has 3 columns, the length of y
(5, 3, 3, 4)

Eine kurze Erklärung in den Dokumenten gefunden: https://docs.scipy.org/doc/numpy/reference/arrays.indexing.html#combining-advanced-and-basic-indexing


Bearbeiten:

Aus der ursprünglichen Frage können Sie einen Einzeiler Ihres gewünschten Subslicing verwenden x[0][:,:,y] :

x[0][:,:,y].shape
# returns
(2, 50, 3)

Wenn Sie es jedoch versuchen , diesen Subslices zuzuweisen , müssen Sie sehr vorsichtig sein, dass Sie eine Shared-Memory-Ansicht des ursprünglichen Arrays betrachten. Andernfalls erfolgt die Zuordnung nicht zum ursprünglichen Array, sondern zu einer Kopie.

Shared Memory tritt nur auf, wenn Sie eine Ganzzahl oder ein Slice verwenden, um Ihr Array zu unterteilen, dh x[:,0:3,:,:]oder x[0,:,:,1:-1].

np.shares_memory(x, x[0])
# returns:
True

np.shares_memory(x, x[:,:,:,y])
# returns:
False

Sowohl in Ihrer ursprünglichen Frage als auch in meinem Beispiel y handelt es sich weder um ein Int noch um ein Slice. Daher wird immer eine Kopie des Originals zugewiesen.

ABER! Weil Ihre Array für yals eine Scheibe ausgedrückt werden, Sie CAN tatsächlich über eine frei belegbare Ansicht Ihres Array erhalten:

x[0,:,:,0:21:10].shape
# returns:
(2, 50, 3)

np.shares_memory(x, x[0,:,:,0:21:10])
# returns:
True

# actually assigns to the original array
x[0,:,:,0:21:10] = 100

Hier verwenden wir das Slice 0:21:10, um jeden Index zu erfassen, der sich in befinden würde range(0,21,10). Wir müssen verwenden 21und nicht20 weil der Stopppunkt wie in der rangeFunktion vom Slice ausgeschlossen ist .

Wenn Sie also ein Slice erstellen können, das Ihren Subslicing-Kriterien entspricht, können Sie eine Zuweisung vornehmen.

James
quelle
4

Es heißt combining advanced and basic indexing. In combining advanced and basic indexingnumpy führen Sie zuerst die Indizierung in der erweiterten Indizierung durch und verketten / verketten Sie das Ergebnis mit der Dimension der Basisindizierung.

Beispiel aus Dokumenten:

Sei x.shape (10,20,30,40,50) und nehme an, dass ind_1 und ind_2 in die Form (2,3,4) gesendet werden können. Dann hat x [:, ind_1, ind_2] die Form (10,2,3,4,40,50), weil der (20,30) -förmige Unterraum von X durch den (2,3,4) -Unterraum von ersetzt wurde die Indizes. Allerdings x [:, ind_1,:, ind_2] hat Form (2,3,4,10,30,50) , weil es kein eindeutiger Platz im Indizierungs Subraum fallen, so ist es gehefteten-on an den Anfang . Es ist immer möglich, mit .transpose () den Unterraum an eine beliebige Stelle zu verschieben. Beachten Sie, dass dieses Beispiel nicht mit take repliziert werden kann.

so, auf x[0,:,:,y], 0und ysind vorab Indizierung. Sie werden zusammen gesendet, um eine Dimension zu erhalten (3,).

In [239]: np.broadcast(0,y).shape
Out[239]: (3,)

Dies geht (3,)an den Anfang der 2. und 3. Dimension zu machen(3, 2, 50)

Um zu sehen , dass die erste und letzte Dimension wirklich zusammenträgt, können Sie versuchen , Änderung 0zu [0,1]den Fehlern des Rundfunks zu sehen

print(x[[0,1],:,:,y])

Output:
IndexError                                Traceback (most recent call last)
<ipython-input-232-5d10156346f5> in <module>
----> 1 x[[0,1],:,:,y]

IndexError: shape mismatch: indexing arrays could not be broadcast together with
 shapes (2,) (3,)
Andy L.
quelle