web-dev-qa-db-fra.com

Comment obtenir des indices de N valeurs maximales dans un tableau NumPy?

NumPy propose un moyen d'obtenir l'index de la valeur maximale d'un tableau via np.argmax.

Je voudrais une chose similaire, mais en retournant les index des valeurs maximales N.

Par exemple, si j'ai un tableau, [1, 3, 2, 4, 5], function(array, n=3) renverra les indices [4, 3, 1] qui correspondent aux éléments [5, 4, 3].

392
Alexis Métaireau

Le plus simple que j'ai pu trouver est:

In [1]: import numpy as np

In [2]: arr = np.array([1, 3, 2, 4, 5])

In [3]: arr.argsort()[-3:][::-1]
Out[3]: array([4, 3, 1])

Cela implique une sorte complète du tableau. Je me demande si numpy fournit un moyen intégré de faire un tri partiel; jusqu'à présent, je n'ai pas réussi à en trouver un.

Si cette solution s'avère trop lente (en particulier pour les petites n), il peut être intéressant de coder quelque chose dans Cython .

271
NPE

Les nouvelles versions de NumPy (1.8 et supérieures) ont une fonction appelée argpartition pour cela. Pour obtenir les indices des quatre plus gros éléments, faites

>>> a = np.array([9, 4, 4, 3, 3, 9, 0, 4, 6, 0])
>>> a
array([9, 4, 4, 3, 3, 9, 0, 4, 6, 0])
>>> ind = np.argpartition(a, -4)[-4:]
>>> ind
array([1, 5, 8, 0])
>>> a[ind]
array([4, 9, 6, 9])

Contrairement à argsort, cette fonction s'exécute en temps linéaire dans le pire des cas, mais les index renvoyés ne sont pas triés, comme le montre le résultat de l'évaluation de a[ind]. Si vous en avez besoin aussi, triez-les ensuite:

>>> ind[np.argsort(a[ind])]
array([1, 8, 5, 0])

Pour obtenir le sommet - k , les éléments dans cet ordre trié prennent O ( n + k log k ) heure.

496
Fred Foo

Plus simple encore:

idx = (-arr).argsort()[:n]

n est le nombre de valeurs maximales.

40
Ketan

Utilisation:

>>> import heapq
>>> import numpy
>>> a = numpy.array([1, 3, 2, 4, 5])
>>> heapq.nlargest(3, range(len(a)), a.take)
[4, 3, 1]

Pour les listes Python normales:

>>> a = [1, 3, 2, 4, 5]
>>> heapq.nlargest(3, range(len(a)), a.__getitem__)
[4, 3, 1]

Si vous utilisez Python 2, utilisez xrange au lieu de range.

Source: heapq - algorithme de file d'attente de tas

29
anishpatel

Si vous travaillez avec un tableau multidimensionnel, vous devrez alors aplatir et démêler les index:

def largest_indices(ary, n):
    """Returns the n largest indices from a numpy array."""
    flat = ary.flatten()
    indices = np.argpartition(flat, -n)[-n:]
    indices = indices[np.argsort(-flat[indices])]
    return np.unravel_index(indices, ary.shape)

Par exemple:

>>> xs = np.sin(np.arange(9)).reshape((3, 3))
>>> xs
array([[ 0.        ,  0.84147098,  0.90929743],
       [ 0.14112001, -0.7568025 , -0.95892427],
       [-0.2794155 ,  0.6569866 ,  0.98935825]])
>>> largest_indices(xs, 3)
(array([2, 0, 0]), array([2, 2, 1]))
>>> xs[largest_indices(xs, 3)]
array([ 0.98935825,  0.90929743,  0.84147098])
27
danvk

Si vous ne vous souciez pas de l'ordre des K-èmes éléments les plus grands, vous pouvez utiliser argpartition , qui devrait mieux fonctionner qu'un tri complet à travers argsort.

K = 4 # We want the indices of the four largest values
a = np.array([0, 8, 0, 4, 5, 8, 8, 0, 4, 2])
np.argpartition(a,-K)[-K:]
array([4, 1, 5, 6])

Les crédits vont à cette question .

J'ai fait quelques tests et il semble que argpartition surpasse argsort à mesure que la taille du tableau et la valeur de K augmentent.

9
blue

Pour les tableaux multidimensionnels, vous pouvez utiliser le mot clé axis afin d'appliquer le partitionnement le long de l'axe attendu.

