6.6. Indexare și segmentare¶
Un ndarray este adresat în patru moduri: indici unici, segmente, măști booleene și formele de atribuire ale fiecăruia.
6.6.1. Elemente unice¶
Indexarea cu paranteze pătrate returnează valoarea de la poziția dată:
a = np.arange(10, dtype=np.uint8)
print(a[0], a[-1]) # 0 9
print(a[1], a[-2]) # 1 8
Indicii negativi se numără de la sfârșit, la fel ca pentru un list Python. Un indice în afara intervalului ridică IndexError.
Pentru tablouri de rang superior, fiecare axă primește un indice. Indicii se pun într-un singur set de paranteze, separați prin virgule:
m = np.arange(9, dtype=np.uint8).reshape((3, 3))
print(m[1, 1]) # 4
print(m[2, 0]) # 6
Atunci când se furnizează mai puțini indici decât axe, axele neindexate sunt lăsate intacte. Rezultatul este o vedere cu rang redus a sursei:
print(m[0]) # the first row, as a 1-D view of m
6.6.2. Segmente¶
Un segment start:stop:step returnează o vedere a tabloului. Vederea partajează tamponul de date subiacent cu sursa; scrierea prin vedere scrie în sursă:
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)
Atunci când este nevoie de un tampon independent, copy() produce unul în mod explicit.
Segmentarea se extinde natural la dimensiuni superioare. Fiecare axă primește propriul segment:
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
Amestecarea unui întreg (indice unic, elimină axa) cu un segment (păstrează axa) este permisă și este modul în care se scrie în mod normal accesul la un singur rând / o singură coloană.
6.6.3. Măști booleene¶
Un tablou boolean de aceeași formă ca sursa selectează elementele unde masca este True. Indexarea booleană funcționează în prezent pe tablouri 1-D; intrările de rang superior ridică NotImplementedError
a = np.arange(9, dtype=np.float)
mask = a < 5
print(a[mask])
Ieșire:
array([0.0, 1.0, 2.0, 3.0, 4.0], dtype=float)
Masca este un ndarray obișnuit de tip bool, așa că orice expresie care produce una funcționează:
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])
Indexarea booleană returnează o copie. Elementele selectate se află la orice poziții unde masca este True – nu la un pas regulat prin sursă – deci nu există niciun descriptor pe care o vedere l-ar putea folosi pentru a le adresa, iar rezultatul este materializat în propriul tampon.
6.6.4. Indexare cu tablou de întregi¶
Transmiterea unei liste sau a unui tablou de indici între paranteze extrage acele elemente într-un singur pas:
a = np.array([10, 20, 30, 40, 50], dtype=np.uint8)
a[[0, 2, 4]]
# array([10, 30, 50], dtype=uint8)
Rezultatul este o copie; elementele extrase nu mai partajează stocarea cu sursa. Aceeași formă funcționează în stânga unei atribuiri:
a[[0, 2, 4]] = 0
# array([0, 20, 0, 40, 0], dtype=uint8)
take() (tratată în Selectare și rearanjare) este forma de funcție a aceleiași operații și acceptă un cuvânt-cheie out= pentru utilizare fără alocare într-o buclă de tip streaming.
6.6.5. Atribuire de segment¶
Segmentele și măștile apar în stânga unei atribuiri la fel ca în dreapta. Partea din dreapta poate fi un scalar, un alt tablou sau o vedere:
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
Măștile booleene din stânga înlocuiesc elementele care satisfac condiția:
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. De ce contează atribuirea de segment pe o cameră¶
Atribuirea de segment scrie printr-un tablou care există deja. Niciun tablou nou nu este alocat. Aceasta este diferența dintre:
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
Prima versiune solicită de la cameră RAM cât pentru două tablouri noi; a doua versiune nu solicită nimic. Pe un microcontroler cu RAM limitat, acea diferență este adesea diferența dintre un script care rulează confortabil și unul care rămâne fără memorie.
Performanță tratează tiparul în detaliu. Regula importantă deocamdată este că atribuirea de segment, operatorii aritmetici in-place (+=, *=, …) și cuvântul-cheie out= de la funcțiile universale sunt cele trei instrumente care fac posibile actualizările fără alocare.