Wydajne sortowanie tablicy numpy w porządku malejącym?

121

Dziwię się, że to konkretne pytanie nie zostało wcześniej zadane, ale tak naprawdę nie znalazłem go w SO ani w dokumentacji np.sort.

Powiedzmy, że mam losową tablicę numpy zawierającą liczby całkowite, np:

> temp = np.random.randint(1,10, 10)    
> temp
array([2, 4, 7, 4, 2, 2, 7, 6, 4, 4])

Jeśli je posortuję, domyślnie otrzymuję kolejność rosnącą:

> np.sort(temp)
array([2, 2, 2, 4, 4, 4, 4, 6, 7, 7])

ale chcę, aby rozwiązanie zostało posortowane w porządku malejącym .

Teraz wiem, że zawsze mogę:

reverse_order = np.sort(temp)[::-1]

ale czy to ostatnie stwierdzenie jest skuteczne ? Czy nie tworzy kopii w kolejności rosnącej, a następnie odwraca tę kopię, aby uzyskać wynik w odwróconej kolejności? Jeśli tak jest, czy istnieje skuteczna alternatywa? Nie wygląda na to, że np.sortakceptuje parametry, aby zmienić znak porównań w operacji sortowania, aby uzyskać rzeczy w odwrotnej kolejności.

Amelio Vazquez-Reina
źródło

Odpowiedzi:

139

temp[::-1].sort()sortuje tablicę w miejscu, podczas gdy np.sort(temp)[::-1]tworzy nową tablicę.

In [25]: temp = np.random.randint(1,10, 10)

In [26]: temp
Out[26]: array([5, 2, 7, 4, 4, 2, 8, 6, 4, 4])

In [27]: id(temp)
Out[27]: 139962713524944

In [28]: temp[::-1].sort()

In [29]: temp
Out[29]: array([8, 7, 6, 5, 4, 4, 4, 4, 2, 2])

In [30]: id(temp)
Out[30]: 139962713524944
Padraic Cunningham
źródło
30
Dzięki, ale skąd temp[::-1].sort()wiadomo, że musi sortować w odwrotnej kolejności? Sposób, w jaki to czytam, jest: odwróć oryginalną tablicę, a następnie posortuj ją (w porządku rosnącym). Dlaczego odwrócenie oryginalnej tablicy (w kolejności losowej), a następnie posortowanie jej w porządku rosnącym zwróci tablicę w kolejności odwrotnej?
Amelio Vazquez-Reina
14
Czy to zachowanie jest udokumentowane, ponieważ jest dość nieintuicyjne.
ebarr
18
Wygląda na to, że to działa, ponieważ po [::-1]prostu mówi numpy, aby wykonał iterację po tablicy od tyłu, zamiast faktycznie zmieniać kolejność tablicy. Więc kiedy ma miejsce sortowanie na miejscu, w rzeczywistości sortuje w porządku rosnącym i przesuwa bity dookoła, ale pozostawia nietkniętą część iteracji wstecz.
perimosocordiae
46
Z a=np.array((...))idiomem a[::-1]niczego nie odwraca, to po prostu nowe spojrzenie na te same dane, a dokładniej widok lustrzany. Metoda a[::-1].sort() działa na lustrzanym obrazie , co oznacza, że ​​gdy sortprzesuwa się w lewo mniejszy element w jego lustrzanym obrazie, w rzeczywistości przesuwa go w prawo w rzeczywistym bloku pamięci atablicy. Widok lustrzany jest sortowany w porządku rosnącym, dane rzeczywiste są sortowane w porządku malejącym. Wypróbuj sam w domu, używając różnych monet i lustra!
gboffi
30
To naprawdę powinno być dodane jako czytelny parametr, np.sort(temp,order='descending')a nie wymagający tego rodzaju hacków
Nathan
92
>>> a=np.array([5, 2, 7, 4, 4, 2, 8, 6, 4, 4])

>>> np.sort(a)
array([2, 2, 4, 4, 4, 4, 5, 6, 7, 8])

>>> -np.sort(-a)
array([8, 7, 6, 5, 4, 4, 4, 4, 2, 2])
Mike O'Connor
źródło
2
Najlepsza odpowiedź - krótka i słodka, a wiedza o tym, axisdo czego np.sortzostała zastosowana, nie jest konieczna.
Luke Davis,
2
Różni się to od np.sort(temp)[::-1]tego, że umieszcza nans z tyłu tablicy zamiast z przodu. To, czy to dobrze, czy źle, jest przedmiotem dyskusji ..
Ben
15

W przypadku krótkich tablic sugeruję użycie np.argsort(), znajdując indeksy posortowanej tablicy zanegowanej, co jest nieco szybsze niż odwracanie posortowanej tablicy:

In [37]: temp = np.random.randint(1,10, 10)

In [38]: %timeit np.sort(temp)[::-1]
100000 loops, best of 3: 4.65 µs per loop

In [39]: %timeit temp[np.argsort(-temp)]
100000 loops, best of 3: 3.91 µs per loop
Kasravnd
źródło
a[np.argsort(-a)]jest prawdopodobnie najlepszym podejściem do innych na tej stronie. Brak zmiany o 1 krok i jeden znak minus mniej do przemyślenia.
Jarad
8

Niestety, gdy masz złożoną tablicę, np.sort(temp)[::-1]działa tylko poprawnie. Dwie inne wymienione tutaj metody nie są skuteczne.

