Разве это не просто ids = np.array(avgDists).argsort()[-n:]?
Хайме
2
@ Джейме: Нет, это не работает. «Правильный ответ» есть [3, 1, 2]. Ваша строка производит [2, 1, 3](если n == 3 в качестве примера)
Dawg
2
@drewk Ну, тогда сделай это ids = np.array(avgDists).argsort()[-n:][::-1]. Дело в том, чтобы избежать копирования всего списка, который вы получите, добавив -перед ним. Не относится к маленькому примеру ОП, может быть для больших случаев.
Хайме
1
@ Джейме: Вы правы. Смотрите мой обновленный ответ. Синтаксис tho прямо противоположен вашему комментарию к конечному фрагменту: np.array(avgDists).argsort()[::-1][:n]он сделает это. Кроме того, если вы собираетесь использовать NumPy, оставайтесь в NUMPY. Сначала конвертируем список в массив: avgDist=np.array(avgDists)потом он становитсяavgDist.argsort()[::-1][:n}
dawg
Ответы:
230
Если вы отрицаете массив, самые низкие элементы становятся самыми высокими элементами и наоборот. Поэтому индексами nвысших элементов являются:
(-avgDists).argsort()[:n]
Другой способ рассуждать об этом, как упомянуто в комментариях , состоит в том , чтобы наблюдать, что большие элементы идут последними в argsort. Итак, вы можете прочитать из хвоста argsort, чтобы найти самые nвысокие элементы:
avgDists.argsort()[::-1][:n]
Оба метода имеют O (n log n) во временной сложности, потому что argsortздесь преобладает вызов. Но у второго подхода есть приятное преимущество: он заменяет отрицание O (n) массива на срез O (1) . Если вы работаете с маленькими массивами внутри циклов, вы можете получить некоторое повышение производительности, избегая этого отрицания, а если вы работаете с огромными массивами, то вы можете сэкономить на использовании памяти, поскольку отрицание создает копию всего массива.
Обратите внимание, что эти методы не всегда дают эквивалентные результаты: если запрашивается стабильная реализация сортировки argsort, например, путем передачи аргумента ключевого слова kind='mergesort', то первая стратегия сохранит стабильность сортировки, но вторая стратегия нарушит стабильность (т. Е. Позиции равных элементы будут перевернуты).
Пример времени:
Используя небольшой массив из 100 поплавков и хвост длиной 30, метод просмотра был примерно на 15% быстрее
>>> avgDists = np.random.rand(100)>>> n =30>>> timeit (-avgDists).argsort()[:n]1.93µs ±6.68 ns per loop (mean ± std. dev. of 7 runs,1000000 loops each)>>> timeit avgDists.argsort()[::-1][:n]1.64µs ±3.39 ns per loop (mean ± std. dev. of 7 runs,1000000 loops each)>>> timeit avgDists.argsort()[-n:][::-1]1.64µs ±3.66 ns per loop (mean ± std. dev. of 7 runs,1000000 loops each)
Для больших массивов, argsort является доминирующим и нет значительной разницы во времени
>>> avgDists = np.random.rand(1000)>>> n =300>>> timeit (-avgDists).argsort()[:n]21.9µs ±51.2 ns per loop (mean ± std. dev. of 7 runs,10000 loops each)>>> timeit avgDists.argsort()[::-1][:n]21.7µs ±33.3 ns per loop (mean ± std. dev. of 7 runs,10000 loops each)>>> timeit avgDists.argsort()[-n:][::-1]21.9µs ±37.1 ns per loop (mean ± std. dev. of 7 runs,10000 loops each)
Обратите внимание, что комментарий от Недима ниже является неправильным. Обрезание до или после реверса не влияет на эффективность, так как обе эти операции только по-разному оценивают массив и не копируют данные.
Еще более эффективно нарезать перед np.array(avgDists).argsort()[:-n][::-1]
реверсом
3
Эти ответы не эквивалентны, если исходный массив содержит nans. В таком случае, первое решение, кажется, дает более естественный результат с nans в конце, а не в начале.
feilchenfeldt
1
Как они сравниваются, когда желательна стабильная сортировка? Предположительно стратегия нарезки меняет равные позиции?
Эрик,
1
@ user3666197 Я чувствовал, что это не имеет отношения к ответу. Независимо от того, создает ли отрицание копию или нет (это делает), здесь не очень важно, важная информация состоит в том, что вычисление отрицания представляет собой сложность O (n) по сравнению с принятием другого среза, который является O (1) .
Вим
1
@ user3666197 Да, это хороший момент - если массив занимает 50% доступной памяти, мы, безусловно, захотим избежать его копирования и замены. Я снова отредактирую, чтобы упомянуть, что там создается копия.
Вим
70
Точно так же, как и Python, он [::-1]переворачивает массив, возвращаемый argsort()и [:n]дает последние n элементов:
Этот ответ хорош, но я чувствую, что ваша формулировка искажает реальные характеристики производительности: «даже при этом очень небольшом наборе данных метод просмотра значительно быстрее» . В действительности отрицание - это O (n), а argsort - это O (n log n) . Это означает, что расхождение во времени уменьшится для больших наборов данных - доминирует термин O (n log n) , однако ваше предложение является оптимизацией части O (n) . Таким образом, сложность остается той же, и именно для этого небольшого набора данных, в частности , мы видим существенные различия.
Вим
2
Асимптотически эквивалентная сложность все еще может означать, что один алгоритм асимптотически в два раза быстрее другого. Отбрасывание таких различий может иметь последствия. Например, даже если расхождение во времени (в процентах) приближается к 0, я готов поспорить, что алгоритм с отрицанием все еще использует вдвое больше памяти.
ошибка
@bug Может, но не в этом случае. Я добавил несколько моментов в свой ответ. Числа показывают, что для больших массивов эти подходы имеют схожие временные характеристики, что подтверждает гипотезу о том, что argsort является доминирующим. Для отрицания я бы предположил, что вы правы в отношении использования памяти, но пользователи все равно могут предпочесть это, если им небезразлично положение нанов и / или нужна стабильная сортировка.
Вим
6
Вы можете использовать команды flip numpy.flipud()или numpy.fliplr()получить индексы в порядке убывания после сортировки с помощью argsortкоманды. Это то, что я обычно делаю.
Вместо использования np.argsortвы можете использовать np.argpartition- если вам нужны только индексы самых низких / самых высоких n элементов.
Это не требует сортировки всего массива, а только части, которая вам нужна, но обратите внимание, что «порядок внутри вашего раздела» не определен, поэтому, хотя он дает правильные индексы, они могут быть не правильно упорядочены:
>>> avgDists =[1,8,6,9,4]>>> np.array(avgDists).argpartition(2)[:2]# indices of lowest 2 items
array([0,4], dtype=int64)>>> np.array(avgDists).argpartition(-2)[-2:]# indices of highest 2 items
array([1,3], dtype=int64)
Или, если вы используете два вместе, то есть argsort и argpartition, операция должна быть выполнена над операцией argpartition.
Демонголем
3
Вы можете создать копию массива, а затем умножить каждый элемент на -1.
Как результат, ранее самые большие элементы стали бы самыми маленькими.
Индексы n самых маленьких элементов в копии - это n самых больших элементов в оригинале.
Другой способ - использовать только аргумент «-» для аргумента argsort, например: «df [np.argsort (-df [:, 0])]», при условии, что df является фреймом данных, и вы хотите отсортировать его по первому столбец (представлен номером столбца '0'). Измените имя столбца соответствующим образом. Конечно, столбец должен быть числовым.
ids = np.array(avgDists).argsort()[-n:]
?[3, 1, 2]
. Ваша строка производит[2, 1, 3]
(если n == 3 в качестве примера)ids = np.array(avgDists).argsort()[-n:][::-1]
. Дело в том, чтобы избежать копирования всего списка, который вы получите, добавив-
перед ним. Не относится к маленькому примеру ОП, может быть для больших случаев.np.array(avgDists).argsort()[::-1][:n]
он сделает это. Кроме того, если вы собираетесь использовать NumPy, оставайтесь в NUMPY. Сначала конвертируем список в массив:avgDist=np.array(avgDists)
потом он становитсяavgDist.argsort()[::-1][:n}
Ответы:
Если вы отрицаете массив, самые низкие элементы становятся самыми высокими элементами и наоборот. Поэтому индексами
n
высших элементов являются:Другой способ рассуждать об этом, как упомянуто в комментариях , состоит в том , чтобы наблюдать, что большие элементы идут последними в argsort. Итак, вы можете прочитать из хвоста argsort, чтобы найти самые
n
высокие элементы:Оба метода имеют O (n log n) во временной сложности, потому что
argsort
здесь преобладает вызов. Но у второго подхода есть приятное преимущество: он заменяет отрицание O (n) массива на срез O (1) . Если вы работаете с маленькими массивами внутри циклов, вы можете получить некоторое повышение производительности, избегая этого отрицания, а если вы работаете с огромными массивами, то вы можете сэкономить на использовании памяти, поскольку отрицание создает копию всего массива.Обратите внимание, что эти методы не всегда дают эквивалентные результаты: если запрашивается стабильная реализация сортировки
argsort
, например, путем передачи аргумента ключевого словаkind='mergesort'
, то первая стратегия сохранит стабильность сортировки, но вторая стратегия нарушит стабильность (т. Е. Позиции равных элементы будут перевернуты).Пример времени:
Используя небольшой массив из 100 поплавков и хвост длиной 30, метод просмотра был примерно на 15% быстрее
Для больших массивов, argsort является доминирующим и нет значительной разницы во времени
Обратите внимание, что комментарий от Недима ниже является неправильным. Обрезание до или после реверса не влияет на эффективность, так как обе эти операции только по-разному оценивают массив и не копируют данные.
источник
np.array(avgDists).argsort()[:-n][::-1]
Точно так же, как и Python, он
[::-1]
переворачивает массив, возвращаемыйargsort()
и[:n]
дает последние n элементов:Преимущество этого метода заключается в том, что
ids
это представление avgDists:(«OWNDATA» в значении False указывает, что это представление, а не копия)
Еще один способ сделать это что-то вроде:
Проблема состоит в том, что способ, которым это работает, состоит в том, чтобы создать отрицание каждого элемента в массиве:
ANd создает копию для этого:
Так что, если вы рассчитываете каждый с этим очень маленьким набором данных:
Метод просмотра значительно быстрее (и использует 1/2 памяти ...)
источник
Вы можете использовать команды flip
numpy.flipud()
илиnumpy.fliplr()
получить индексы в порядке убывания после сортировки с помощьюargsort
команды. Это то, что я обычно делаю.источник
Вместо использования
np.argsort
вы можете использоватьnp.argpartition
- если вам нужны только индексы самых низких / самых высоких n элементов.Это не требует сортировки всего массива, а только части, которая вам нужна, но обратите внимание, что «порядок внутри вашего раздела» не определен, поэтому, хотя он дает правильные индексы, они могут быть не правильно упорядочены:
источник
Вы можете создать копию массива, а затем умножить каждый элемент на -1.
Как результат, ранее самые большие элементы стали бы самыми маленькими.
Индексы n самых маленьких элементов в копии - это n самых больших элементов в оригинале.
источник
-array
С вашим примером:
Получить индексы n максимальных значений:
Сортировать их в порядке убывания:
Получить результаты (для n = 4):
источник
Как намекнул @Kanmani, можно использовать более простую интерпретацию
numpy.flip
, как показано ниже:Используя шаблон посетителя, а не функции-члены, легче читать порядок операций.
источник
Другой способ - использовать только аргумент «-» для аргумента argsort, например: «df [np.argsort (-df [:, 0])]», при условии, что df является фреймом данных, и вы хотите отсортировать его по первому столбец (представлен номером столбца '0'). Измените имя столбца соответствующим образом. Конечно, столбец должен быть числовым.
источник