Der effizienteste Weg, um ein Numpy-Array umzukehren

275

Ob Sie es glauben oder nicht, nachdem Sie meinen aktuellen Code profiliert haben, hat die wiederholte Operation der Numpy-Array-Umkehrung einen riesigen Teil der Laufzeit verschlungen. Was ich gerade habe, ist die übliche ansichtsbasierte Methode:

reversed_arr = arr[::-1]

Gibt es eine andere Möglichkeit, es effizienter zu machen, oder ist es nur eine Illusion aus meiner Besessenheit von unrealistischer Numpy-Performance?

nye17
quelle
27
Äh ... gibt arr[::-1]nur eine umgekehrte Ansicht zurück. Es ist so schnell wie möglich und hängt nicht von der Anzahl der Elemente im Array ab, da es nur die Schritte ändert. Ist das, was Sie umkehren, tatsächlich ein numpy Array?
Joe Kington
ja, in der Tat arr ist ein numpy Array.
Nye17
12
Hmmm ... Nun, auf meinem Laptop dauert es ungefähr 670 Nanosekunden, unabhängig von der Länge des Arrays. Wenn dies Ihr Engpass ist, müssen Sie möglicherweise die Sprache wechseln ... Ich bin mir ziemlich sicher, dass Sie keinen schnelleren Weg finden werden, ein numpy-Array umzukehren. Auf jeden Fall viel Glück!
Joe Kington
6
Müssen Sie es unbedingt in einer Schleife ausführen? In einigen Fällen ist es besser, ein numpy-Array mit Millionen von Elementen zu erstellen und dann das gesamte Array zu bearbeiten. Selbst wenn Sie eine Finite-Differenzen-Methode oder ähnliches durchführen, bei der das Ergebnis vom vorherigen Ergebnis abhängt, können Sie dies manchmal tun. (Hervorhebung manchmal ...) Auf jeden Fall ist fortran immer noch König, wenn Geschwindigkeit das Hauptziel ist. f2pyist dein Freund! Es lohnt sich oft, leistungskritische Teile eines Algorithmus (insbesondere im wissenschaftlichen Rechnen) in einer anderen Sprache zu schreiben und von Python aus aufzurufen. Viel Glück!
Joe Kington
1
@berto. Es ist langsamer, da es ein Wrapper für arr[::-1]: github.com/numpy/numpy/blob/master/numpy/lib/twodim_base.py ist . Suche nach def flipud. Die Funktion ist buchstäblich vier Zeilen lang.
Verrückter Physiker

Antworten:

239

Beim Erstellen erstellen reversed_arrSie eine Ansicht in das ursprüngliche Array. Sie können dann das ursprüngliche Array ändern, und die Ansicht wird aktualisiert, um die Änderungen widerzuspiegeln.

Erstellen Sie die Ansicht häufiger neu als nötig? Sie sollten in der Lage sein, so etwas zu tun:

arr = np.array(some_sequence)
reversed_arr = arr[::-1]

do_something(arr)
look_at(reversed_arr)
do_something_else(arr)
look_at(reversed_arr)

Ich bin kein Numpy-Experte, aber dies scheint der schnellste Weg zu sein, um Dinge in Numpy zu erledigen. Wenn Sie dies bereits tun, können Sie es meiner Meinung nach nicht verbessern.

PS Tolle Diskussion über numpy Ansichten hier:

Blick auf ein numpy Array?

steveha
quelle
Hilft es, ein Slice-Objekt zu erstellen und es dann auf vielen Arrays wiederzuverwenden?
Endolith
1
Eigentlich habe ich es gerade getestet und sehe keinen Unterschied zu dem außerhalb der Schleife erstellten Slice-Objekt. (Oh, warte, es ist etwas schneller. Wiederholbar 43,4 ms gegenüber 44,3 ms für eine 1000000-Schleife)
Endolith
Was soll die look_atFunktion tun?
Mrgloom
1
@mrgloom Es soll jede Aufgabe darstellen, die die Daten betrachtet. Das Beispiel sollte zeigen, dass die Ansicht reversed_arrnach dem Ändern der zugrunde liegenden Daten weiterhin verwendet werden kann. Das Schreiben neuer Werte in das Array macht die Ansicht nicht ungültig. Tatsächlich können Sie die Ansicht auch zum Schreiben neuer Werte in das Array verwenden. reversed_arr[0] = 99würde das letzte Element im Array auf 99 setzen, genau wie arr[-1] = 99würde.
Steveha
60

Wie oben erwähnt, wird a[::-1]wirklich nur eine Ansicht erstellt, sodass es sich um eine Operation mit konstanter Zeit handelt (und als solche nicht länger dauert, wenn das Array wächst). Wenn das Array zusammenhängend sein muss (z. B. weil Sie viele Vektoroperationen damit ausführen), ascontiguousarrayist es ungefähr so ​​schnell wie flipup/ fliplr:

