Najbardziej wydajny sposób na odwrócenie tablicy numpy

276

Wierzcie lub nie, po wyprofilowaniu mojego obecnego kodu powtarzające się operacje odwracania tablicy numpy zjadły ogromną część czasu pracy. To, co mam teraz, to wspólna metoda oparta na widoku:

reversed_arr = arr[::-1]

Czy jest jakiś inny sposób, aby zrobić to bardziej efektywnie, czy to tylko złudzenie mojej obsesji na punkcie nierealistycznego numpicznego wykonania?

nye17
źródło
27
Er ... arr[::-1]po prostu zwraca odwrócony widok. Jest tak szybki, jak to tylko możliwe, i nie zależy od liczby elementów w tablicy, ponieważ po prostu zmienia kroki. Czy to, co cofasz, faktycznie jest tablicą liczb liczbowych?
Joe Kington,
tak, w rzeczywistości arrjest tablicą numpy.
nye17
12
Hmmm ... Cóż, na moim laptopie trwa około 670 nanosekund, niezależnie od długości matrycy. Jeśli to jest twoje wąskie gardło, być może będziesz musiał zmienić języki ... Jestem pewien, że nie znajdziesz szybszego sposobu na odwrócenie tablicy numpy. W każdym razie powodzenia!
Joe Kington,
6
Cóż, musisz koniecznie uruchomić go w pętli? W niektórych przypadkach lepiej jest utworzyć tablicę numpy z milionami przedmiotów, a następnie operować na całej tablicy. Nawet jeśli robisz metodę różnic skończonych lub coś podobnego, gdzie wynik zależy od poprzedniego wyniku, możesz czasem to zrobić. (Nacisk na czasem ...) W każdym razie, jeśli prędkość jest głównym celem, fortran nadal jest królem. f2pyjest twoim przyjacielem! Często warto pisać krytyczne pod względem wydajności części algorytmu (szczególnie w obliczeniach naukowych) w innym języku i wywoływać go z języka python. Powodzenia!
Joe Kington
1
@berto. Jest wolniejszy, ponieważ jest opakowaniem dla arr[::-1]: github.com/numpy/numpy/blob/master/numpy/lib/twodim_base.py . Szukać def flipud. Funkcja ma dosłownie cztery linie.
Szalony fizyk

Odpowiedzi:

239

Podczas tworzenia reversed_arrtworzysz widok do oryginalnej tablicy. Następnie możesz zmienić oryginalną tablicę, a widok zostanie zaktualizowany, aby odzwierciedlić zmiany.

Czy odtwarzasz widok częściej niż potrzebujesz? Powinieneś być w stanie zrobić coś takiego:

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

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

Nie jestem ekspertem od numpy, ale wydaje się, że byłby to najszybszy sposób na robienie tego w numpy. Jeśli to właśnie robisz, nie sądzę, że możesz to poprawić.

PS Świetna dyskusja na temat wielu wyświetleń tutaj:

Zobacz na tablicę numpy?

steveha
źródło
Czy pomaga utworzyć obiekt plasterka, a następnie użyć go ponownie na wielu tablicach?
endolith
1
Właściwie właśnie go przetestowałem i nie widzę żadnej różnicy w stosunku do obiektu plasterka utworzonego poza pętlą. (Och, czekaj, to jest bardzo nieznacznie szybsze. Powtarzalnie 43,4 ms wobec 44,3 ms dla pętli 1000000)
endolith
Jaka look_atfunkcja ma pełnić?
mrgloom,
1
@mrgloom To ma reprezentować każde zadanie, które patrzy na dane. Celem tego przykładu było wykazanie, że widok reversed_arrjest nadal użyteczny po zmianie danych podstawowych. Zapisanie nowych wartości w tablicy nie unieważnia widoku. W rzeczywistości można również użyć widoku do zapisania nowych wartości w tablicy. reversed_arr[0] = 99ustawiłby ostatni element w tablicy na 99, tak samo jak arr[-1] = 99zrobiłby.
steveha
60

Jak wspomniano powyżej, a[::-1]tak naprawdę tworzy tylko widok, więc jest to operacja o stałym czasie (i jako taka nie trwa dłużej, gdy tablica się powiększa). Jeśli potrzebujesz, aby tablica była ciągła (na przykład ponieważ wykonujesz z nią wiele operacji wektorowych), ascontiguousarrayjest ona tak szybka jak flipup/ fliplr:

wprowadź opis zdjęcia tutaj


Kod do wygenerowania wykresu:

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
źródło
perfplot wymaga co najmniej Pythona 3.6, ponieważ używa ciągów f (interpolacja
literałów
42

Ponieważ wydaje się, że nie jest to jeszcze oznaczone jako odpowiedź ... Odpowiedź Thomasa Arildsena powinna być właściwa: po prostu użyj

np.flipud(your_array) 

jeśli jest to tablica 1d (tablica kolumnowa).

Z matrize zrobić

fliplr(matrix)

jeśli chcesz odwrócić wiersze i flipud(matrix)jeśli chcesz odwrócić kolumny. Nie ma potrzeby przekształcania tablicy 1d w 2-wymiarową tablicę wierszy (macierz z jedną warstwą Brak), a następnie odwracanie jej.

zauberfein
źródło
38

np.fliplr() odwraca tablicę od lewej do prawej.

Pamiętaj, że w przypadku tablic 1d musisz trochę oszukać:

arr1d = np.array(some_sequence)
reversed_arr = np.fliplr([arr1d])[0]
tooty44
źródło
34
reversed_arr = np.flipud(arr1d)wydaje się działać bezpośrednio.
Thomas Arildsen
3

Rozbuduję wcześniejszą odpowiedź na temat np.fliplr(). Oto kod ilustrujący tworzenie tablicy 1d, przekształcanie jej w tablicę 2d, odwracanie, a następnie konwersję z powrotem do tablicy 1d. time.clock()zostaną wykorzystane do zachowania czasu, który jest wyrażony w sekundach.

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

Bez instrukcji drukowania:

[2 1 0]
0.00203907123594

Z wypisanym komentarzem:

5.59799927506e-05

Pod względem wydajności uważam, że to przyzwoite. Dla tych z was, którzy lubią to robić w jednym wierszu, oto ta forma.

np.fliplr(np.atleast_2d(np.array(range(3))))[0]
M. Murphy
źródło
3
Mierzenie czasu przy tak małej tablicy jest dość bezużyteczne. Jeśli chcesz porównać rzeczy, lepiej użyć czegoś, co zajmuje trochę czasu, na przykład 3000, a może nawet więcej elementów.
Barabas
0

Rozwijając to, co powiedzieli inni, dam krótki przykład.

Jeśli masz tablicę 1D ...

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

Ale jeśli pracujesz z tablicą 2D ...

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

To nie odwraca Matrycy.

Powinien użyć np.flip do odwrócenia elementów

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

Jeśli chcesz wydrukować elementy matrycy jeden po drugim, użyj płaskiego wraz z przerzuceniem

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

Aby działał z liczbami ujemnymi i długą listą, możesz wykonać następujące czynności:

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

Gdzie flipud jest dla tablicy 1d

Boris Stoyanov
źródło