# For a 2D array
indices = np.argpartition(arr, -N, axis=1)[:, -N:]

Et pour attraper les objets:

x = arr.shape[0]
arr[np.repeat(np.arange(x), N), indices.ravel()].reshape(x, N)

Mais notez que cela ne retournera pas un résultat trié. Dans ce cas, vous pouvez utiliser np.argsort() le long de l’axe prévu:

indices = np.argsort(arr, axis=1)[:, -N:]

# Result
x = arr.shape[0]
arr[np.repeat(np.arange(x), N), indices.ravel()].reshape(x, N)

Voici un exemple:

In [42]: a = np.random.randint(0, 20, (10, 10))

In [44]: a
Out[44]:
array([[ 7, 11, 12,  0,  2,  3,  4, 10,  6, 10],
       [16, 16,  4,  3, 18,  5, 10,  4, 14,  9],
       [ 2,  9, 15, 12, 18,  3, 13, 11,  5, 10],
       [14,  0,  9, 11,  1,  4,  9, 19, 18, 12],
       [ 0, 10,  5, 15,  9, 18,  5,  2, 16, 19],
       [14, 19,  3, 11, 13, 11, 13, 11,  1, 14],
       [ 7, 15, 18,  6,  5, 13,  1,  7,  9, 19],
       [11, 17, 11, 16, 14,  3, 16,  1, 12, 19],
       [ 2,  4, 14,  8,  6,  9, 14,  9,  1,  5],
       [ 1, 10, 15,  0,  1,  9, 18,  2,  2, 12]])

In [45]: np.argpartition(a, np.argmin(a, axis=0))[:, 1:] # 1 is because the first item is the minimum one.
Out[45]:
array([[4, 5, 6, 8, 0, 7, 9, 1, 2],
       [2, 7, 5, 9, 6, 8, 1, 0, 4],
       [5, 8, 1, 9, 7, 3, 6, 2, 4],
       [4, 5, 2, 6, 3, 9, 0, 8, 7],
       [7, 2, 6, 4, 1, 3, 8, 5, 9],
       [2, 3, 5, 7, 6, 4, 0, 9, 1],
       [4, 3, 0, 7, 8, 5, 1, 2, 9],
       [5, 2, 0, 8, 4, 6, 3, 1, 9],
       [0, 1, 9, 4, 3, 7, 5, 2, 6],
       [0, 4, 7, 8, 5, 1, 9, 2, 6]])

In [46]: np.argpartition(a, np.argmin(a, axis=0))[:, -3:]
Out[46]:
array([[9, 1, 2],
       [1, 0, 4],
       [6, 2, 4],
       [0, 8, 7],
       [8, 5, 9],
       [0, 9, 1],
       [1, 2, 9],
       [3, 1, 9],
       [5, 2, 6],
       [9, 2, 6]])

In [89]: a[np.repeat(np.arange(x), 3), ind.ravel()].reshape(x, 3)
Out[89]:
array([[10, 11, 12],
       [16, 16, 18],
       [13, 15, 18],
       [14, 18, 19],
       [16, 18, 19],
       [14, 14, 19],
       [15, 18, 19],
       [16, 17, 19],
       [ 9, 14, 14],
       [12, 15, 18]])
7
Kasrâmvd

Cela sera plus rapide qu'un tri complet en fonction de la taille de votre tableau d'origine et de la taille de votre sélection:

>>> A = np.random.randint(0,10,10)
>>> A
array([5, 1, 5, 5, 2, 3, 2, 4, 1, 0])
>>> B = np.zeros(3, int)
>>> for i in xrange(3):
...     idx = np.argmax(A)
...     B[i]=idx; A[idx]=0 #something smaller than A.min()
...     
>>> B
array([0, 2, 3])

Bien entendu, cela implique d’altérer votre tableau d’origine. Ce que vous pourriez corriger (si nécessaire) en faisant une copie ou en remplaçant les valeurs d'origine. ... selon ce qui est le moins cher pour votre cas d'utilisation.

4
Paul

La méthode _np.argpartition_ renvoie uniquement les k indices les plus grands, effectue un tri local et est plus rapide que _np.argsort_ (effectue un tri complet) lorsque le tableau est assez grand. Mais les indices renvoyés sont PAS dans l'ordre croissant/décroissant . Disons avec un exemple:

Enter image description here

