J'ai un tableau 1D dans numpy et je veux trouver la position de l'index où une valeur dépasse la valeur dans le tableau numpy.
Par exemple.
aa = range(-10,10)
Trouver la position dans aa
où la valeur 5
est dépassée.
C'est un peu plus rapide (et plus joli)
np.argmax(aa>5)
Puisque argmax
s'arrête au premier True
("En cas d'occurrences multiples des valeurs maximales, les indices correspondant à la première occurrence sont renvoyés.") et ne sauve pas une autre liste.
In [2]: N = 10000
In [3]: aa = np.arange(-N,N)
In [4]: timeit np.argmax(aa>N/2)
100000 loops, best of 3: 52.3 us per loop
In [5]: timeit np.where(aa>N/2)[0][0]
10000 loops, best of 3: 141 us per loop
In [6]: timeit np.nonzero(aa>N/2)[0][0]
10000 loops, best of 3: 142 us per loop
étant donné le contenu trié de votre tableau, il existe une méthode encore plus rapide: searchsorted .
import time
N = 10000
aa = np.arange(-N,N)
%timeit np.searchsorted(aa, N/2)+1
%timeit np.argmax(aa>N/2)
%timeit np.where(aa>N/2)[0][0]
%timeit np.nonzero(aa>N/2)[0][0]
# Output
100000 loops, best of 3: 5.97 µs per loop
10000 loops, best of 3: 46.3 µs per loop
10000 loops, best of 3: 154 µs per loop
10000 loops, best of 3: 154 µs per loop
In [34]: a=np.arange(-10,10)
In [35]: a
Out[35]:
array([-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2,
3, 4, 5, 6, 7, 8, 9])
In [36]: np.where(a>5)
Out[36]: (array([16, 17, 18, 19]),)
In [37]: np.where(a>5)[0][0]
Out[37]: 16
Cela m'a aussi intéressé et j'ai comparé toutes les réponses suggérées avec perfplot . (Avertissement: je suis l'auteur de perfplot.)
Si vous savez que le tableau que vous regardez est déjà trié , alors
numpy.searchsorted(a, alpha)
est pour toi. C’est une opération à temps constant, c’est-à-dire que la vitesse ne dépend pas de la taille du tableau. Vous ne pouvez pas aller plus vite que ça.
Si vous ne savez rien de votre tableau, vous ne vous trompez pas
numpy.argmax(a > alpha)
Déjà trié:
Non trié:
Code pour reproduire l'intrigue:
import numpy
import perfplot
alpha = 0.5
def argmax(data):
return numpy.argmax(data > alpha)
def where(data):
return numpy.where(data > alpha)[0][0]
def nonzero(data):
return numpy.nonzero(data > alpha)[0][0]
def searchsorted(data):
return numpy.searchsorted(data, alpha)
out = perfplot.show(
# setup=numpy.random.Rand,
setup=lambda n: numpy.sort(numpy.random.Rand(n)),
kernels=[
argmax, where,
nonzero,
searchsorted
],
n_range=[2**k for k in range(2, 20)],
logx=True,
logy=True,
xlabel='len(array)'
)
Dans le cas d'un range
ou de tout autre tableau à croissance linéaire, vous pouvez simplement calculer l'index par programmation, sans avoir à itérer du tout sur le tableau:
def first_index_calculate_range_like(val, arr):
if len(arr) == 0:
raise ValueError('no value greater than {}'.format(val))
Elif len(arr) == 1:
if arr[0] > val:
return 0
else:
raise ValueError('no value greater than {}'.format(val))
first_value = arr[0]
step = arr[1] - first_value
# For linearly decreasing arrays or constant arrays we only need to check
# the first element, because if that does not satisfy the condition
# no other element will.
if step <= 0:
if first_value > val:
return 0
else:
raise ValueError('no value greater than {}'.format(val))
calculated_position = (val - first_value) / step
if calculated_position < 0:
return 0
Elif calculated_position > len(arr) - 1:
raise ValueError('no value greater than {}'.format(val))
return int(calculated_position) + 1
On pourrait probablement améliorer cela un peu. Je me suis assuré qu'il fonctionne correctement pour quelques exemples de tableaux et de valeurs, mais cela ne signifie pas qu'il ne puisse pas y avoir d'erreur, en particulier étant donné qu'il utilise des flottants ...
>>> import numpy as np
>>> first_index_calculate_range_like(5, np.arange(-10, 10))
16
>>> np.arange(-10, 10)[16] # double check
6
>>> first_index_calculate_range_like(4.8, np.arange(-10, 10))
15
Etant donné qu'il peut calculer la position sans aucune itération, ce sera un temps constant (O(1)
) et peut probablement battre toutes les autres approches mentionnées. Cependant, cela nécessite une étape constante dans le tableau, sinon cela produira des résultats erronés.
Une approche plus générale consisterait à utiliser une fonction numba:
@nb.njit
def first_index_numba(val, arr):
for idx in range(len(arr)):
if arr[idx] > val:
return idx
return -1
Cela fonctionnera pour n'importe quel tableau, mais il devra parcourir le tableau, donc dans le cas moyen, il s'agira de O(n)
:
>>> first_index_numba(4.8, np.arange(-10, 10))
15
>>> first_index_numba(5, np.arange(-10, 10))
16
Même si Nico Schlömer a déjà fourni quelques points de repère, j’ai pensé qu’il pourrait être utile d’inclure mes nouvelles solutions et de tester différentes "valeurs".
La configuration du test:
import numpy as np
import math
import numba as nb
def first_index_using_argmax(val, arr):
return np.argmax(arr > val)
def first_index_using_where(val, arr):
return np.where(arr > val)[0][0]
def first_index_using_nonzero(val, arr):
return np.nonzero(arr > val)[0][0]
def first_index_using_searchsorted(val, arr):
return np.searchsorted(arr, val) + 1
def first_index_using_min(val, arr):
return np.min(np.where(arr > val))
def first_index_calculate_range_like(val, arr):
if len(arr) == 0:
raise ValueError('empty array')
Elif len(arr) == 1:
if arr[0] > val:
return 0
else:
raise ValueError('no value greater than {}'.format(val))
first_value = arr[0]
step = arr[1] - first_value
if step <= 0:
if first_value > val:
return 0
else:
raise ValueError('no value greater than {}'.format(val))
calculated_position = (val - first_value) / step
if calculated_position < 0:
return 0
Elif calculated_position > len(arr) - 1:
raise ValueError('no value greater than {}'.format(val))
return int(calculated_position) + 1
@nb.njit
def first_index_numba(val, arr):
for idx in range(len(arr)):
if arr[idx] > val:
return idx
return -1
funcs = [
first_index_using_argmax,
first_index_using_min,
first_index_using_nonzero,
first_index_calculate_range_like,
first_index_numba,
first_index_using_searchsorted,
first_index_using_where
]
from simple_benchmark import benchmark, MultiArgument
et les parcelles ont été générées en utilisant:
%matplotlib notebook
b.plot()
b = benchmark(
funcs,
{2**i: MultiArgument([0, np.arange(2**i)]) for i in range(2, 20)},
argument_name="array size")
La fonction numba fonctionne mieux, suivie de la fonction calculer et de la fonction tri par recherche. Les autres solutions fonctionnent beaucoup moins bien.
b = benchmark(
funcs,
{2**i: MultiArgument([2**i-2, np.arange(2**i)]) for i in range(2, 20)},
argument_name="array size")
Pour les petits tableaux, la fonction numba est incroyablement rapide, mais pour les grands tableaux, elle est surperformée par la fonction de calcul et la fonction searchsorted.
b = benchmark(
funcs,
{2**i: MultiArgument([np.sqrt(2**i), np.arange(2**i)]) for i in range(2, 20)},
argument_name="array size")
C'est plus intéressant. Encore une fois, numba et la fonction de calcul fonctionnent bien, mais cela déclenche en fait le pire cas de tri sélectif qui ne fonctionne vraiment pas bien dans ce cas.
Un autre point intéressant est le comportement de ces fonctions s’il n’ya pas de valeur dont l’index doit être retourné:
arr = np.ones(100)
value = 2
for func in funcs:
print(func.__name__)
try:
print('-->', func(value, arr))
except Exception as e:
print('-->', e)
Avec ce résultat:
first_index_using_argmax
--> 0
first_index_using_min
--> zero-size array to reduction operation minimum which has no identity
first_index_using_nonzero
--> index 0 is out of bounds for axis 0 with size 0
first_index_calculate_range_like
--> no value greater than 2
first_index_numba
--> -1
first_index_using_searchsorted
--> 101
first_index_using_where
--> index 0 is out of bounds for axis 0 with size 0
Searchsorted, argmax et numba renvoient simplement une valeur incorrecte. Cependant, searchsorted
et numba
renvoient un index qui n'est pas un index valide pour le tableau.
Les fonctions where
, min
, nonzero
et calculate
lèvent une exception. Cependant, seule l'exception pour calculate
dit quelque chose d'utile.
Cela signifie que vous devez en fait encapsuler ces appels dans une fonction d'encapsulage appropriée qui capture les exceptions ou les valeurs de retour non valides et les gère correctement, au moins si vous n'êtes pas sûr que la valeur puisse être dans le tableau.
Remarque: les options de calcul et searchsorted
ne fonctionnent que dans des conditions spéciales. La fonction "calculer" nécessite une étape constante et la recherche triée requiert le tri du tableau. Celles-ci pourraient donc être utiles dans les bonnes circonstances mais ne sont pas des solutions générales à ce problème. Si vous avez affaire à des listes triées Python, jetez un œil au module bisect au lieu d'utiliser Numpys. recherche triés.
Je voudrais proposer
np.min(np.append(np.where(aa>5)[0],np.inf))
Cela retournera le plus petit index où la condition est remplie, tout en renvoyant l'infini si la condition n'est jamais remplie (et where
renvoie un tableau vide).
J'irais avec
i = np.min(np.where(V >= x))
où V
est un vecteur (tableau 1d), x
est la valeur et i
est l'indice obtenu.