Wie erhalte ich Indizes von N Maximalwerten in einem NumPy-Array?

482

NumPy schlägt eine Möglichkeit vor, den Index des Maximalwerts eines Arrays über abzurufen np.argmax.

Ich möchte etwas Ähnliches, aber die Indizes der NMaximalwerte zurückgeben.

Wenn ich ein Array zum Beispiel haben, [1, 3, 2, 4, 5], function(array, n=3)würde die Indizes zurück [4, 3, 1], die den Elementen entsprechen [5, 4, 3].

Alexis Métaireau
quelle
4
Ihre Frage ist nicht wirklich genau definiert. Zum Beispiel, was würden die Indizes (Sie erwarten) für sein array([5, 1, 5, 5, 2, 3, 2, 4, 1, 5]), Whit n= 3? Welche von allen Alternativen, wie [0, 2, 3], [0, 2, 9], ...wäre die richtige? Bitte erläutern Sie Ihre spezifischen Anforderungen. Danke
essen
@eat, es ist mir eigentlich egal, welches in diesem speziellen Fall zurückgegeben werden soll. Auch wenn es logisch erscheint, den ersten zurückzugeben, ist dies für mich keine Voraussetzung.
Alexis Métaireau
argsortDies ist möglicherweise eine praktikable Alternative, wenn Sie sich nicht um die Reihenfolge der zurückgegebenen Indeces kümmern. Siehe meine Antwort unten.
Blau

Antworten:

347

Das einfachste, was ich mir vorstellen konnte, ist:

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])

Dies beinhaltet eine vollständige Art des Arrays. Ich frage mich, ob es numpyeine eingebaute Möglichkeit gibt, eine Teilsortierung durchzuführen. Bisher konnte ich keinen finden.

Wenn sich herausstellt, dass diese Lösung zu langsam ist (insbesondere für kleine n), lohnt es sich möglicherweise, etwas in Cython zu codieren .

NPE
quelle
1
Könnte Zeile 3 äquivalent geschrieben werden als arr.argsort()[-1:-4:-1]? Ich habe es im Dolmetscher versucht und es kommt zum gleichen Ergebnis, aber ich frage mich, ob es nicht durch ein Beispiel gebrochen wird.
Abroekhof
44
@abroekhof Ja, das sollte für jede Liste oder jedes Array gleichwertig sein. Alternativ könnte dies ohne die Umkehrung durch Verwendung erfolgen np.argsort(-arr)[:3], was ich besser lesbar und auf den Punkt finde.
Askewchan
6
Was bedeutet [:: - 1]? @ NPE
1a1a11a
@ 1a1a11a bedeutet, dass ein Array umgekehrt wird (wörtlich: eine Kopie eines Arrays wird in umgekehrter Reihenfolge von uneingeschränktem min auf uneingeschränktes
Maximum übertragen
15
arr.argsort()[::-1][:n]ist besser, weil es leer für n=0anstelle des vollständigen Arrays
zurückgibt
599

Neuere NumPy-Versionen (1.8 und höher) haben eine dafür aufgerufene Funktion argpartition. Um die Indizes der vier größten Elemente zu erhalten, tun Sie dies

>>> 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])

Im Gegensatz argsortdazu läuft diese Funktion im schlimmsten Fall in linearer Zeit, aber die zurückgegebenen Indizes werden nicht sortiert, wie aus dem Ergebnis der Auswertung hervorgeht a[ind]. Wenn Sie das auch brauchen, sortieren Sie sie anschließend:

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

Um die top- zu bekommen k Elemente in sortierter Reihenfolge auf diese Weise nimmt O ( n + k log k ) Zeit.