Nous pouvons voir que si vous voulez un ordre croissant strict des k premiers index, _np.argpartition_ ne retournera pas ce que vous voulez.

Outre le tri manuel après np.argpartition, ma solution consiste à utiliser PyTorch, torch.topk , un outil de construction de réseau neuronal, fournissant aux API de type NumPy une prise en charge CPU et GPU. C'est aussi rapide que NumPy avec MKL, et offre un boost de GPU si vous avez besoin de gros calculs matriciels/vectoriels.

Le code strict pour les index k ascendants/descendants sera:

Enter image description here

Notez que torch.topk accepte un tenseur de torche et renvoie les k valeurs supérieures et les k indices supérieurs en type _torch.Tensor_. Semblable à np, torch.topk accepte également un argument d'axe afin que vous puissiez gérer des tableaux/tenseurs multidimensionnels.

3
futureer

Utilisation:

def max_indices(arr, k):
    '''
    Returns the indices of the k first largest elements of arr
    (in descending order in values)
    '''
    assert k <= arr.size, 'k should be smaller or equal to the array size'
    arr_ = arr.astype(float)  # make a copy of arr
    max_idxs = []
    for _ in range(k):
        max_element = np.max(arr_)
        if np.isinf(max_element):
            break
        else:
            idx = np.where(arr_ == max_element)
        max_idxs.append(idx)
        arr_[idx] = -np.inf
    return max_idxs

Cela fonctionne aussi avec les tableaux 2D. Par exemple,

In [0]: A = np.array([[ 0.51845014,  0.72528114],
                     [ 0.88421561,  0.18798661],
                     [ 0.89832036,  0.19448609],
                     [ 0.89832036,  0.19448609]])
In [1]: max_indices(A, 8)
Out[1]:
    [(array([2, 3], dtype=int64), array([0, 0], dtype=int64)),
     (array([1], dtype=int64), array([0], dtype=int64)),
     (array([0], dtype=int64), array([1], dtype=int64)),
     (array([0], dtype=int64), array([0], dtype=int64)),
     (array([2, 3], dtype=int64), array([1, 1], dtype=int64)),
     (array([1], dtype=int64), array([1], dtype=int64))]

In [2]: A[max_indices(A, 8)[0]][0]
Out[2]: array([ 0.89832036])
2
AndyK

Utilisation:

from operator import itemgetter
from heapq import nlargest
result = nlargest(N, enumerate(your_list), itemgetter(1))

Maintenant, la liste result contient N tuples (index, value) où value est maximisé.

2
off99555

bottleneck a une fonction de tri partiel, si le coût du tri du tableau entier pour obtenir les N valeurs les plus grandes est trop important.

Je ne connais rien à ce module; Je viens de googler numpy partial sort.

2
Katriel

Ce qui suit est un moyen très facile de voir les éléments maximaux et leurs positions. Ici axis est le domaine; axis = 0 correspond au nombre maximal de colonnes et axis = 1 correspond à un nombre maximal de lignes pour le cas 2D. Et pour les dimensions supérieures, cela dépend de vous.

M = np.random.random((3, 4))
print(M)
print(M.max(axis=1), M.argmax(axis=1))
1
liberal

J'ai trouvé le plus intuitif d'utiliser np.unique.

L'idée est que la méthode unique renvoie les indices des valeurs d'entrée. Ensuite, à partir de la valeur unique maximale et des indications, la position des valeurs d'origine peut être recréée.

multi_max = [1,1,2,2,4,0,0,4]
uniques, idx = np.unique(multi_max, return_inverse=True)
print np.squeeze(np.argwhere(idx == np.argmax(uniques)))
>> [4 7]
0
phi

Je pense que le moyen le plus efficace en termes de temps est de parcourir manuellement le tableau et de conserver un minimum de taille k, comme d'autres personnes l'ont mentionné.

Et je viens aussi avec une approche de force brute:

top_k_index_list = [ ]
for i in range(k):
    top_k_index_list.append(np.argmax(my_array))
    my_array[top_k_index_list[-1]] = -float('inf')

Définissez le plus grand élément sur une valeur négative élevée après avoir utilisé argmax pour obtenir son index. Et ensuite, le prochain appel de argmax renverra le deuxième élément le plus important. Et vous pouvez enregistrer la valeur d'origine de ces éléments et les récupérer si vous le souhaitez.

0
Zhenghao Zhao