Was ist bei einem NumPy- Array A der schnellste / effizienteste Weg, um dieselbe Funktion f auf jede Zelle anzuwenden ?
Angenommen, wir weisen A (i, j) das f (A (i, j)) zu .
Die Funktion f hat keinen Binärausgang, daher helfen die Maskierungsoperationen nicht.
Ist die "offensichtliche" Doppelschleifeniteration (durch jede Zelle) die optimale Lösung?
Antworten:
Sie können die Funktion einfach vektorisieren und dann jedes Mal, wenn Sie sie benötigen, direkt auf ein Numpy-Array anwenden:
Es ist wahrscheinlich besser, einen expliziten Ausgabetyp direkt beim Vektorisieren anzugeben:
quelle
vectorize
Funktionsbeschreibung: Die Vektorisierungsfunktion wird hauptsächlich zur Vereinfachung und nicht zur Leistung bereitgestellt. Die Implementierung ist im Wesentlichen eine for-Schleife. Dies wird den Prozess also höchstwahrscheinlich überhaupt nicht beschleunigen.vectorize
der Rückgabetyp bestimmt wird. Das hat Fehler erzeugt.frompyfunc
ist etwas schneller, gibt aber ein dtype-Objektarray zurück. Beide Feed-Skalare, keine Zeilen oder Spalten.np.vectorize
meine Funktion (die RK45 verwendet) einschalte, beschleunige ich mich um den Faktor ~ 20.Eine ähnliche Frage lautet: Zuordnung eines NumPy-Arrays an Ort und Stelle . Wenn Sie ein Ufunc für Ihr f () finden, sollten Sie den Parameter out verwenden.
quelle
Wenn Sie mit Zahlen und arbeiten
f(A(i,j)) = f(A(j,i))
, können Sie scipy.spatial.distance.cdist verwenden , um f als Abstand zwischenA(i)
und zu definierenA(j)
.quelle
Ich glaube, ich habe eine bessere Lösung gefunden. Die Idee, die Funktion in Python Universal Function zu ändern (siehe Dokumentation ), die parallele Berechnungen unter der Haube durchführen kann.
Man kann sein eigenes Customized
ufunc
in C schreiben , was sicherlich effizienter ist, oder durch Aufrufennp.frompyfunc
der eingebauten Factory-Methode. Nach dem Testen ist dies effizienter alsnp.vectorize
:Ich habe auch größere Proben getestet und die Verbesserung ist proportional. Einen Vergleich der Leistungen anderer Methoden finden Sie in diesem Beitrag
quelle
Wenn das 2d-Array (oder nd-Array) C- oder F-zusammenhängend ist, ist diese Aufgabe, eine Funktion auf ein 2d-Array abzubilden, praktisch dieselbe wie die Aufgabe, eine Funktion auf ein 1d-Array abzubilden - wir nur muss es so sehen, zB via
np.ravel(A,'K')
.Mögliche Lösung für 1d-Array wurde beispielsweise diskutiert hier .
Wenn jedoch der Speicher des 2d-Arrays nicht zusammenhängend ist, ist die Situation etwas komplizierter, da mögliche Cache-Fehler vermieden werden sollen, wenn die Achse in falscher Reihenfolge behandelt wird.
Numpy verfügt bereits über eine Maschinerie, um Achsen in der bestmöglichen Reihenfolge zu verarbeiten. Eine Möglichkeit, diese Maschine zu benutzen, ist
np.vectorize
. In der Dokumentation von numpynp.vectorize
heißt es jedoch, dass es "hauptsächlich aus Gründen der Benutzerfreundlichkeit und nicht der Leistung bereitgestellt wird" - eine langsame Python-Funktion bleibt eine langsame Python-Funktion mit dem gesamten damit verbundenen Overhead! Ein weiteres Problem ist der enorme Speicherverbrauch - siehe zum Beispiel diesen SO-Beitrag .Wenn man eine C-Funktion ausführen möchte, aber die Maschinen von numpy verwenden möchte, ist es eine gute Lösung, numba für die Erstellung von Ufuncs zu verwenden, zum Beispiel:
Es ist leicht
np.vectorize
zu schlagen, aber auch, wenn dieselbe Funktion als Multiplikation / Addition von Numpy-Arrays ausgeführt wird, d. H.Im Anhang dieser Antwort finden Sie den Zeitmesscode:
Numbas Version (grün) ist ungefähr 100-mal schneller als die Python-Funktion (dh
np.vectorize
), was nicht überraschend ist. Es ist aber auch etwa zehnmal schneller als die Numpy-Funktionalität, da die Numbas-Version keine Zwischen-Arrays benötigt und somit den Cache effizienter nutzt.Der ufunc-Ansatz von numba ist zwar ein guter Kompromiss zwischen Benutzerfreundlichkeit und Leistung, aber immer noch nicht das Beste, was wir tun können. Es gibt jedoch keine Silberkugel oder einen Ansatz, der für eine Aufgabe am besten geeignet ist - man muss verstehen, wo die Grenzen liegen und wie sie gemindert werden können.
Zum Beispiel für transzendentale Funktionen (zB
exp
,sin
,cos
) numba bietet keine Vorteile gegenüber der numpynp.exp
(es gibt keine temporären Arrays erstellt - die Hauptquelle der Speed-up). Meine Anaconda-Installation verwendet jedoch Intels VML für Vektoren, die größer als 8192 sind - dies ist einfach nicht möglich, wenn der Speicher nicht zusammenhängend ist. Daher ist es möglicherweise besser, die Elemente in einen zusammenhängenden Speicher zu kopieren, um Intels VML verwenden zu können:Aus Gründen der Fairness des Vergleichs habe ich die Parallelisierung von VML deaktiviert (siehe Code im Anhang):
Wie man sehen kann, wird der Kopieraufwand nach dem Start von VML mehr als ausgeglichen. Sobald jedoch die Daten für den L3-Cache zu groß werden, ist der Vorteil minimal, da die Aufgabe erneut an die Speicherbandbreite gebunden wird.
Auf der anderen Seite könnte numba auch Intels SVML verwenden, wie in diesem Beitrag erläutert :
und Verwendung von VML mit Parallelisierungsausbeuten:
Die Version von numba hat weniger Overhead, aber für einige Größen schlägt VML SVML trotz des zusätzlichen Kopieraufwands - was keine Überraschung ist, da die ufuncs von numba nicht parallelisiert sind.
Auflistungen:
A. Vergleich der Polynomfunktion:
B. Vergleich von
exp
:quelle
Alle obigen Antworten lassen sich gut vergleichen, aber wenn Sie eine benutzerdefinierte Funktion für die Zuordnung verwenden müssen und dies auch tun, müssen
numpy.ndarray
Sie die Form des Arrays beibehalten.Ich habe nur zwei verglichen, aber es wird die Form von behalten
ndarray
. Ich habe das Array mit 1 Million Einträgen zum Vergleich verwendet. Hier benutze ich die Quadratfunktion. Ich präsentiere den allgemeinen Fall für ein n-dimensionales Array. Für zweidimensional machen Sie einfachiter
für 2D.Ausgabe
Hier können Sie die
numpy.fromiter
Benutzerquadratfunktion deutlich sehen. Verwenden Sie eine beliebige Funktion Ihrer Wahl. Wenn Ihre Funktion davon abhängt,i, j
dass es sich um Array-Indizes handelt, iterieren Sie über die Größe des Arraysfor ind in range(arr.size)
. Verwenden Sie diese Optionnumpy.unravel_index
, umi, j, ..
anhand Ihres 1D-Index und der Form des Arrays numpy.unravel_index zu ermittelnDiese Antwort ist inspiriert von meiner Antwort auf eine andere Frage hier
quelle