anishtain4
źródło
@ anishtain4: Czy przez „tablicę zespoloną” masz na myśli tablicę liczb zespolonych? A może miałeś na myśli tablicę z jakimś innym rodzajem złożoności (jeśli tak, proszę podać rodzaj złożoności). W obu przypadkach wydaje mi się, że możesz bardziej szczegółowo omówić swoją odpowiedź, zastanawiając się, dlaczego inne metody mogą zawodzić. Dzięki.
fontanna
@fountainhead Mam na myśli tablicę liczb zespolonych. Ponieważ jest to stare pytanie, nie pamiętam mojego przypadku testowego od tamtego czasu, aby bardziej szczegółowo omawiać.
anishtain
8

Uważaj na wymiary.

Pozwolić

x  # initial numpy array
I = np.argsort(x) or I = x.argsort() 
y = np.sort(x)    or y = x.sort()
z  # reverse sorted array

Full Reverse

z = x[-I]
z = -np.sort(-x)
z = np.flip(y)
  • flipzmienione 1.15, wymagane są poprzednie wersje . Rozwiązanie: .1.14 axispip install --upgrade numpy

Odwrócony pierwszy wymiar

z = y[::-1]
z = np.flipud(y)
z = np.flip(y, axis=0)

Drugi wymiar odwrócony

z = y[::-1, :]
z = np.fliplr(y)
z = np.flip(y, axis=1)

Testowanie

Testowanie na tablicy 100 × 10 × 10 1000 razy.

Method       | Time (ms)
-------------+----------
y[::-1]      | 0.126659  # only in first dimension
-np.sort(-x) | 0.133152
np.flip(y)   | 0.121711
x[-I]        | 4.611778

x.sort()     | 0.024961
x.argsort()  | 0.041830
np.flip(x)   | 0.002026

Wynika to głównie z ponownego indeksowania, a nie argsort.

# Timing code
import time
import numpy as np


def timeit(fun, xs):
    t = time.time()
    for i in range(len(xs)):  # inline and map gave much worse results for x[-I], 5*t
        fun(xs[i])
    t = time.time() - t
    print(np.round(t,6))

I, N = 1000, (100, 10, 10)
xs = np.random.rand(I,*N)
timeit(lambda x: np.sort(x)[::-1], xs)
timeit(lambda x: -np.sort(-x), xs)
timeit(lambda x: np.flip(x.sort()), xs)
timeit(lambda x: x[-x.argsort()], xs)
timeit(lambda x: x.sort(), xs)
timeit(lambda x: x.argsort(), xs)
timeit(lambda x: np.flip(x), xs)
A. West
źródło
np.flip () - super
Darius
6

Witam Szukałem rozwiązania odwrotnego sortowania dwuwymiarowej tablicy numpy i nie mogłem znaleźć niczego, co działałoby, ale wydaje mi się, że natknąłem się na rozwiązanie, które przesyłam na wypadek, gdyby ktoś był na tej samej łodzi.

x=np.sort(array)
y=np.fliplr(x)

np.sort sortuje rosnąco, co nie jest tym, czego chcesz, ale polecenie fliplr odwraca wiersze od lewej do prawej! Wydaje się działać!

Mam nadzieję, że ci to pomoże!

Wydaje mi się, że jest to podobne do sugestii dotyczącej -np.sort (-a) powyżej, ale odstraszył mnie komentarz, że nie zawsze działa. Być może moje rozwiązanie też nie zawsze będzie działać, jednak przetestowałem je z kilkoma tablicami i wydaje się, że jest w porządku.

Naz
źródło
1

Możesz najpierw posortować tablicę (domyślnie rosnąco), a następnie zastosować np.flip () ( https://docs.scipy.org/doc/numpy/reference/generated/numpy.flip.html )

FYI Działa również z obiektami datetime.

Przykład:

    x = np.array([2,3,1,0]) 
    x_sort_asc=np.sort(x) 
    print(x_sort_asc)

    >>> array([0, 1, 2, 3])

    x_sort_desc=np.flip(x_sort_asc) 
    print(x_sort_desc)

    >>> array([3,2,1,0])
maleckicoa
źródło
Dla tych, którzy mają NaN w swoich tablicach, bądź ostrożny, różne proponowane metody dają różne wyniki. Na przykład, jeśli x = np.array([2,3,np.nan,1,0]) to np.flip(np.sort(x))podejście daje [nan 3. 2. 1. 0.], podczas gdy -np.sort(-x)podejście daje [3. 2. 1. 0. nan].
Uwe Mayer
1

Oto krótka sztuczka

In[3]: import numpy as np
In[4]: temp = np.random.randint(1,10, 10)
In[5]: temp
Out[5]: array([5, 4, 2, 9, 2, 3, 4, 7, 5, 8])

In[6]: sorted = np.sort(temp)
In[7]: rsorted = list(reversed(sorted))
In[8]: sorted
Out[8]: array([2, 2, 3, 4, 4, 5, 5, 7, 8, 9])

In[9]: rsorted
Out[9]: [9, 8, 7, 5, 5, 4, 4, 3, 2, 2]
Don Coder
źródło
-3

sugeruję użycie tego ...

np.arange(start_index, end_index, intervals)[::-1]

na przykład:

np.arange(10, 20, 0.5)
np.arange(10, 20, 0.5)[::-1]

Następnie twój resault:

[ 19.5,  19. ,  18.5,  18. ,  17.5,  17. ,  16.5,  16. ,  15.5,
    15. ,  14.5,  14. ,  13.5,  13. ,  12.5,  12. ,  11.5,  11. ,
    10.5,  10. ]
morteza omidipoor
źródło
1
Jak to rozwiązuje problem? Jesteś po prostu tworząc całkowicie niezwiązane, nowy (malejąco) tablicę, która - nawiasem mówiąc - może być wykonane w sposób bardziej efektywny: np.arange(20-0.5, 10-0.5, -0.5). Ale to inna historia i może być dyskusyjna ze względu na gorszą czytelność. Tablica wejściowa nie jest w ogóle posortowana
Daniel