Gibt es eine NumPy-Funktion, um den ersten Index von etwas in einem Array zurückzugeben?


463

Ich weiß, dass es eine Methode für eine Python-Liste gibt, um den ersten Index von etwas zurückzugeben:

>>> l = [1, 2, 3]
>>> l.index(2)
1

Gibt es so etwas für NumPy-Arrays?


3
Zu
Ihrer

Antworten:


522

Ja, hier ist die Antwort mit einem NumPy-Array arrayund einem Wert item, nach dem gesucht werden soll:

itemindex = numpy.where(array==item)

Das Ergebnis ist ein Tupel mit zuerst allen Zeilenindizes und dann allen Spaltenindizes.

Wenn ein Array beispielsweise zwei Dimensionen hat und Ihr Element an zwei Stellen enthält, dann

array[itemindex[0][0]][itemindex[1][0]]

wäre gleich Ihrem Artikel und so würde

array[itemindex[0][1]][itemindex[1][1]]

numpy.where


1
Wenn Sie nach der ersten Zeile suchen, in der ein Element in der ersten Spalte vorhanden ist, funktioniert dies (obwohl es einen Indexfehler rows, columns = np.where(array==item); first_idx = sorted([r for r, c in zip(rows, columns) if c == 0])[0]
auslöst,

27
Was ist, wenn die Suche nach dem ersten Wert beendet werden soll? Ich denke nicht, wo () vergleichbar ist mit find ()
Michael Clerx

2
Ah! Wenn Sie an Leistung interessiert sind, lesen Sie
Michael Clerx

11
np.argwherewäre hier etwas nützlicher:itemindex = np.argwhere(array==item)[0]; array[tuple(itemindex)]
Eric

3
Es ist erwähnenswert, dass diese Antwort davon ausgeht, dass das Array 2D ist. wherefunktioniert auf jedem Array und gibt ein Tupel der Länge 3 zurück, wenn es auf einem 3D-Array usw. verwendet wird.
P. Camilleri

69

Wenn Sie den Index des ersten Auftretens nur eines Werts benötigen , können Sie Folgendes verwenden nonzero(oder where, was in diesem Fall dasselbe bedeutet):

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

Wenn Sie den ersten Index für jeden von vielen Werten benötigen , können Sie natürlich wiederholt dasselbe wie oben tun, aber es gibt einen Trick, der möglicherweise schneller ist. Im Folgenden werden die Indizes des ersten Elements jeder Teilsequenz ermittelt :

>>> nonzero(r_[1, diff(t)[:-1]])
(array([0, 3, 5, 6, 7, 8]),)

Beachten Sie, dass der Anfang beider Teilsequenzen von 3s und beider Teilsequenzen von 8s gefunden wird:

[ 1 , 1, 1, 2 , 2, 3 , 8 , 3 , 8 , 8]

Es ist also etwas anders als das erste zu finden Auftreten jedes Werts zu finden. In Ihrem Programm können Sie möglicherweise mit einer sortierten Version von arbeiten t, um das zu erhalten, was Sie möchten:

>>> st = sorted(t)
>>> nonzero(r_[1, diff(st)[:-1]])
(array([0, 3, 5, 7]),)

4
Könnten Sie bitte erklären, was r_ist?
Geoff

1
@Geoff, r_verkettet; oder genauer gesagt, es übersetzt Slice-Objekte in Verkettung entlang jeder Achse. Ich hätte hstackstattdessen verwenden können; das mag weniger verwirrend gewesen sein. Weitere Informationen finden Sie in der Dokumentationr_ . Es gibt auch eine c_.
Vebjorn Ljosa

+1, schön! (vs NP.where) Ihre Lösung ist viel einfacher (und wahrscheinlich schneller), wenn es nur das erste Auftreten eines bestimmten Werts in einem 1D-Array ist, das wir benötigen
Doug

3
Der letztere Fall (Finden des ersten Index aller Werte) ist gegeben durchvals, locs = np.unique(t, return_index=True)
askewchan

50

Sie können auch ein NumPy-Array in eine Liste in der Luft konvertieren und dessen Index abrufen. Zum Beispiel,

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

Es wird 1 gedruckt.


Möglicherweise hat sich die Bibliothek seit dem ersten Schreiben geändert. Dies war jedoch die erste Lösung, die für mich funktioniert hat.
Amracel

1
Ich habe dies gut genutzt, um mehrere Werte in einer Liste mithilfe eines Listenverständnisses zu finden:[find_list.index(index_list[i]) for i in range(len(index_list))]
Matt Wenham

1
@MattWenham Wenn es groß genug ist, können Sie es find_listin ein NumPy-Array von object(oder etwas Spezifischerem, das angemessen ist) konvertieren und es einfach tun find_arr[index_list].
Narfanar

Völlig unangebracht, aber dies ist das erste Mal, dass ich den Satz "in der Luft" sehe - was ich an seiner Stelle am meisten gesehen habe, ist wahrscheinlich "on the fly".
flow2k

18

Nur um eine sehr performante und handliche hinzuzufügen Alternative basierend auf np.ndenumerate, um den ersten Index zu finden:

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.

Dies ist ziemlich schnell und befasst sich natürlich mit mehrdimensionalen Arrays :

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

Dies kann viel schneller sein (weil es die Operation kurzschließt) als jeder Ansatz, der np.whereoder verwendet np.nonzero.


Allerdings np.argwherekönnte auch behandeln anmutig mit mehrdimensionalen Arrays (Sie würden müssen manuell in ein Tupel geworfen es und es ist nicht kurzgeschlossen) , aber es würde scheitern , wenn keine Übereinstimmung gefunden wird:

>>> tuple(np.argwhere(arr1 == 2)[0])
(2, 2, 2)
>>> tuple(np.argwhere(arr2 == 2)[0])
(5,)

2
@njitist eine Abkürzung von jit(nopython=True)dh die Funktion wird zum Zeitpunkt des ersten Durchlaufs im laufenden Betrieb vollständig kompiliert, sodass die Python-Interpreter-Aufrufe vollständig entfernt werden.
Bartolo-Otrit

14

Wenn Sie dies als Index für etwas anderes verwenden möchten, können Sie boolesche Indizes verwenden, wenn die Arrays sendbar sind. Sie benötigen keine expliziten Indizes. Der absolut einfachste Weg, dies zu tun, besteht darin, einfach basierend auf einem Wahrheitswert zu indizieren.

other_array[first_array == item]

Jede boolesche Operation funktioniert:

a = numpy.arange(100)
other_array[first_array > 50]

Die Nicht-Null-Methode verwendet auch Boolesche Werte:

index = numpy.nonzero(first_array == item)[0][0]

Die beiden Nullen stehen für das Tupel von Indizes (vorausgesetzt, first_array ist 1D) und dann für das erste Element im Array von Indizes.


10

l.index(x)Gibt das kleinste i zurück , sodass i der Index des ersten Auftretens von x in der Liste ist.

Man kann davon ausgehen, dass die index()Funktion in Python so implementiert ist, dass sie nach dem Finden der ersten Übereinstimmung stoppt, und dies führt zu einer optimalen Durchschnittsleistung.

Verwenden Sie einen Iterator ( ndenumerate ) , um ein Element zu finden, das nach der ersten Übereinstimmung in einem NumPy-Array stoppt .

In [67]: l=range(100)

In [68]: l.index(2)
Out[68]: 2

NumPy-Array:

In [69]: a = np.arange(100)

In [70]: next((idx for idx, val in np.ndenumerate(a) if val==2))
Out[70]: (2L,)

Beachten Sie, dass beide Methoden index()und nexteinen Fehler zurückgeben, wenn das Element nicht gefunden wird. Mit nextkann man ein zweites Argument verwenden, um einen speziellen Wert zurückzugeben, falls das Element nicht gefunden wird, z

In [77]: next((idx for idx, val in np.ndenumerate(a) if val==400),None)

Es gibt auch andere Funktionen in NumPy ( argmax, where, und nonzero) , die verwendet werden kann , ein Element in einem Array zu finden, aber sie haben alle den Nachteil , daß durch die ganze Reihe gehen auf der Suche nach allen Vorkommen, also nicht zum Auffinden des ersten Elements optimiert. Beachten Sie auch das whereund nonzerogeben Sie Arrays zurück, sodass Sie das erste Element auswählen müssen, um den Index abzurufen.

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

Zeitvergleich

Wenn Sie nur überprüfen, ob bei großen Arrays die Lösung mithilfe eines Iterators schneller ist, wenn sich das gesuchte Element am Anfang des Arrays befindet (mithilfe %timeitder IPython-Shell):

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

Dies ist ein offenes NumPy GitHub-Problem .

Siehe auch: Numpy: Finden Sie schnell den ersten Wertindex


1
Ich denke, Sie sollten auch ein Timing für den schlimmsten Fall (letztes Element) angeben, damit die Leser wissen, was mit ihnen im schlimmsten Fall passiert, wenn sie Ihren Ansatz verwenden.
MSeifert

@ MSeifert Ich kann kein vernünftiges Timing für die Iteratorlösung im schlimmsten Fall bekommen - ich werde diese Antwort löschen, bis ich herausfinde, was daran falsch ist
user2314737

1
funktioniert nicht %timeit next((idx for idx, val in np.ndenumerate(a) if val==99999))? Wenn Sie sich fragen, warum es 1000-mal langsamer ist, dann deshalb, weil Python-Loops über numpy Arrays notorisch langsam sind.
MSeifert

@MSeifert nein das wusste ich nicht, aber ich bin auch verwirrt darüber, dass argmaxund wherein diesem Fall viel schneller sind (gesuchtes Element am Ende des Arrays)
user2314737

Sie sollten so schnell sein, als ob das Element am Anfang steht. Sie verarbeiten immer das gesamte Array, sodass sie immer die gleiche Zeit benötigen (zumindest sollten sie dies tun).
MSeifert

9

Für eindimensional sortierte Arrays wäre es viel einfacher und effizienter, O (log (n)) zu verwenden, indem numpy.searchsorted verwendet wird, das eine NumPy-Ganzzahl (Position) zurückgibt. Zum Beispiel,

arr = np.array([1, 1, 1, 2, 3, 3, 4])
i = np.searchsorted(arr, 3)

Stellen Sie einfach sicher, dass das Array bereits sortiert ist

Überprüfen Sie auch, ob der zurückgegebene Index i tatsächlich das gesuchte Element enthält, da das Hauptziel von searchsorted darin besteht, Indizes zu finden, in die Elemente eingefügt werden sollen, um die Reihenfolge aufrechtzuerhalten.

if arr[i] == 3:
    print("present")
else:
    print("not present")

2
searchsorted ist nicht nlog (n), da das Array vor der Suche nicht sortiert wird. Es wird davon ausgegangen, dass das Argumentarray bereits sortiert ist. Schauen Sie sich die Dokumentation von numpy.searchsorted an (Link oben)
Alok Nayak

6

Um nach Kriterien zu indizieren, können Sie so etwas wie das Folgende tun:

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

Und hier ist eine schnelle Funktion, um das zu tun, was list.index () tut, außer dass keine Ausnahme ausgelöst wird, wenn sie nicht gefunden wird. Achtung - dies ist bei großen Arrays wahrscheinlich sehr langsam. Sie können dies wahrscheinlich auf Arrays patchen, wenn Sie es lieber als Methode verwenden möchten.

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]

