Wie finde ich den Index des ersten Auftretens einer Zahl in einem Numpy-Array? Geschwindigkeit ist mir wichtig. Die folgenden Antworten interessieren mich nicht, da sie das gesamte Array scannen und nicht aufhören, wenn sie das erste Vorkommen finden:
itemindex = numpy.where(array==item)[0][0]
nonzero(array == item)[0][0]
Anmerkung 1: Keine der Antworten auf diese Frage scheint relevant zu sein. Gibt es eine Numpy-Funktion, um den ersten Index von etwas in einem Array zurückzugeben?
Hinweis 2: Die Verwendung einer C-kompilierten Methode wird einer Python-Schleife vorgezogen.
Es ist zwar viel zu spät für Sie, aber für zukünftige Referenz: Die Verwendung von numba ( 1 ) ist der einfachste Weg, bis numpy es implementiert. Wenn Sie eine Anaconda-Python-Distribution verwenden, sollte diese bereits installiert sein. Der Code wird so kompiliert, dass er schnell ist.
und dann:
quelle
xrange
muss für geändert werdenrange
.enumerate
wie infor i, v in enumerate(vec):
;if v == item: return i
. (Dies ist keine gute Idee in Python <= 2.7, woenumerate
eine Liste anstelle eines einfachen Iterators erstellt wird.)Ich habe einen Benchmark für verschiedene Methoden erstellt:
argwhere
nonzero
wie in der Frage.tostring()
wie in der Antwort von @Rob ReilinkDer Python- und Fortran- Code ist verfügbar. Ich habe die vielversprechenden wie das Konvertieren in eine Liste übersprungen.
Die Ergebnisse im Protokollmaßstab. Die X-Achse ist die Position der Nadel (es dauert länger, um festzustellen, ob sie sich weiter unten im Array befindet). Der letzte Wert ist eine Nadel, die nicht im Array enthalten ist. Die Y-Achse ist die Zeit, um sie zu finden.
Das Array hatte 1 Million Elemente und die Tests wurden 100 Mal ausgeführt. Die Ergebnisse schwanken immer noch ein wenig, aber der qualitative Trend ist klar: Python und f2py werden beim ersten Element beendet, sodass sie unterschiedlich skalieren. Python wird zu langsam, wenn sich die Nadel nicht in den ersten 1% befindet, während
f2py
es schnell ist (aber Sie müssen es kompilieren).Zusammenfassend ist f2py die schnellste Lösung , insbesondere wenn die Nadel ziemlich früh erscheint.
Es ist nicht eingebaut, was nervt, aber es sind wirklich nur 2 Minuten Arbeit. Fügen Sie dies einer Datei mit dem Namen hinzu
search.f90
:Wenn Sie nach etwas anderem suchen
integer
, ändern Sie einfach den Typ. Dann kompilieren Sie mit:Danach können Sie (aus Python):
quelle
f2py
1 Artikel langsamer als 10?Sie können ein boolesches Array
array.tostring()
mithilfe der find () -Methode in einen Python-String konvertieren :Dies beinhaltet jedoch das Kopieren der Daten, da Python-Zeichenfolgen unveränderlich sein müssen. Ein Vorteil ist, dass Sie auch zB nach einer steigenden Flanke suchen können
\x00\x01
quelle
Bei sortierten Arrays
np.searchsorted
funktioniert.quelle
Ich denke, Sie sind auf ein Problem gestoßen, bei dem eine andere Methode und einige a priori Kenntnisse des Arrays wirklich helfen würden. Die Art von Dingen, bei denen Sie eine X-Wahrscheinlichkeit haben, Ihre Antwort im ersten Y-Prozent der Daten zu finden. Die Aufteilung des Problems in der Hoffnung, Glück zu haben, dann in Python mit einem verschachtelten Listenverständnis oder so.
Das Schreiben einer C-Funktion für diese Brute Force ist auch mit ctypes nicht allzu schwierig .
Der C-Code, den ich zusammen gehackt habe (index.c):
und die Python:
und ich bekomme 92.
Wickeln Sie die Python in eine richtige Funktion und los geht's.
Die C-Version ist für diesen Samen viel (~ 20x) schneller (Warnung, ich bin nicht gut mit Timeit)
quelle
@tal hat bereits eine
numba
Funktion zum Auffinden des ersten Index vorgestellt, die jedoch nur für 1D-Arrays funktioniert. Mit findennp.ndenumerate
Sie auch den ersten Index in einem arbitarisch dimensionierten Array:Beispielfall:
Das Timing zeigt, dass die Leistung der Tals- Lösung ähnlich ist :
quelle
array
vor dem Einspeisennp.ndenumerate
, sodass Ihre interessierende Achse an erster Stelle steht.np.argwhere
) bis 717 ns (Ihre Lösung), beide für eine Reihe von Formen(3000000, 12)
).Wenn Ihre Liste sortiert ist , können Sie mit dem Paket 'bisect' eine sehr schnelle Indexsuche durchführen. Es ist O (log (n)) anstelle von O (n).
Findet x im Array a, im sortierten Fall definitiv schneller als jede C-Routine, die alle ersten Elemente durchläuft (für ausreichend lange Listen).
Es ist manchmal gut zu wissen.
quelle
>>> cond = "import numpy as np;a = np.arange(40)"
timeit("np.searchsorted(a, 39)", cond)
funktioniert für 3.47867107391 Sekunden.timeit("bisect.bisect(a, 39)", cond2)
arbeitet für 7.0661458969116 Sekunden. Es sieht so aus, als wärenumpy.searchsorted
es besser für sortierte Arrays (zumindest für Ints).Soweit ich weiß, sind nur np.any und np.all auf booleschen Arrays kurzgeschlossen.
In Ihrem Fall muss numpy das gesamte Array zweimal durchlaufen, einmal, um die boolesche Bedingung zu erstellen, und ein zweites Mal, um die Indizes zu finden.
Meine Empfehlung in diesem Fall wäre, Cython zu verwenden. Ich denke, es sollte einfach sein, ein Beispiel für diesen Fall anzupassen, insbesondere wenn Sie nicht viel Flexibilität für verschiedene d-Typen und Formen benötigen.
quelle
Ich brauchte das für meinen Job, also brachte ich mir Python und Numpys C-Oberfläche bei und schrieb meine eigene. http://pastebin.com/GtcXuLyd Es ist nur für 1-D-Arrays geeignet, funktioniert jedoch für die meisten Datentypen (int, float oder string). Tests haben gezeigt, dass es erneut etwa 20-mal schneller ist als der erwartete Ansatz in reinem Python- numpy.
quelle
Dieses Problem kann in reiner Zahl effektiv gelöst werden, indem das Array in Blöcken verarbeitet wird:
Das Array wird in großen Teilen verarbeitet
step
. Jestep
länger der Schritt ist, desto schneller wird das Null-Array verarbeitet (Worst-Case). Je kleiner es ist, desto schneller wird das Array mit Null ungleich verarbeitet. Der Trick besteht darin, mit einem kleinen zu beginnenstep
und es exponentiell zu erhöhen. Darüber hinaus ist es aufgrund begrenzter Vorteile nicht erforderlich, diese Schwelle zu überschreiten.Ich habe die Lösung mit der reinen Lösung ndarary.nonzero und numba mit 10 Millionen Floats verglichen.
Und Ergebnisse auf meiner Maschine:
Pure
ndarray.nonzero
ist definitiv lockerer. Die Numba-Lösung ist im besten Fall etwa fünfmal schneller. Im schlimmsten Fall ist es ungefähr dreimal schneller.quelle
Wenn Sie nach dem ersten Nicht-Null-Element suchen, können Sie einen folgenden Hack verwenden:
Es ist eine sehr schnelle "numpy-pure" Lösung, die jedoch in einigen unten diskutierten Fällen fehlschlägt.
Die Lösung nutzt die Tatsache, dass so gut wie die gesamte Darstellung von Null für numerische Typen aus
0
Bytes besteht . Dies gilt auch für Numpysbool
. In neueren Versionen von numpyargmax()
verwendet die Funktion bei der Verarbeitung desbool
Typs eine Kurzschlusslogik . Die Größe vonbool
ist 1 Byte.Man muss also:
bool
. Es wird keine Kopie erstelltargmax()
diese Option, um das erste Byte ungleich Null mithilfe der Kurzschlusslogik zu finden//
) des Versatzes durch die Größe eines einzelnen Elements, ausgedrückt in Bytes (x.itemsize
).x[idx]
tatsächlich nicht Null ist, um den Fall zu identifizieren, in dem keine Nicht-Null vorhanden istIch habe einen Benchmark gegen die Numba-Lösung erstellt und sie erstellt
np.nonzero
.Das Ergebnis auf meiner Maschine sind:
Die Lösung ist 33% schneller als numba und "numpy-pure".
Die Nachteile:
object
float
oderdouble
Berechnungen erscheintquelle
x
bevor Sie anrufennonzero()
. Es ist wahrscheinlich langsamer als numba, aber es durchsucht nicht das gesamte Array, während es nach dem ersten Null-Eintrag sucht, sodass es möglicherweise schnell genug für Ihre Anforderungen ist.Als langjähriger Matlab-Benutzer habe ich schon seit einiger Zeit nach einer effizienten Lösung für dieses Problem gesucht. Schließlich habe ich , motiviert durch Diskussionen und Vorschläge in diesem Thread , versucht, eine Lösung zu finden, die eine API implementiert , die der hier vorgeschlagenen ähnlich ist und im Moment nur 1D-Arrays unterstützt.
Sie würden es so verwenden
Die unterstützten Bedingungsoperatoren sind: cmp_equal, cmp_not_equal, cmp_larger, cmp_smaller, cmp_larger_eq, cmp_smaller_eq. Aus Effizienzgründen ist die Erweiterung in c geschrieben.
Die Quelle, Benchmarks und andere Details finden Sie hier:
https://pypi.python.org/pypi?name=py_find_1st&:action=display
Für die Verwendung in unserem Team (Anaconda unter Linux und MacOS) habe ich ein Anaconda-Installationsprogramm erstellt, das die Installation vereinfacht. Sie können es wie hier beschrieben verwenden
https://anaconda.org/roebel/py_find_1st
quelle
Nur ein Hinweis: Wenn Sie eine Sequenz von Suchvorgängen ausführen, kann der Leistungsgewinn durch clevere Aktionen wie das Konvertieren in Zeichenfolgen in der äußeren Schleife verloren gehen, wenn die Suchdimension nicht groß genug ist. Sehen Sie, wie die Leistung des Iterierens von find1, das den oben vorgeschlagenen String-Konvertierungstrick verwendet, und find2, das argmax entlang der inneren Achse verwendet (plus einer Anpassung, um sicherzustellen, dass eine Nichtübereinstimmung als -1 zurückgegeben wird).
Ausgänge
Ein in C geschriebener Fund wäre jedoch zumindest etwas schneller als jeder dieser Ansätze
quelle
Wie wäre es damit
quelle
where(array==item)[0][0]
von der Frage ...Sie können Ihr Array in ein Array umwandeln
list
und dessenindex()
Methode verwenden:Soweit mir bekannt ist, handelt es sich um eine C-kompilierte Methode.
quelle
timeit()
ein Array mit 10000 Ganzzahlen verwendet - die Konvertierung in eine Liste war ungefähr 100-mal langsamer! Ich hatte vergessen, dass die zugrunde liegende Datenstruktur für ein Numpy-Array sich stark von einer Liste unterscheidet.