Dziwne indeksowanie przy użyciu numpy

27

Mam zmienną x, która ma kształt (2,2,50,100).

Mam też tablicę y, która jest równa np. Tablica ([0,10,20]). Dziwna rzecz dzieje się, gdy indeksuję x [0,:,:, y].

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)

Dlaczego ostatnie wyjście (3,2,50), a nie (2,50,3)?

Paul Scotti
źródło
Jestem nowy na numpy, więc nie mam odpowiedzi na twoje pytanie. Aby zbadać to dalej, sugeruję znalezienie mniejszego przykładu, który jest tylko 2D lub 3D i jest podobny jak najwyżej 10 elementów na dowolnej osi.
Uczeń kodowy

Odpowiedzi:

21

W ten sposób numpy wykorzystuje zaawansowane indeksowanie do rozgłaszania kształtów tablic. Kiedy zdasz a 0dla pierwszego indeksu i ydla ostatniego indeksu, numpy wyemituje 0ten sam kształt co y. Zachodzi równoważność: x[0,:,:,y] == x[(0, 0, 0),:,:,y]. Oto przykład

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

Teraz, ponieważ skutecznie przekazujesz dwa zestawy indeksów, używasz zaawansowanego interfejsu API indeksowania do tworzenia (w tym przypadku) par indeksów.

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)
]

Który ma pierwszy wymiar taki sam jak długość y. Oto co widzisz.

Jako przykład spójrz na tablicę z 4 wymiarami opisanymi w następnym fragmencie:

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 ma naprawdę łatwą do zrozumienia sekwencyjną formę, której możemy teraz użyć do pokazania, co się dzieje ...

Pierwszy wymiar przypomina 2 skoroszyty programu Excel, drugi wymiar ma 3 arkusze w każdym skoroszycie, trzeci wymiar ma 4 wiersze na arkusz, a ostatni wymiar to 5 wartości dla każdego wiersza (lub kolumn na arkusz).

Patrząc na to w ten sposób, prosząc x[0,:,:,0], jest powiedzenie: „w pierwszym skoroszycie, dla każdego arkusza, dla każdego wiersza, podaj mi pierwszą wartość / kolumnę”.

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]

Ale teraz dzięki zaawansowanemu indeksowaniu możemy myśleć o tym, x[(0,0,0),:,:,y]że „w pierwszym skoroszycie, dla każdego arkusza, dla każdego wiersza podaj mi ywartość th / kolumnę. Ok, teraz zrób to dla każdej wartości 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]]])

To, że wariuje, to to, że numpy będzie nadawać, aby pasowało do zewnętrznych wymiarów tablicy indeksów. Więc jeśli chcesz wykonać tę samą operację jak powyżej, ale ZARÓWNO dla „skoroszytów programu Excel”, nie musisz zapętlać i łączyć. Możesz po prostu przekazać tablicę do pierwszego wymiaru, ale MUSI mieć zgodny kształt.

Przekazywanie liczby całkowitej zostaje przekazane do y.shape == (3,). Jeśli chcesz przekazać tablicę jako pierwszy indeks, tylko ostatni wymiar tablicy musi być zgodny y.shape. To znaczy ostatni wymiar pierwszego indeksu musi wynosić 3 lub 1.

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)

Znaleziono krótkie wyjaśnienie w dokumentacji: https://docs.scipy.org/doc/numpy/reference/arrays.indexing.html#combining-advanced-and-basic-indexing


Edytować:

Z pierwotnego pytania, aby uzyskać linijkę żądanego subplikowania, możesz użyć x[0][:,:,y]:

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

Jeśli jednak próbujesz przypisać do tych podsieci, musisz bardzo uważać, patrząc na widok pamięci współdzielonej oryginalnej tablicy. W przeciwnym razie przypisanie nie będzie do oryginalnej tablicy, ale do kopii.

Pamięć współdzielona występuje tylko wtedy, gdy używasz liczby całkowitej lub wycinka do podzestawu tablicy, tj . x[:,0:3,:,:]Lub x[0,:,:,1:-1].

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

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

Zarówno w twoim pierwotnym pytaniu, jak i moim przykładzie, ynie jest ani int, ani plasterek, więc zawsze kończy się przypisaniem do kopii oryginału.

ALE! Ze względu na swoją tablicę ymożna wyrazić jako plaster, ty CAN rzeczywiście uzyskać przypisywany widok macierzy poprzez:

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

Tutaj używamy wycinka, 0:21:10aby pobrać każdy indeks, który byłby w środku range(0,21,10). Musimy użyć, 21a nie 20dlatego, że punkt zatrzymania jest wykluczony z wycinka, tak jak w rangefunkcji.

Zasadniczo, jeśli możesz skonstruować plasterek, który pasuje do twoich kryteriów subplikowania, możesz wykonać przypisanie.

James
źródło
4

To się nazywa combining advanced and basic indexing. W combining advanced and basic indexing, numpy wykonaj najpierw indeksowanie w indeksowaniu zaawansowanym i podprzestrzeń / połącz wynik z wymiarem indeksowania podstawowego.

Przykład z dokumentów:

Niech x.shape będzie (10,20,30,40,50) i załóżmy, że ind_1 i ind_2 mogą być nadawane do kształtu (2,3,4). Następnie x [:, ind_1, ind_2] ma kształt (10,2,3,4,40,50), ponieważ podprzestrzeń w kształcie (20,30) z X została zastąpiona podprzestrzenią (2,3,4) z wskaźniki. Jednak x [:, ind_1,:, ind_2] ma kształt (2,3,4,10,30,50,50), ponieważ nie ma jednoznacznego miejsca na upuszczenie w podprzestrzeni indeksowania, dlatego jest on sczepiony na początku . Zawsze można użyć funkcji .transpose (), aby przenieść podprzestrzeń w dowolne miejsce. Zauważ, że tego przykładu nie można replikować przy użyciu take.

tak, na x[0,:,:,y], 0i ysą zaliczki indeksowania. Są one emitowane razem, aby uzyskać wymiar (3,).

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

Ten (3,)pinezki do początku 2. i 3. Aby dokonać wymiaru(3, 2, 50)

Aby zobaczyć, że 1 i ostatni wymiar są naprawdę nadawania razem, można spróbować zmienić 0aby [0,1]zobaczyć błędu nadawania

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.
źródło