5

Für 1D-Arrays würde ich empfehlen np.flatnonzero(array == value)[0], was beiden entspricht np.nonzero(array == value)[0][0]und np.where(array == value)[0][0]die Hässlichkeit des Entpackens eines 1-Element-Tupels vermeidet.


4

Eine Alternative zur Auswahl des ersten Elements aus np.where () besteht darin, einen Generatorausdruck zusammen mit enumerate zu verwenden, z.

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

Für ein zweidimensionales Array würde man tun:

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

Der Vorteil dieses Ansatzes besteht darin, dass die Überprüfung der Elemente des Arrays nach dem Auffinden der ersten Übereinstimmung beendet wird, während np.where alle Elemente auf Übereinstimmung überprüft. Ein Generatorausdruck wäre schneller, wenn es früh im Array eine Übereinstimmung gibt.


Für den Fall, dass das Array möglicherweise überhaupt nicht übereinstimmt, können Sie mit dieser Methode auch bequem einen Fallback-Wert angeben. Wenn das erste Beispiel Noneals Fallback zurückkehren würde, würde es werden next((i for i, x_i in enumerate(x) if x_i == 2), None).
Erlend Magnus Viggen

4

In NumPy gibt es viele Operationen, die möglicherweise zusammengestellt werden könnten, um dies zu erreichen. Dies gibt Indizes von Elementen zurück, die gleich item sind:

numpy.nonzero(array - item)

Sie können dann die ersten Elemente der Listen verwenden, um ein einzelnes Element zu erhalten.


5
würde das nicht die Indizes aller Elemente geben, die nicht gleich item sind?
Autoplektische

3

Das numpy_indexed- Paket (Haftungsausschluss, ich bin sein Autor) enthält ein vektorisiertes Äquivalent von list.index für numpy.ndarray; das ist:

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]

Diese Lösung hat die Leistung vektorisiert, auf ndarrays verallgemeinert und bietet verschiedene Möglichkeiten, mit fehlenden Werten umzugehen.


-1

Hinweis: Dies gilt für Python 2.7

Sie können eine Lambda-Funktion verwenden, um das Problem zu beheben. Sie funktioniert sowohl für das NumPy-Array als auch für die Liste.

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]

Und du kannst verwenden

result[0]

um den ersten Index der gefilterten Elemente zu erhalten.

Verwenden Sie für Python 3.6

list(result)

anstatt

result

Dies führt <filter object at 0x0000027535294D30>zu Python 3 (getestet unter Python 3.6.3). Vielleicht Update für Python 3?
Peter Mortensen
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.