numpy.amax () findet den Maximalwert in einem Array und numpy.amin () macht dasselbe für den Minimalwert . Wenn ich sowohl max als auch min finden möchte, muss ich beide Funktionen aufrufen, was erfordert, dass das (sehr große) Array zweimal durchlaufen wird, was langsam erscheint.
Gibt es eine Funktion in der Numpy-API, die sowohl max als auch min mit nur einem Durchgang durch die Daten findet?
amax
undamin
minmax
zur betreffenden Bibliothek ( github.com/numpy/numpy/issues/9836 ).Antworten:
Nein. Zum Zeitpunkt dieses Schreibens gibt es keine solche Funktion. (Und ja, wenn es eine solche Funktion gäbe, wäre ihre Leistung deutlich besser als beim Aufrufen
numpy.amin()
undnumpy.amax()
nacheinander auf einem großen Array.)quelle
Ich denke nicht, dass es ein Problem ist, zweimal über das Array zu gehen.Betrachten Sie den folgenden Pseudocode:Während es hier nur 1 Schleife gibt, gibt es immer noch 2 Prüfungen. (Anstatt 2 Schleifen mit jeweils 1 Prüfung zu haben). Wirklich das einzige, was Sie sparen, ist der Overhead von 1 Schleife. Wenn die Arrays wirklich groß sind, wie Sie sagen, ist dieser Overhead im Vergleich zur tatsächlichen Arbeitslast der Schleife gering. (Beachten Sie, dass dies alles in C implementiert ist, sodass die Schleifen ohnehin mehr oder weniger frei sind.)
EDIT Entschuldigung an die 4 von euch, die gestimmt haben und an mich geglaubt haben. Sie können dies definitiv optimieren.
Hier ist ein Fortran-Code, der über in ein Python-Modul kompiliert werden kann
f2py
(vielleicht kann einCython
Guru mitkommen und dies mit einer optimierten C-Version vergleichen ...):Kompilieren Sie es über:
Und jetzt sind wir an einem Ort, an dem wir es testen können:
Die Ergebnisse sind für mich etwas umwerfend:
Ich muss sagen, ich verstehe es nicht ganz. Vergleicht man nur
np.min
gegenüberminmax1
undminmax2
ist nach wie vor eine verlorene Schlacht, es ist also nicht nur ein Speicherproblem ...Hinweise - Das Erhöhen der Größe um den Faktor
10**a
und das Verringern der Wiederholung um den Faktor10**a
(Halten der Problemgröße konstant) ändert die Leistung zwar, jedoch nicht auf eine scheinbar konsistente Weise, was zeigt, dass ein gewisses Zusammenspiel zwischen Speicherleistung und Funktionsaufruf-Overhead besteht Python. Selbst der Vergleich einer einfachenmin
Implementierung in fortran übertrifft die Anzahl um einen Faktor von ungefähr 2 ...quelle
i < minval
true,i > maxval
ist es immer false, sodass Sie durchschnittlich nur 1,5 Überprüfungen pro Iteration durchführen müssen, wenn die zweiteif
durch eine ersetzt wirdelif
.f2py
nur handcodiertes Fortran so verpackt wird, dass es von Python aufgerufen werden kann. Ein "gerechterer" Test besteht wahrscheinlich darin, C von Hand zu codieren und es dann mitf2py
(!) Für Python zu verpacken. Wenn Sie C ++ zulassen, ist Shed Skin möglicherweise der ideale Ort, um die Vereinfachung der Codierung mit der Leistung in Einklang zu bringen.Es gibt eine Funktion zum Finden (max-min) namens numpy.ptp, wenn dies für Sie nützlich ist:
Aber ich glaube nicht, dass es eine Möglichkeit gibt, mit einer Durchquerung sowohl Min als auch Max zu finden.
EDIT: ptp ruft nur min und max unter der Haube auf
quelle
Sie können Numba verwenden , einen NumPy-fähigen dynamischen Python-Compiler, der LLVM verwendet. Die resultierende Implementierung ist ziemlich einfach und klar:
Es sollte auch schneller sein als die
min() & max()
Implementierung eines Numpy . Und das alles, ohne eine einzige C / Fortran-Codezeile schreiben zu müssen.Führen Sie Ihre eigenen Leistungstests durch, da dies immer von Ihrer Architektur, Ihren Daten, Ihren Paketversionen abhängt ...
quelle
numba
Funktion vor dem Benchmark auszuführen , um sicherzustellen, dass sie JIT-kompiliert ist ?. Wenn Sie deripython
Einfachheit halber auch verwenden, würde ich Ihnen empfehlen, die Zeitausführung zu%timeit whatever_code()
messen.elif
erlaubt, dass Ihr Minimum größer als Ihr Maximum ist. Bei einem Array der Länge 1 ist das Maximum beispielsweise der Wert, während min + unendlich ist. Keine große Sache für ein Einzelstück, aber kein guter Code, um tief in den Bauch eines Produktionstiers zu werfen.Im Allgemeinen können Sie die Anzahl der Vergleiche für einen Minmax-Algorithmus reduzieren, indem Sie zwei Elemente gleichzeitig verarbeiten und nur das kleinere mit dem temporären Minimum und das größere mit dem temporären Maximum vergleichen. Im Durchschnitt braucht man nur 3/4 der Vergleiche als einen naiven Ansatz.
Dies könnte in c oder fortran (oder einer anderen einfachen Sprache) implementiert werden und sollte in Bezug auf die Leistung nahezu unschlagbar sein. Ich benutzenumba um das Prinzip zu veranschaulichen und eine sehr schnelle, dtype-unabhängige Implementierung zu erhalten:
Es ist definitiv schneller als der naive Ansatz, den Peque vorgestellt hat:
Wie erwartet dauert die neue Minmax-Implementierung nur ungefähr 3/4 der Zeit, die die naive Implementierung benötigt hat (
2.1 / 2.75 = 0.7636363636363637
)quelle
Nur um ein paar Ideen zu den Zahlen zu bekommen, die man angesichts der folgenden Ansätze erwarten kann:
(Die
extrema_loop_*()
Ansätze ähneln denen, die hier vorgeschlagen werden , während dieextrema_while_*()
Ansätze auf dem Code von hier basieren. )Die folgenden Zeiten:
zeigen an, dass die
extrema_while_*()
am schnellsten sind, wobeiextrema_while_nb()
sie am schnellsten sind. In jedem Fall übertreffen auch dieextrema_loop_nb()
undextrema_loop_cy()
-Lösungen den Nur-NumPy-Ansatz (unter Verwendungnp.max()
undnp.min()
separat).Beachten Sie schließlich, dass keines davon so flexibel ist wie
np.min()
/np.max()
(in Bezug auf n-dim-Unterstützung,axis
Parameter usw.).(Der vollständige Code ist hier verfügbar. )
quelle
extrema_while_nb
Niemand erwähnte numpy.percentile , also dachte ich, ich würde es tun . Wenn Sie nach
[0, 100]
Perzentilen fragen , erhalten Sie ein Array aus zwei Elementen, dem minimalen (0. Perzentil) und dem maximalen (100. Perzentil).Es erfüllt jedoch nicht den Zweck des OP: Es ist nicht schneller als min und max getrennt. Dies liegt wahrscheinlich an einigen Maschinen, die nicht extreme Perzentile zulassen würden (ein schwierigeres Problem, das länger dauern sollte ).
Eine zukünftige Version von Numpy könnte in einem Sonderfall die normale Perzentilberechnung überspringen, wenn dies nur
[0, 100]
angefordert wird. Ohne der Schnittstelle etwas hinzuzufügen, gibt es eine Möglichkeit, Numpy in einem Aufruf nach Min und Max zu fragen (im Gegensatz zu dem, was in der akzeptierten Antwort gesagt wurde), aber die Standardimplementierung der Bibliothek nutzt diesen Fall nicht aus, um dies zu erreichen lohnend.quelle
Dies ist ein alter Thread, aber trotzdem, wenn sich jemand das jemals wieder ansieht ...
Wenn Sie gleichzeitig nach Min und Max suchen, können Sie die Anzahl der Vergleiche reduzieren. Wenn es sich um Floats handelt, die Sie vergleichen (was ich denke), kann dies Ihnen Zeit sparen, wenn auch nicht die Komplexität der Berechnungen.
Anstelle von (Python-Code):
Sie können zuerst zwei benachbarte Werte im Array vergleichen und dann nur den kleineren mit dem aktuellen Minimum und den größeren mit dem aktuellen Maximum vergleichen:
Der Code hier ist in Python geschrieben. Aus Gründen der Geschwindigkeit würden Sie C oder Fortran oder Cython verwenden. Auf diese Weise führen Sie jedoch 3 Vergleiche pro Iteration mit len (ar) / 2 Iterationen durch, was 3/2 * len (ar) Vergleiche ergibt. Im Gegensatz dazu führen Sie beim Vergleich "auf offensichtliche Weise" zwei Vergleiche pro Iteration durch, was zu 2 * len (ar) -Vergleichen führt. Spart Ihnen 25% der Vergleichszeit.
Vielleicht wird dies eines Tages jemand nützlich finden.
quelle
np.bincount
, siehe hier . Es verwendet nicht den Trick, auf den Sie hinweisen, da es sich als bis zu 2x langsamer als der naive Ansatz herausstellte. Es gibt einen Link von der PR zu einigen umfassenden Benchmarks beider Methoden.Auf den ersten Blick scheint der Trick zu tun:
numpy.histogram
... aber wenn Sie sich die Quelle für diese Funktion ansehen , ruft sie einfach
a.min()
unda.max()
unabhängig auf und vermeidet daher nicht die in dieser Frage angesprochenen Leistungsprobleme. :-(Ähnlich
scipy.ndimage.measurements.extrema
sieht es nach einer Möglichkeit aus, aber es ruft auch einfacha.min()
unda.max()
unabhängig an.quelle
np.histogram
funktioniert nicht immer dafür, da die zurückgegebenen(amin, amax)
Werte für die minimalen und maximalen Werte des Fachs gelten. Wenn ich zum Beispiela = np.zeros(10)
,np.histogram(a, bins=1)
kehrt(array([10]), array([-0.5, 0.5]))
. Der Benutzer sucht(amin, amax)
in diesem Fall nach = (0, 0).Die Mühe hat sich für mich sowieso gelohnt, deshalb werde ich hier die schwierigste und am wenigsten elegante Lösung für jeden vorschlagen, der interessiert sein könnte. Meine Lösung besteht darin, einen Multithread-Min-Max-Algorithmus in einem Durchgang in C ++ zu implementieren und damit ein Python-Erweiterungsmodul zu erstellen. Dieser Aufwand erfordert ein wenig Aufwand, um die Verwendung der Python- und NumPy C / C ++ - APIs zu erlernen. Hier werde ich den Code zeigen und einige kleine Erklärungen und Referenzen für alle geben, die diesen Weg beschreiten möchten.
Multithread Min / Max
Hier ist nichts zu interessant. Das Array ist in große Teile unterteilt
length / workers
. Das min / max wird für jeden Block in a berechnetfuture
, der dann nach dem globalen min / max gescannt wird.Das Python-Erweiterungsmodul
Hier wird es hässlich ... Eine Möglichkeit, C ++ - Code in Python zu verwenden, besteht darin, ein Erweiterungsmodul zu implementieren. Dieses Modul kann mit dem
distutils.core
Standardmodul erstellt und installiert werden . Eine vollständige Beschreibung dessen, was dies bedeutet, finden Sie in der Python-Dokumentation: https://docs.python.org/3/extending/extending.html . HINWEIS: Es gibt sicherlich andere Möglichkeiten, ähnliche Ergebnisse zu erzielen, um https://docs.python.org/3/extending/index.html#extending-index zu zitieren :Im Wesentlichen ist dieser Weg wahrscheinlich eher akademisch als praktisch. Nachdem dies gesagt wurde, habe ich als nächstes eine Moduldatei erstellt, indem ich mich ziemlich nah an das Tutorial gehalten habe. Dies ist im Wesentlichen ein Boilerplate, damit Distutils wissen, was mit Ihrem Code zu tun ist, und daraus ein Python-Modul erstellen. Bevor Sie dies tun, ist es wahrscheinlich ratsam, eine virtuelle Python- Umgebung zu erstellen, damit Sie Ihre Systempakete nicht verschmutzen (siehe https://docs.python.org/3/library/venv.html#module-venv ).
Hier ist die Moduldatei:
In dieser Datei werden Python und die NumPy-API in erheblichem Umfang verwendet. Weitere Informationen finden Sie unter: https://docs.python.org/3/c-api/arg.html#c.PyArg_ParseTuple und für NumPy : https://docs.scipy.org/doc/numpy/reference/c-api.array.html .
Modul installieren
Als nächstes müssen Sie distutils verwenden, um das Modul zu installieren. Dies erfordert eine Setup-Datei:
Um das Modul endgültig zu installieren, führen Sie es
python3 setup.py install
in Ihrer virtuellen Umgebung aus.Testen des Moduls
Schließlich können wir testen, ob die C ++ - Implementierung tatsächlich die naive Verwendung von NumPy übertrifft. Dazu ein einfaches Testskript:
Hier sind die Ergebnisse, die ich dabei erzielt habe:
Diese sind weitaus weniger ermutigend als die Ergebnisse früher im Thread, die eine etwa 3,5-fache Beschleunigung anzeigten und kein Multithreading enthielten. Die Ergebnisse, die ich erzielt habe, sind einigermaßen vernünftig. Ich würde erwarten, dass der Aufwand für das Threading und die Zeit dominieren, bis die Arrays sehr groß werden. Ab diesem Zeitpunkt würde sich die Leistungssteigerung dem
std::thread::hardware_concurrency
x-Anstieg nähern .Fazit
Es scheint sicherlich Raum für anwendungsspezifische Optimierungen für einige NumPy-Codes zu geben, insbesondere im Hinblick auf Multithreading. Ob sich die Mühe lohnt oder nicht, ist mir nicht klar, aber es scheint sicherlich eine gute Übung (oder so) zu sein. Ich denke, dass das Erlernen einiger dieser "Tools von Drittanbietern" wie Cython möglicherweise eine bessere Zeitnutzung darstellt, aber wer weiß.
quelle
v = min_max_it->get();
. Dieget
Methode blockiert, bis das Ergebnis fertig ist, und gibt es zurück. Da die Schleife jede Zukunft durchläuft, wird sie erst beendet, wenn alle abgeschlossen sind. future.get ()Der kürzeste Weg, den ich mir ausgedacht habe, ist folgender:
Aber da es das Array sortiert, ist es nicht das effizienteste.
Ein anderer kurzer Weg wäre:
Dies sollte effizienter sein, aber das Ergebnis wird berechnet und ein Float zurückgegeben.
quelle