6.6. Indexelés és szeletelés¶
Egy ndarray négyféleképpen címezhető: egyedi indexekkel, szeletekkel, logikai maszkokkal, valamint ezek értékadásos formáival.
6.6.1. Egyedi elemek¶
A szögletes zárójeles indexelés az adott pozícióban lévő értéket adja vissza:
a = np.arange(10, dtype=np.uint8)
print(a[0], a[-1]) # 0 9
print(a[1], a[-2]) # 1 8
A negatív indexek a végétől számolnak, ugyanúgy, mint a Python list esetén. A tartományon kívüli index IndexError kivételt vált ki.
Magasabb rangú tömböknél minden tengely egy indexet kap. Az indexek egyetlen zárójelpáron belülre kerülnek, vesszővel elválasztva:
m = np.arange(9, dtype=np.uint8).reshape((3, 3))
print(m[1, 1]) # 4
print(m[2, 0]) # 6
Ha kevesebb indexet adsz meg, mint ahány tengely van, az indexeletlen tengelyek érintetlenül maradnak. Az eredmény a forrás egy csökkentett rangú nézete
print(m[0]) # the first row, as a 1-D view of m
6.6.2. Szeletek¶
Egy start:stop:step szelet a tömb egy nézetét adja vissza. A nézet az alapul szolgáló adatpuffert megosztja a forrással; a nézeten keresztüli írás a forrásba ír:
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)
Ha független pufferre van szükség, a copy() explicit módon létrehoz egyet.
A szeletelés természetesen kiterjed magasabb dimenziókra is. Minden tengely a saját szeletét kapja:
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
Egy egész szám (egyedi index, eldobja a tengelyt) és egy szelet (megtartja a tengelyt) keverése megengedett, és általában így írják az egy sor / egy oszlop eléréseket.
6.6.3. Logikai maszkok¶
Egy a forrással azonos alakú logikai tömb azokat az elemeket választja ki, ahol a maszk True. A logikai indexelés jelenleg 1 dimenziós tömbökön működik; a magasabb rangú bemenetek NotImplementedError kivételt váltanak ki:
a = np.arange(9, dtype=np.float)
mask = a < 5
print(a[mask])
Kimenet:
array([0.0, 1.0, 2.0, 3.0, 4.0], dtype=float)
A maszk egy közönséges bool ndarray, így bármilyen kifejezés működik, amely ilyet eredményez:
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])
A logikai indexelés egy másolatot ad vissza. A kiválasztott elemek azokon a pozíciókon helyezkednek el, ahol a maszk True – nem a forráson keresztüli szabályos lépésközzel –, így nincs olyan leíró, amelyet egy nézet a megcímzésükhöz használhatna, ezért az eredmény a saját pufferébe kerül megvalósításra.
6.6.4. Egész tömbös indexelés¶
Ha indexek listáját vagy tömbjét adod meg zárójelben, az egyetlen lépésben kiválasztja azokat az elemeket:
a = np.array([10, 20, 30, 40, 50], dtype=np.uint8)
a[[0, 2, 4]]
# array([10, 30, 50], dtype=uint8)
Az eredmény egy másolat; a kiválasztott elemek többé nem osztoznak a tárolón a forrással. Ugyanez a forma az értékadás bal oldalán is működik:
a[[0, 2, 4]] = 0
# array([0, 20, 0, 40, 0], dtype=uint8)
A take() (a Kiválasztás és átrendezés oldalon tárgyalva) ugyanezen művelet függvényformája, és elfogad egy out= kulcsszót a streamelő ciklusban való foglalásmentes használathoz.
6.6.5. Szeletértékadás¶
A szeletek és maszkok az értékadás bal oldalán is megjelennek, nemcsak a jobb oldalon. A jobb oldal lehet skalár, egy másik tömb vagy egy nézet:
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
A bal oldalon lévő logikai maszkok lecserélik azokat az elemeket, amelyek kielégítik a feltételt:
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. Miért fontos a szeletértékadás egy kamerán¶
A szeletértékadás egy már létező tömbön keresztül ír. Nem foglalódik új tömb. Ez a különbség az alábbi között:
out = a + b # makes a new array the size of a
out = out * 2 # makes another new array
és:
out[:] = a # writes into the existing out
out += b # in place
out *= 2 # in place
Az első változat két friss tömbnyi RAM-ot kér a kamerától; a második változat semmit sem kér. Egy korlátozott RAM-mal rendelkező mikrovezérlőn ez a különbség gyakran a kényelmesen futó szkript és a memóriából kifogyó szkript közötti különbség.
A Teljesítmény oldal részletesen tárgyalja a mintát. Egyelőre az a fontos szabály, hogy a szeletértékadás, a helyben módosító aritmetikai operátorok (+=, *=, …) és az univerzális függvények out= kulcsszava az a három eszköz, amely lehetővé teszi a foglalásmentes frissítéseket.