Ефективне сортування масивного масиву у порядку зменшення?


121

Я здивований, що цього конкретного питання раніше не задавали, але я дійсно не знайшов його ні на SO, ні на документації np.sort.

Скажімо, у мене є випадковий нумеровий масив, що містить цілі числа, наприклад:

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

Якщо я сортую його, я отримую порядку за зростанням за замовчуванням:

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

але я хочу, щоб рішення було впорядковано у порядку зменшення .

Тепер я знаю, що завжди можу:

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

але чи ефективне це останнє твердження ? Чи не створює він копію у порядку зростання, а потім обертає цю копію, щоб отримати результат у зворотному порядку? Якщо це дійсно так, чи є ефективна альтернатива? Це не схоже на те, що np.sortприймає параметри для зміни знаку порівнянь в операції сортування, щоб навести речі в зворотному порядку.

Відповіді:


139

temp[::-1].sort()сортує масив на місці, тоді як np.sort(temp)[::-1]створює новий масив.

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

30
Дякую, але як temp[::-1].sort()знати, що він має сортувати у зворотному порядку ?? Я читаю так: поверніть початковий масив і сортуйте його (у порядку зростання). Чому б реверсування вихідного масиву (приходить у випадковому порядку), а потім сортування його у порядку зростання, повертає масив у порядку реверсування?
Амеліо Васкес-Рейна

14
Чи задокументована така поведінка, як це досить неінтуїтивно.
ebarr

18
Це виглядає так, як це працює, тому що [::-1]просто повідомляє numpy перебирати масив назад, а не фактично переупорядковувати масив. Отже, коли відбувається місце на місці сортування, воно фактично сортує у порядку зростання і переміщує біти навколо, але залишає частину зворотної ітерації недоторканою.
perimosocordiae

45
З a=np.array((...))ідіомою a[::-1]нічого не обертається, це просто новий погляд на ті самі дані, точніше дзеркальний вигляд. Метод a[::-1].sort() працює на дзеркальному зображенні , маючи на увазі, що коли sortрухається ліворуч менший елемент у своєму дзеркальному зображенні, насправді він переміщує його праворуч у реальному блоці пам'яті aмасиву. Дзеркальний вигляд сортується у порядку зростання, реальні дані сортуються у порядку зменшення. Спробуйте вдома самостійно, з різними монетами та дзеркалом!
gboffi

30
Це дійсно повинно бути додано як читабельний параметр, np.sort(temp,order='descending')а не вимагати подібних хак
Натан

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

2
Найкраща відповідь - коротка і солодка, і не потрібно знати, axisдо чого np.sortбуло застосовано.
Люк Девіс

2
Це відрізняється від того, np.sort(temp)[::-1]що він розміщує nans в задній частині масиву, а не спереду. Це добре чи погано - це для обговорення ..
Бен

15

Для коротких масивів я пропоную використовувати np.argsort(), знаходячи індекси відсортованого масиву negatived, який трохи швидше, ніж реверсування відсортованого масиву:

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

a[np.argsort(-a)]це, мабуть, найкращий підхід до будь-яких інших на цій сторінці. Немає -1 ступінчастого перевороту та ще один мінус знак, про який можна подумати.
Ярад

8

На жаль, коли у вас складний масив, він np.sort(temp)[::-1]працює тільки належним чином. Два інших способи, згадані тут, не є ефективними.


@ anishtain4: Під "складним масивом" ви мали на увазі масив складних чисел? Або ви мали на увазі масив з іншим видом складності (якщо так, то pls вкажіть, яку складність). В будь-якому випадку я вважаю, що ви могли б детальніше пояснити свою відповідь, вивчивши, як інші методи можуть не вдатися. Дякую.
fountainhead

@fountainhead Я маю на увазі масив складних чисел. Оскільки це старе питання, я не пам’ятаю свого тестового випадку з тих пір, щоб детальніше розглянути.
anishtain4

8

Будьте обережні з розмірами.

Дозволяти

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

Повний реверс

z = x[-I]
z = -np.sort(-x)
z = np.flip(y)
  • flipзмінено 1.15, потрібні попередні версії . Рішення .1.14 axispip install --upgrade numpy

Перший вимір змінено

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

Другий вимір перетворений

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

Тестування

Тестування на масиві 100 × 10 × 10 1000 разів.

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

В основному це пов'язано швидше з переіндексацією 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)

np.flip () - супер
Дарій

6

Здрастуйте, я шукав рішення для зворотного сортування двовимірного нумерованого масиву, і я не міг знайти нічого, що спрацювало, але я думаю, що я натрапив на рішення, яке я завантажую на випадок, якщо хтось знаходиться в одному човні.

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

np.sort сортує висхідне, яке не є тим, що ви хочете, але команда fliplr гортає рядки зліва направо! Здається, працює!

Сподіваюся, це допоможе тобі!

Я думаю, це схоже на пропозицію про -np.sort (-a) вище, але мене відклали за коментар, що це не завжди працює. Можливо, моє рішення не завжди буде працювати, проте я протестував його з кількома масивами і, здається, все в порядку.


1

Спочатку ви можете сортувати масив ( Виходячи за замовчуванням), а потім застосувати np.flip () ( https://docs.scipy.org/doc/numpy/reference/generated/numpy.flip.html )

FYI Також працює з об'єктами дати.

Приклад:

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

Для тих, хто має NaN у своїх масивах, будьте обережні, різні запропоновані методи дають різні результати. Наприклад, якщо x = np.array([2,3,np.nan,1,0]) тоді np.flip(np.sort(x))підхід дає [nan 3. 2. 1. 0.], тоді як -np.sort(-x)підхід дає [3. 2. 1. 0. nan].
Уве Майєр

1

Ось швидкий трюк

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]

-3

Я пропоную скористатися цим ...

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

наприклад:

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

Тоді ваш результат:

[ 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. ]

1
Як це вирішує проблему? Ви просто створити повністю, непов'язаний, новий ( по спадаючій) масив , який - до речі - можна було б зробити більш ефективним чином: np.arange(20-0.5, 10-0.5, -0.5). Але це вже інша історія і може бути, через гіршу читаність, дискусійною. Вхідний масив взагалі не відсортований
Даніель
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.