6.6. Indexation et tranchage¶
Un ndarray s’adresse de quatre manières : indices simples, tranches, masques booléens, et les formes d’affectation de chacun.
6.6.1. Éléments individuels¶
L’indexation par crochets renvoie la valeur à la position donnée
a = np.arange(10, dtype=np.uint8)
print(a[0], a[-1]) # 0 9
print(a[1], a[-2]) # 1 8
Les indices négatifs comptent à partir de la fin, comme pour une list Python. Un indice hors limites lève IndexError.
Pour les tableaux de rang supérieur, chaque axe prend un indice. Les indices vont dans un seul jeu de crochets, séparés par des virgules
m = np.arange(9, dtype=np.uint8).reshape((3, 3))
print(m[1, 1]) # 4
print(m[2, 0]) # 6
Lorsque moins d’indices que d’axes sont fournis, les axes non indexés sont laissés intacts. Le résultat est une vue de rang réduit de la source
print(m[0]) # the first row, as a 1-D view of m
6.6.2. Tranches¶
Une tranche start:stop:step renvoie une vue du tableau. La vue partage le tampon de données sous-jacent avec la source ; écrire à travers la vue écrit dans la source
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)
Lorsqu’un tampon indépendant est nécessaire, copy() en produit un explicitement.
Le tranchage s’étend naturellement aux dimensions supérieures. Chaque axe prend sa propre tranche
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
Mélanger un entier (indice simple, supprime l’axe) et une tranche (conserve l’axe) est autorisé et constitue la façon normale d’écrire un accès à une seule ligne ou une seule colonne.
6.6.3. Masques booléens¶
Un tableau booléen de même forme que la source sélectionne les éléments où le masque vaut True. L’indexation booléenne fonctionne actuellement sur les tableaux 1-D ; les entrées de rang supérieur lèvent NotImplementedError
a = np.arange(9, dtype=np.float)
mask = a < 5
print(a[mask])
Sortie
array([0.0, 1.0, 2.0, 3.0, 4.0], dtype=float)
Le masque est un ndarray bool ordinaire, donc toute expression qui en produit un fonctionne
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])
L’indexation booléenne renvoie une copie. Les éléments sélectionnés se trouvent aux positions où le masque vaut True – pas à un pas régulier à travers la source – il n’existe donc aucun descripteur qu’une vue pourrait utiliser pour les adresser, et le résultat est matérialisé dans son propre tampon.
6.6.4. Indexation par tableau d’entiers¶
Passer une liste ou un tableau d’indices entre crochets sélectionne ces éléments en une seule étape
a = np.array([10, 20, 30, 40, 50], dtype=np.uint8)
a[[0, 2, 4]]
# array([10, 30, 50], dtype=uint8)
Le résultat est une copie ; les éléments sélectionnés ne partagent plus de stockage avec la source. La même forme fonctionne à gauche d’une affectation
a[[0, 2, 4]] = 0
# array([0, 20, 0, 40, 0], dtype=uint8)
take() (traité sur Sélection et réorganisation) est la forme fonction de la même opération et accepte un mot-clé out= pour une utilisation sans allocation dans une boucle de streaming.
6.6.5. Affectation par tranche¶
Les tranches et les masques apparaissent à gauche d’une affectation autant qu’à droite. Le membre de droite peut être un scalaire, un autre tableau ou une vue
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
Les masques booléens à gauche remplacent les éléments qui satisfont la condition
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. Pourquoi l’affectation par tranche compte sur une caméra¶
L’affectation par tranche écrit à travers un tableau qui existe déjà. Aucun nouveau tableau n’est alloué. C’est la différence entre
out = a + b # makes a new array the size of a
out = out * 2 # makes another new array
et
out[:] = a # writes into the existing out
out += b # in place
out *= 2 # in place
La première version demande à la caméra la RAM de deux nouveaux tableaux ; la seconde version ne demande rien. Sur un microcontrôleur à RAM limitée, cette différence est souvent la différence entre un script qui s’exécute confortablement et un script qui manque de mémoire.
Performances traite ce schéma en détail. La règle importante pour l’instant est que l’affectation par tranche, les opérateurs arithmétiques en place (+=, *=, …) et le mot-clé out= des fonctions universelles sont les trois outils qui rendent possibles les mises à jour sans allocation.