Эффективно сортировать массив numpy в порядке убывания?


121

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

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

> 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

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

14
Задокументировано ли это поведение, поскольку оно довольно неинтуитивно.
ebarr

18
Похоже, это работает, потому что [::-1]просто указывает numpy перебирать массив в обратном направлении, а не фактически переупорядочивать массив. Поэтому, когда происходит сортировка на месте, она фактически сортирует в возрастающем порядке и перемещает биты, но оставляет нетронутой часть обратной итерации.
perimosocordiae

47
С a=np.array((...))идиомой a[::-1]ничего не меняет, это просто новый взгляд на те же данные, а точнее зеркальное представление. Этот метод a[::-1].sort() работает с зеркальным изображением , подразумевая, что при sortперемещении влево меньшего элемента в его зеркальном изображении, в действительности он перемещает его вправо в реальном блоке памяти aмассива. Зеркальное отображение сортируется в порядке возрастания, реальные данные сортируются в порядке убывания. Попробуйте дома самостоятельно, с разными монетами и зеркалом!
gboffi

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

93
>>> 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: Под "сложным массивом" вы имели в виду массив комплексных чисел? Или вы имели в виду массив с какой-то другой сложностью (если да, пожалуйста, укажите, какой сложности). В любом случае, я думаю, вы могли бы подробнее рассказать о своем ответе, выяснив, как другие методы могут дать сбой. Спасибо.
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

Здравствуйте, я искал решение для обратной сортировки двумерного массива numpy, и я не смог найти ничего, что работало бы, но я думаю, что наткнулся на решение, которое я загружаю на случай, если кто-то окажется в той же лодке.

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 Он также работает с объектами datetime.

Пример:

    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.