Existe-t-il une fonction NumPy pour renvoyer le premier index de quelque chose dans un tableau?
Question
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?
La solution
Oui, voici la réponse: un tableau NumPy, tableau
et une valeur, élément
, dans lesquels 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, alors
array[itemindex[0][0]][itemindex[1][0]]
serait égal à votre élément et il en serait de même
array[itemindex[0][1]][itemindex[1][1]]
Autres conseils
Si vous avez besoin de l'index de la première occurrence de une seule valeur , vous pouvez utiliser non nul
(ou où
, ce qui revient à même chose 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 chacune des nombreuses valeurs , vous pouvez évidemment procéder de la même manière que ci-dessus, mais il existe une astuce qui peut être plus rapide. Vous trouverez ci-dessous 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 des deux sous-séquences de 3 et de 8s:
[ 1 , 1, 1, 2 , 2, 3 , 8 , 3 , 8 , 8]
Il est donc légèrement différent de rechercher 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.
Si vous voulez utiliser cela comme un index vers quelque chose d'autre, vous pouvez utiliser des index booléens si les tableaux sont diffusables; vous n'avez pas besoin d'indices explicites. Pour ce faire, la méthode la 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 également des booléens:
index = numpy.nonzero(first_array == item)[0][0]
Les deux zéros correspondent au tuple d'indices (en supposant que first_array est 1D), puis au premier élément du tableau d'indices.
Juste pour ajouter un numba Alternative basée sur np.ndenumerate
. / a> 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 traite 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 (car il court-circuite l'opération) que toute approche utilisant np.where
ou np.nonzero
.
Cependant, np.argwhere
peut également traiter gracieusement des tableaux multidimensionnels (vous devez le convertir manuellement en un tuple et il n'est pas court-circuité), mais il échouera si aucune correspondance n'est trouvée. trouvé:
>>> tuple(np.argwhere(arr1 == 2)[0])
(2, 2, 2)
>>> tuple(np.argwhere(arr2 == 2)[0])
(5,)
l.index (x)
renvoie le plus petit i tel que i soit l'index de la première occurrence de x dans la liste.
On peut supposer en toute sécurité que la fonction index ()
en Python est implémentée de sorte qu'elle s'arrête après la recherche de la première correspondance, ce qui entraîne des performances moyennes optimales.
Pour rechercher un élément s'arrêtant après la première correspondance d'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 suivant
renvoient une erreur si l'élément n'est pas trouvé. Avec next
, vous pouvez utiliser un deuxième argument pour renvoyer une valeur spéciale au cas où l'élément ne serait pas trouvé, par exemple.
In [77]: next((idx for idx, val in np.ndenumerate(a) if val==400),None)
Il existe d'autres fonctions dans NumPy ( argmax
, où
et non nul
) qui peuvent être utilisées pour rechercher un élément dans un tableau, mais ils ont tous l’inconvénient de parcourir l’ensemble du tableau à la recherche de toutes occurrences, de sorte qu’elles ne sont pas optimisées pour la recherche du premier élément. Notez également que where
et non nul
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),)
Comparaison du temps
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
dans 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
Il s'agit d'un numéro de NumPy GitHub ouvert.
Voir aussi: Numpy: recherche rapidement le premier index de 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 appliquer ce correctif à des tableaux à l'aide de singe.
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 tableaux 1D, je recommanderais np.flatnonzero (tableau == valeur) [0]
, ce qui équivaut à la fois np.nonzero (tableau == valeur) [0 ] [0]
et np.where (tableau == valeur) [0] [0]
, mais évite la laideur de déconditionner un tuple à 1 élément.
NumPy propose de nombreuses opérations qui pourraient ê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.
Pour les tableaux triés unidimensionnels, il serait beaucoup plus simple et efficace d’utiliser 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é, l'objectif principal de searchsorted étant 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")
Une alternative à la sélection du premier élément de np.where () consiste à utiliser une expression génératrice avec enumerate, 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 lorsque la première correspondance est 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 avait une correspondance au début du tableau.
Le package numpy_indexed contient un équivalent vectorisé de list.index. pour numpy.ndarray; c'est-à-dire:
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 façons de traiter les valeurs manquantes.
Remarque: il s'agit de la version Python 2.7
Vous pouvez utiliser une fonction lambda pour traiter le problème et cela fonctionne à la fois sur les tableaux et les listes NumPy.
your_list = [11, 22, 23, 44, 55]
result = filter(lambda x:your_list[x]>30, range(len(your_list)))
#result: [3, 4]
import numpy as np
your_numpy_array = np.array([11, 22, 23, 44, 55])
result = filter(lambda x:your_numpy_array [x]>30, range(len(your_list)))
#result: [3, 4]
Et vous pouvez utiliser
result[0]
pour obtenir le premier index des éléments filtrés.
Pour Python 3.6, utilisez
list(result)
au lieu de
result