Geben Sie hier die Bildbeschreibung ein


Code zum Generieren des Plots:

import numpy
import perfplot


perfplot.show(
    setup=lambda n: numpy.random.randint(0, 1000, n),
    kernels=[
        lambda a: a[::-1],
        lambda a: numpy.ascontiguousarray(a[::-1]),
        lambda a: numpy.fliplr([a])[0],
    ],
    labels=["a[::-1]", "ascontiguousarray(a[::-1])", "fliplr"],
    n_range=[2 ** k for k in range(25)],
    xlabel="len(a)",
    logx=True,
    logy=True,
)
Nico Schlömer
quelle
Für perfplot ist mindestens Python 3.6 erforderlich, da f-Strings (Literal String Interpolation) verwendet werden
8:52 Uhr
42

Weil dies noch nicht als beantwortet markiert zu sein scheint ... Die Antwort von Thomas Arildsen sollte die richtige sein: einfach verwenden

np.flipud(your_array) 

wenn es sich um ein 1d-Array (Spaltenarray) handelt.

Mit Matrizen machen

fliplr(matrix)

Wenn Sie Zeilen umkehren und flipud(matrix)Spalten umdrehen möchten. Sie müssen Ihr 1d-Spaltenarray nicht zu einem zweidimensionalen Zeilenarray (Matrix mit einer Ebene "Keine") machen und es dann umdrehen.

zauberfein
quelle
38

np.fliplr() Kippt das Array von links nach rechts.

Beachten Sie, dass Sie es für 1d-Arrays ein wenig austricksen müssen:

arr1d = np.array(some_sequence)
reversed_arr = np.fliplr([arr1d])[0]
tooty44
quelle
34
reversed_arr = np.flipud(arr1d)scheint direkt zu funktionieren.
Thomas Arildsen
3

Ich werde auf die frühere Antwort über näher eingehen np.fliplr(). Hier ist ein Code, der zeigt, wie ein 1d-Array erstellt, in ein 2d-Array umgewandelt, umgedreht und dann wieder in ein 1d-Array konvertiert wird. time.clock()wird verwendet, um die Zeit zu halten, die in Sekunden angegeben wird.

import time
import numpy as np

start = time.clock()
x = np.array(range(3))
#transform to 2d
x = np.atleast_2d(x)
#flip array
x = np.fliplr(x)
#take first (and only) element
x = x[0]
#print x
end = time.clock()
print end-start

Mit unkommentierter Druckanweisung:

[2 1 0]
0.00203907123594

Mit auskommentierter Druckerklärung:

5.59799927506e-05

In Bezug auf die Effizienz finde ich das anständig. Für diejenigen unter Ihnen, die es lieben, es in einer Zeile zu tun, ist hier diese Form.

np.fliplr(np.atleast_2d(np.array(range(3))))[0]
M. Murphy
quelle
3
Das Timing von etwas mit einem so kleinen Array ist ziemlich nutzlos. Wenn Sie Dinge vergleichen möchten, ist es besser, etwas zu verwenden, das eine Weile dauert, wie 3000 oder vielleicht sogar mehr Elemente.
Barabas
0

Ich werde ein kurzes Beispiel geben, um das zu erweitern, was andere gesagt haben.

Wenn Sie ein 1D-Array haben ...

>>> import numpy as np
>>> x = np.arange(4) # array([0, 1, 2, 3])
>>> x[::-1] # returns a view
Out[1]: 
array([3, 2, 1, 0])

Aber wenn Sie mit einem 2D-Array arbeiten ...

>>> x = np.arange(10).reshape(2, 5)
>>> x
Out[2]:
array([[0, 1, 2, 3, 4],
       [5, 6, 7, 8, 9]])

>>> x[::-1] # returns a view:
Out[3]: array([[5, 6, 7, 8, 9],
               [0, 1, 2, 3, 4]])

Dies kehrt die Matrix nicht wirklich um.

Sollte np.flip verwenden, um die Elemente tatsächlich umzukehren

>>> np.flip(x)
Out[4]: array([[9, 8, 7, 6, 5],
               [4, 3, 2, 1, 0]])

Wenn Sie die Elemente einer Matrix einzeln drucken möchten, verwenden Sie flach zusammen mit Flip

>>> for el in np.flip(x).flat:
>>>     print(el, end = ' ')
9 8 7 6 5 4 3 2 1 0
John Mil.
quelle
-1

Damit es mit negativen Zahlen und einer langen Liste funktioniert, können Sie Folgendes tun:

b = numpy.flipud(numpy.array(a.split(),float))

Wo Flipud für 1d Arra ist

Boris Stoyanov
quelle