Fred Foo
quelle
27
@varela argpartitionläuft in der linearen Zeit O (n) unter Verwendung des Introselect- Algorithmus. Die nachfolgende Sortierung behandelt nur k Elemente, sodass diese in O (k log k) ausgeführt werden.
Fred Foo
2
Wenn sich jemand fragt, wie genau np.argpartitionund wie der Schwesteralgorithmus np.partitionfunktioniert, finden Sie in der verknüpften Frage eine ausführlichere Erklärung: stackoverflow.com/questions/10337533/…
Ramon Martinez
7
@FredFoo: Warum hast du -4 benutzt? Hast du das getan, um rückwärts zu beginnen? (Da k positiv oder negativ ist, funktioniert das für mich gleich! Es werden nur die kleinsten Zahlen zuerst gedruckt!
Rika
2
@LKT verwenden, a=np.array([9, 4, 4, 3, 3, 9, 0, 4, 6, 0])weil normale Python-Listen im Gegensatz zunp.array
Marawan Okasha
2
@Umangsinghal verwendet np.argpartitionein optionales axisArgument. So finden Sie die Indizes der obersten n Werte für jede Zeile:np.argpartition(a, -n, axis=1)[-n:]
Ralph
48

Noch einfacher:

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

Dabei ist n die Anzahl der Maximalwerte.

Ketan
quelle
7
Kann dies für ein 2d-Array durchgeführt werden? Wenn nicht, wissen Sie vielleicht wie?
Andrew Hundt
2
@ AndrewHundt: Verwenden Sie einfach (-arr) .argsort (Achse = -1) [:,: n]
MiniQuark
2
Ähnliches wäre, arr[arr.argsort()[-n:]]anstatt das Array zu negieren, nehmen Sie einfach ein Stück der letzten n Elemente
loganjones16
35

Verwenden:

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

Für reguläre Python-Listen:

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

Wenn Sie Python 2 verwenden, verwenden Sie xrangeanstelle von range.

Quelle: heapq - Heap-Warteschlangenalgorithmus

Anishpatel
quelle
2
Hier ist überhaupt keine Schleife erforderlich : heapq.nlargest(3, xrange(len(a)), a.take). Für Python-Listen können wir .__getitem__anstelle von verwenden .take.
Ashwini Chaudhary
Für n-dimensionale Arrays Aim Allgemeinen : heapq.nlargest(3, range(len(A.ravel())), A.ravel().take). (Ich hoffe, dies funktioniert nur mit Ansichten, siehe auch ( ravel vs flatten] ( stackoverflow.com/a/28930580/603003 )).
ComFreek
31

Wenn Sie zufällig mit einem mehrdimensionalen Array arbeiten, müssen Sie die Indizes reduzieren und entwirren:

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)

Zum Beispiel:

>>> 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])
danvk
quelle
9

Wenn Sie sich nicht für die Reihenfolge der K-ten größten Elemente interessieren, die Sie verwenden können argpartition, sollte diese besser funktionieren als eine vollständige Sortierung 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])

Credits gehen an diese Frage .

Ich habe ein paar Tests durchgeführt und es sieht so aus argpartition, argsortals ob es besser abschneidet, wenn die Größe des Arrays und der Wert von K zunehmen.

Blau
quelle
7

Bei mehrdimensionalen Arrays können Sie das axisSchlüsselwort verwenden, um die Partitionierung entlang der erwarteten Achse anzuwenden.

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

Und für das Ergreifen der Gegenstände:

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

Beachten Sie jedoch, dass dies kein sortiertes Ergebnis zurückgibt. In diesem Fall können Sie np.argsort()entlang der vorgesehenen Achse verwenden:

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

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

Hier ist ein Beispiel:

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]])
Kasramvd
quelle
Ich denke, Sie können die Indizierung hier vereinfachen, indem Sie np.take_along_axis(was wahrscheinlich nicht existierte, als Sie diese Frage beantworteten)
Eric
4

Dies ist schneller als eine vollständige Sortierung, abhängig von der Größe Ihres ursprünglichen Arrays und der Größe Ihrer Auswahl:

>>> 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])

Es geht natürlich darum, Ihr ursprüngliches Array zu manipulieren. Was Sie (falls erforderlich) beheben können, indem Sie eine Kopie erstellen oder die ursprünglichen Werte ersetzen. ... je nachdem, was für Ihren Anwendungsfall günstiger ist.

Paul
quelle
FWIW, Ihre Lösung bietet nicht in allen Situationen eine eindeutige Lösung. OP sollte beschreiben, wie mit diesen eindeutigen Fällen umgegangen werden soll. Danke
essen
@eat Die Frage des OP ist etwas mehrdeutig. Eine Implementierung ist jedoch nicht wirklich interpretationsfähig. :) Das OP sollte sich einfach auf die Definition von np.argmax docs.scipy.org/doc/numpy/reference/generated/numpy.argmax.html beziehen , um sicherzustellen, dass diese spezifische Lösung die Anforderungen erfüllt. Es ist möglich, dass jede Lösung, die die vom OP angegebenen Anforderungen erfüllt, akzeptabel ist.
Paul
Nun, man könnte die Implementierung von argmax(.)auch als eindeutig betrachten. (IMHO versucht es, einer Art Kurzschlusslogik zu folgen, liefert aber leider kein allgemein akzeptables Verhalten). Danke
essen
3

Die Methode gibt np.argpartitionnur die k größten Indizes zurück, führt eine lokale Sortierung durch und ist schneller als np.argsort(vollständige Sortierung), wenn das Array ziemlich groß ist. Die zurückgegebenen Indizes sind jedoch NICHT in aufsteigender / absteigender Reihenfolge . Sagen wir mit einem Beispiel:

