Je sais qu'il existe une méthode permettant à une liste Python de renvoyer le premier index de quelque chose:
>>> l = [1, 2, 3]
>>> l.index(2)
1
Existe-t-il quelque chose comme ça pour les tableaux NumPy?
Oui, voici la réponse: un tableau NumPy, array
, et une valeur, item
, à rechercher:
itemindex = numpy.where(array==item)
Le résultat est un tuple avec d'abord tous les index de lignes, puis tous les index de colonnes.
Par exemple, si un tableau a deux dimensions et qu'il contient votre élément à deux emplacements,
array[itemindex[0][0]][itemindex[1][0]]
serait égal à votre article et le ferait
array[itemindex[0][1]][itemindex[1][1]]
Si vous avez besoin de l'index de la première occurrence de ne seule valeur, vous pouvez utiliser nonzero
(ou where
, ce qui revient au même dans ce cas):
>>> t = array([1, 1, 1, 2, 2, 3, 8, 3, 8, 8])
>>> nonzero(t == 8)
(array([6, 8, 9]),)
>>> nonzero(t == 8)[0][0]
6
Si vous avez besoin du premier index de chacun des plusieurs valeurs, vous pouvez évidemment faire la même chose que ci-dessus à plusieurs reprises, mais il existe une astuce qui peut être plus rapide. Ce qui suit trouve les indices du premier élément de chaque sous-séquence :
>>> nonzero(r_[1, diff(t)[:-1]])
(array([0, 3, 5, 6, 7, 8]),)
Notez qu'il trouve le début de la sous-séquence de 3 et des deux sous-séquences de 8:
[1, 1, 1, 2, 2, , 8, =, 8, 8]
Il est donc légèrement différent de trouver la première occurrence de chaque valeur. Dans votre programme, vous pourrez peut-être utiliser une version triée de t
pour obtenir ce que vous voulez:
>>> st = sorted(t)
>>> nonzero(r_[1, diff(st)[:-1]])
(array([0, 3, 5, 7]),)
Vous pouvez également convertir un tableau NumPy en liste et obtenir son index. Par exemple,
l = [1,2,3,4,5] # Python list
a = numpy.array(l) # NumPy array
i = a.tolist().index(2) # i will return index of 2
print i
Il imprimera 1.
Juste pour ajouter une alternative très performante et pratique numba basée sur np.ndenumerate
pour trouver le premier index:
_from numba import njit
import numpy as np
@njit
def index(array, item):
for idx, val in np.ndenumerate(array):
if val == item:
return idx
# If no item was found return None, other return types might be a problem due to
# numbas type inference.
_
C'est assez rapide et gère naturellement les tableaux multidimensionnels :
_>>> arr1 = np.ones((100, 100, 100))
>>> arr1[2, 2, 2] = 2
>>> index(arr1, 2)
(2, 2, 2)
>>> arr2 = np.ones(20)
>>> arr2[5] = 2
>>> index(arr2, 2)
(5,)
_
Cela peut être beaucoup plus rapide (parce que l'opération court-circuite) que n'importe quelle approche utilisant _np.where
_ ou _np.nonzero
_.
Cependant np.argwhere
pourrait également traiter gracieusement avec des tableaux multidimensionnels (vous devrez le convertir manuellement en un tuple et il n'est pas court-circuité) mais il échouera si aucune correspondance n'est trouvée:
_>>> Tuple(np.argwhere(arr1 == 2)[0])
(2, 2, 2)
>>> Tuple(np.argwhere(arr2 == 2)[0])
(5,)
_
Si vous allez utiliser cela comme un index dans quelque chose d'autre, vous pouvez utiliser des index booléens si les tableaux sont diffusables; vous n'avez pas besoin d'indices explicites. Le moyen le plus simple consiste à indexer simplement sur une valeur de vérité.
other_array[first_array == item]
Toute opération booléenne fonctionne:
a = numpy.arange(100)
other_array[first_array > 50]
La méthode non nulle prend aussi des booléens:
index = numpy.nonzero(first_array == item)[0][0]
Les deux zéros correspondent au nuplet d'indices (en supposant que first_array est 1D), puis au premier élément du tableau d'indices.
l.index(x)
renvoie le plus petit i tel que i soit le index de la première occurrence de x dans la liste.
On peut supposer en toute sécurité que la fonction index()
dans Python est implémentée de manière à s’arrêter après la recherche du premier match, ce qui donne une performance moyenne optimale.
Pour trouver un élément qui s'arrête après la première correspondance dans un tableau NumPy, utilisez un itérateur ( ndenumerate ).
In [67]: l=range(100)
In [68]: l.index(2)
Out[68]: 2
Tableau NumPy:
In [69]: a = np.arange(100)
In [70]: next((idx for idx, val in np.ndenumerate(a) if val==2))
Out[70]: (2L,)
Notez que les deux méthodes index()
et next
renvoient une erreur si l'élément n'est pas trouvé. Avec next
, on peut utiliser un deuxième argument pour renvoyer une valeur spéciale au cas où l'élément n'est pas trouvé, par ex.
In [77]: next((idx for idx, val in np.ndenumerate(a) if val==400),None)
Il existe d'autres fonctions dans NumPy (argmax
, where
et nonzero
) pouvant être utilisées pour rechercher un élément dans un tableau, mais elles présentent toutes l'inconvénient de parcourir l'ensemble du tableau. recherche de toutes les occurrences , ce qui permet d'optimiser la recherche du premier élément. Notez également que where
et nonzero
renvoient des tableaux, vous devez donc sélectionner le premier élément pour obtenir l'index.
In [71]: np.argmax(a==2)
Out[71]: 2
In [72]: np.where(a==2)
Out[72]: (array([2], dtype=int64),)
In [73]: np.nonzero(a==2)
Out[73]: (array([2], dtype=int64),)
Il suffit de vérifier que pour les grands tableaux, la solution utilisant un itérateur est plus rapide lorsque l'élément recherché se trouve au début du tableau (en utilisant %timeit
in le shell IPython):
In [285]: a = np.arange(100000)
In [286]: %timeit next((idx for idx, val in np.ndenumerate(a) if val==0))
100000 loops, best of 3: 17.6 µs per loop
In [287]: %timeit np.argmax(a==0)
1000 loops, best of 3: 254 µs per loop
In [288]: %timeit np.where(a==0)[0][0]
1000 loops, best of 3: 314 µs per loop
Ceci est un open numéro de NumPy GitHub .
Voir aussi: Numpy: recherche rapidement le premier index de la valeur
Pour indexer sur n'importe quel critère, vous pouvez utiliser quelque chose comme ceci:
In [1]: from numpy import *
In [2]: x = arange(125).reshape((5,5,5))
In [3]: y = indices(x.shape)
In [4]: locs = y[:,x >= 120] # put whatever you want in place of x >= 120
In [5]: pts = hsplit(locs, len(locs[0]))
In [6]: for pt in pts:
.....: print(', '.join(str(p[0]) for p in pt))
4, 4, 0
4, 4, 1
4, 4, 2
4, 4, 3
4, 4, 4
Et voici une fonction rapide permettant de faire ce que list.index () fait, sauf que cela ne déclenche pas d'exception s'il n'est pas trouvé. Attention - ceci est probablement très lent sur les grands tableaux. Si vous préférez l’utiliser comme méthode, vous pouvez probablement l’appliquer à des tableaux.
def ndindex(ndarray, item):
if len(ndarray.shape) == 1:
try:
return [ndarray.tolist().index(item)]
except:
pass
else:
for i, subarray in enumerate(ndarray):
try:
return [i] + ndindex(subarray, item)
except:
pass
In [1]: ndindex(x, 103)
Out[1]: [4, 0, 3]
Pour les matrices 1D, je recommanderais np.flatnonzero(array == value)[0]
, ce qui équivaut à la fois à np.nonzero(array == value)[0][0]
et à np.where(array == value)[0][0]
, mais évite la laideur de la décompression d'un tuple à 1 élément.
Pour les tableaux unidimensionnels triés , il serait beaucoup plus simple et efficace O(log(n)) d'utiliser numpy.searchsorted qui renvoie un entier NumPy (position). Par exemple,
arr = np.array([1, 1, 1, 2, 3, 3, 4])
i = np.searchsorted(arr, 3)
Assurez-vous simplement que le tableau est déjà trié
Vérifiez également si l'index renvoyé i contient réellement l'élément recherché, puisque l'objectif principal de searchsorted est de rechercher des index où des éléments doivent être insérés pour maintenir l'ordre.
if arr[i] == 3:
print("present")
else:
print("not present")
De nombreuses opérations dans NumPy pourraient peut-être être mises en place pour atteindre cet objectif. Cela retournera des indices d'éléments égaux à item:
numpy.nonzero(array - item)
Vous pouvez ensuite utiliser les premiers éléments des listes pour obtenir un seul élément.
Une alternative à la sélection du premier élément de np.where () consiste à utiliser une expression génératrice avec énumération, telle que:
>>> import numpy as np
>>> x = np.arange(100) # x = array([0, 1, 2, 3, ... 99])
>>> next(i for i, x_i in enumerate(x) if x_i == 2)
2
Pour un tableau à deux dimensions, on ferait:
>>> x = np.arange(100).reshape(10,10) # x = array([[0, 1, 2,... 9], [10,..19],])
>>> next((i,j) for i, x_i in enumerate(x)
... for j, x_ij in enumerate(x_i) if x_ij == 2)
(0, 2)
L'avantage de cette approche est qu'elle arrête de vérifier les éléments du tableau une fois la première correspondance trouvée, alors que np.where vérifie la correspondance de tous les éléments. Une expression génératrice serait plus rapide s'il y a correspondance au début du tableau.
Le package numpy_indexed (disclaimer, je suis son auteur) contient un équivalent vectorisé de list.index pour numpy.ndarray; C'est:
sequence_of_arrays = [[0, 1], [1, 2], [-5, 0]]
arrays_to_query = [[-5, 0], [1, 0]]
import numpy_indexed as npi
idx = npi.indices(sequence_of_arrays, arrays_to_query, missing=-1)
print(idx) # [2, -1]
Cette solution a vectorisé les performances, généralisée à ndarrays et dispose de différentes manières de traiter les valeurs manquantes.