6.6. Indeksiranje i rezanje¶
ndarray se adresira na četiri načina: pojedinačnim indeksima, rezovima, booleovim maskama i oblicima dodjele za svaki od njih.
6.6.1. Pojedinačni elementi¶
Indeksiranje uglatim zagradama vraća vrijednost na zadanoj poziciji:
a = np.arange(10, dtype=np.uint8)
print(a[0], a[-1]) # 0 9
print(a[1], a[-2]) # 1 8
Negativni indeksi broje od kraja, isto kao kod Python list. Indeks izvan raspona izaziva IndexError.
Za polja višeg ranga, svaka os uzima jedan indeks. Indeksi idu unutar jednog skupa zagrada, odvojeni zarezima:
m = np.arange(9, dtype=np.uint8).reshape((3, 3))
print(m[1, 1]) # 4
print(m[2, 0]) # 6
Kada se navede manje indeksa nego osi, neindeksirane osi ostaju netaknute. Rezultat je pogled smanjenog ranga na izvor:
print(m[0]) # the first row, as a 1-D view of m
6.6.2. Rezovi¶
Rez start:stop:step vraća pogled na polje. Pogled dijeli temeljni međuspremnik podataka s izvorom; pisanje kroz pogled piše u izvor:
a = np.arange(10, dtype=np.uint8)
v = a[::2] # array([0, 2, 4, 6, 8], dtype=uint8)
v[0] = 99
print(a)
# array([99, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=uint8)
Kada je potreban neovisan međuspremnik, copy() ga proizvodi eksplicitno.
Rezanje se prirodno proširuje na više dimenzija. Svaka os uzima svoj rez:
m = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]], dtype=np.uint8)
m[0] # first row
m[0, :2] # first two elements of row 0
m[:, 0] # column 0 (still 2-D in ulab)
m[-1] # last row
m[::2, ::2] # every other row, every other column
Miješanje cijelog broja (pojedinačni indeks, izbacuje os) i reza (zadržava os) je dopušteno i tako se obično piše pristup pojedinom retku / pojedinom stupcu.
6.6.3. Booleove maske¶
Booleovo polje istog oblika kao izvor odabire elemente gdje je maska True. Booleovo indeksiranje trenutno radi na 1-D poljima; ulazi višeg ranga izazivaju NotImplementedError
a = np.arange(9, dtype=np.float)
mask = a < 5
print(a[mask])
Izlaz:
array([0.0, 1.0, 2.0, 3.0, 4.0], dtype=float)
Maska je obično bool ndarray, pa funkcionira bilo koji izraz koji je daje:
b = np.array([4, 4, 4, 3, 3, 3, 13, 13, 13], dtype=np.uint8)
a = np.arange(9, dtype=np.uint8)
print(a[a * a > np.sin(b) * 100.0])
Booleovo indeksiranje vraća kopiju. Odabrani elementi leže na bilo kojim pozicijama gdje je maska True – ne na pravilnom koraku kroz izvor – pa ne postoji deskriptor koji bi ih pogled mogao adresirati, te se rezultat materijalizira u vlastiti međuspremnik.
6.6.4. Indeksiranje cjelobrojnim poljem¶
Prosljeđivanje liste ili polja indeksa u zagradama izdvaja te elemente u jednom koraku:
a = np.array([10, 20, 30, 40, 50], dtype=np.uint8)
a[[0, 2, 4]]
# array([10, 30, 50], dtype=uint8)
Rezultat je kopija; odabrani elementi više ne dijele pohranu s izvorom. Isti oblik funkcionira na lijevoj strani dodjele:
a[[0, 2, 4]] = 0
# array([0, 20, 0, 40, 0], dtype=uint8)
take() (obrađeno na Odabir i preraspoređivanje) je funkcijski oblik iste operacije i prihvaća ključnu riječ out= za korištenje bez alokacije u petlji za obradu toka.
6.6.5. Dodjela rezanjem¶
Rezovi i maske pojavljuju se na lijevoj strani dodjele kao i na desnoj. Desna strana može biti skalar, drugo polje ili pogled:
m = np.zeros((3, 3), dtype=np.uint8)
m[0] = 1 # whole row 0 set to 1
m[:, 2] = 3 # whole column 2 set to 3
m[1, 1:3] = [7, 8] # row 1, columns 1 and 2
Booleove maske na lijevoj strani zamjenjuju elemente koji zadovoljavaju uvjet:
a = np.arange(9, dtype=np.uint8)
a[a < 3] = 99
# array([99, 99, 99, 3, 4, 5, 6, 7, 8], dtype=uint8)
a = np.arange(9, dtype=np.uint8)
b = np.array(range(9)) + 12
a[b < 15] = b[b < 15]
# array([12, 13, 14, 3, 4, 5, 6, 7, 8], dtype=uint8)
6.6.6. Zašto je dodjela rezanjem važna na kameri¶
Dodjela rezanjem piše kroz polje koje već postoji. Nijedno novo polje se ne alocira. To je razlika između:
out = a + b # makes a new array the size of a
out = out * 2 # makes another new array
i:
out[:] = a # writes into the existing out
out += b # in place
out *= 2 # in place
Prva verzija traži od kamere RAM za dva svježa polja; druga verzija ne traži ništa. Na mikrokontroleru s ograničenim RAM-om ta razlika često predstavlja razliku između skripte koja se izvodi udobno i one kojoj ponestane memorije.
Performanse detaljno obrađuje ovaj obrazac. Za sada je važno pravilo da su dodjela rezanjem, aritmetički operatori na licu mjesta (+=, *=, …) i ključna riječ out= na univerzalnim funkcijama tri alata koji omogućuju ažuriranja bez alokacije.