Geben Sie hier die Bildbeschreibung ein

Wir können sehen, dass wenn Sie eine streng aufsteigende Reihenfolge der Top-k-Indizes np.argpartitionwünschen , nicht das zurückgegeben wird, was Sie wollen.

Abgesehen von der manuellen Sortierung nach np.argpartition besteht meine Lösung darin, PyTorch zu verwenden torch.topk, ein Tool für den Aufbau neuronaler Netzwerke, das NumPy-ähnliche APIs sowohl mit CPU- als auch mit GPU-Unterstützung unterstützt. Es ist so schnell wie NumPy mit MKL und bietet einen GPU-Boost, wenn Sie große Matrix- / Vektorberechnungen benötigen.

Der strikte Code für aufsteigende / absteigende Top-k-Indizes lautet:

Geben Sie hier die Bildbeschreibung ein

Beachten Sie, dass torch.topkein Brennertensor akzeptiert wird und sowohl Top-k-Werte als auch Top-k-Indizes vom Typ zurückgegeben werden torch.Tensor. Ähnlich wie bei np akzeptiert torch.topk auch ein Achsenargument, damit Sie mehrdimensionale Arrays / Tensoren verarbeiten können.

Zukunft
quelle
2

Verwenden:

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

Jetzt würde die resultListe N Tupel ( index, value) enthalten , wobei valuemaximiert wird.

off99555
quelle
2

Verwenden:

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

Es funktioniert auch mit 2D-Arrays. Zum Beispiel,

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])
X Æ A-12
quelle
Funktioniert gut, liefert aber mehr Ergebnisse, wenn Sie doppelte (maximale) Werte in Ihrem Array A haben. Ich würde genau k Ergebnisse erwarten, aber bei doppelten Werten erhalten Sie mehr als k Ergebnisse.
Guido
Ich habe den Code leicht geändert. Die Liste der zurückgegebenen Indizes hat eine Länge von genau k. Wenn Sie Duplikate haben, werden diese zu einem einzigen Tupel zusammengefasst.
X Æ A-12
1

bottleneck hat eine partielle Sortierfunktion, wenn der Aufwand für das Sortieren des gesamten Arrays, um die N größten Werte zu erhalten, zu hoch ist.

Ich weiß nichts über dieses Modul; Ich habe nur gegoogelt numpy partial sort.

Katriel
quelle
Ich finde keine partielle
Sortierfunktion
1

Das Folgende ist eine sehr einfache Möglichkeit, die maximalen Elemente und ihre Positionen anzuzeigen. Hier axisist die Domain; axis= 0 bedeutet spaltenweise maximale Anzahl und axis= 1 bedeutet zeilenweise maximale Anzahl für den 2D-Fall. Und für höhere Dimensionen hängt es von Ihnen ab.

M = np.random.random((3, 4))
print(M)
print(M.max(axis=1), M.argmax(axis=1))
liberal
quelle
Ich habe diesen Link verwendet jakevdp.github.io/PythonDataScienceHandbook/…
liberal
0

Ich fand es am intuitivsten zu bedienen np.unique.

Die Idee ist, dass die eindeutige Methode die Indizes der Eingabewerte zurückgibt. Aus dem maximalen eindeutigen Wert und den Angaben kann dann die Position der ursprünglichen Werte neu erstellt werden.

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]
Phi
quelle
0

Ich denke, der Weg mit der größten Zeiteffizienz besteht darin, das Array manuell zu durchlaufen und einen Min-Heap in k-Größe beizubehalten, wie andere bereits erwähnt haben.

Und ich habe mir auch einen Brute-Force-Ansatz ausgedacht:

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')

Setzen Sie das größte Element auf einen großen negativen Wert, nachdem Sie argmax verwendet haben, um seinen Index abzurufen. Und dann gibt der nächste Aufruf von argmax das zweitgrößte Element zurück. Sie können den ursprünglichen Wert dieser Elemente protokollieren und bei Bedarf wiederherstellen.

Zhenghao Zhao
quelle
0

Dieser Code funktioniert für ein Numpy-Matrix-Array:

mat = np.array([[1, 3], [2, 5]]) # numpy matrix

n = 2  # n
n_largest_mat = np.sort(mat, axis=None)[-n:] # n_largest 
tf_n_largest = np.zeros((2,2), dtype=bool) # all false matrix
for x in n_largest_mat: 
  tf_n_largest = (tf_n_largest) | (mat == x) # true-false  

n_largest_elems = mat[tf_n_largest] # true-false indexing 

Dies erzeugt eine wahr-falsch-n_largest-Matrixindizierung, die auch dazu dient, n_largest-Elemente aus einem Matrixarray zu extrahieren

Yi Xiang Chong
quelle