Как получить индексы N максимальных значений в массиве numpy?
Numpy предлагает способ получить индекс максимального значения массива через np.argmax
.
Мне нужна аналогичная вещь, но возвращающая индексы максимальных значений N.
Например, если у меня есть массив [1, 3, 2, 4, 5]
, он function(array, n=3)
вернет [4, 3, 1]
.
Спасибо:)
Ответы
Ответ 1
Самое простое, с чем я смог придумать:
In [1]: import numpy as np
In [2]: arr = np.array([1, 3, 2, 4, 5])
In [3]: arr.argsort()[-3:][::-1]
Out[3]: array([4, 3, 1])
Это включает в себя полный вид массива. Интересно, если numpy
предоставляет встроенный способ сделать частичную сортировку; пока я не смог его найти.
Если это решение оказывается слишком медленным (особенно для небольших n
), может быть стоит посмотреть на что-то кодировать в Cython.
Ответ 2
Новые версии NumPy (1,8 и выше) имеют функцию argpartition
. Чтобы получить индексы четырех наибольших элементов, do
>>> a
array([9, 4, 4, 3, 3, 9, 0, 4, 6, 0])
>>> ind = np.argpartition(a, -4)[-4:]
>>> ind
array([1, 5, 8, 0])
>>> a[ind]
array([4, 9, 6, 9])
В отличие от argsort
, эта функция выполняется в линейном времени в худшем случае, но возвращаемые индексы не сортируются, как видно из результата оценки a[ind]
. Если вам это нужно, сортируйте их позже:
>>> ind[np.argsort(a[ind])]
array([1, 8, 5, 0])
Чтобы получить элементы top-k в отсортированном порядке таким образом, берется время O (n + k log k).
Ответ 3
EDIT: Изменено, чтобы включить улучшение Ashwini Chaudhary.
>>> import heapq
>>> import numpy
>>> a = numpy.array([1, 3, 2, 4, 5])
>>> heapq.nlargest(3, range(len(a)), a.take)
[4, 3, 1]
Для регулярных списков Python:
>>> a = [1, 3, 2, 4, 5]
>>> heapq.nlargest(3, range(len(a)), a.__getitem__)
[4, 3, 1]
Если вы используете Python 2, используйте xrange
вместо range
.
Источник: http://docs.python.org/3/library/heapq.html
Ответ 4
Еще проще:
idx = (-arr).argsort()[:n]
где n - число максимальных значений.
Ответ 5
Если вы работаете с многомерным массивом, вам нужно сгладить и разгадать индексы:
def largest_indices(ary, n):
"""Returns the n largest indices from a numpy array."""
flat = ary.flatten()
indices = np.argpartition(flat, -n)[-n:]
indices = indices[np.argsort(-flat[indices])]
return np.unravel_index(indices, ary.shape)
Например:
>>> xs = np.sin(np.arange(9)).reshape((3, 3))
>>> xs
array([[ 0. , 0.84147098, 0.90929743],
[ 0.14112001, -0.7568025 , -0.95892427],
[-0.2794155 , 0.6569866 , 0.98935825]])
>>> largest_indices(xs, 3)
(array([2, 0, 0]), array([2, 2, 1]))
>>> xs[largest_indices(xs, 3)]
array([ 0.98935825, 0.90929743, 0.84147098])
Ответ 6
Если вы не заботитесь о порядке K-ых крупнейших элементов, вы можете использовать argpartition
, который должен выполнять лучше, чем полный сорт через argsort
.
K = 4 # we want the indeces of the four largest values
a = np.array([0, 8, 0, 4, 5, 8, 8, 0, 4, 2])
np.argpartition(a,-K)[-K:]
array([4, 1, 5, 6])
Кредиты на этот вопрос.
Я провел несколько тестов, и он выглядит loke argpartition
превосходит argsort
по мере увеличения размера массива и увеличения значения K.
Ответ 7
Это будет быстрее, чем полный сортировка в зависимости от размера исходного массива и размера вашего выбора:
>>> A = np.random.randint(0,10,10)
>>> A
array([5, 1, 5, 5, 2, 3, 2, 4, 1, 0])
>>> B = np.zeros(3, int)
>>> for i in xrange(3):
... idx = np.argmax(A)
... B[i]=idx; A[idx]=0 #something smaller than A.min()
...
>>> B
array([0, 2, 3])
Это, конечно, связано с изменением исходного массива. Который вы могли бы исправить (если необходимо), сделав копию или заменив исходные значения.... в зависимости от того, что дешевле для вашего случая использования.
Ответ 8
bottleneck
имеет функцию частичной сортировки, если расход сортировки всего массива только для получения наибольших значений N слишком велик.
Я ничего не знаю об этом модуле; Я просто googled numpy partial sort
.
Ответ 9
Для многомерных массивов вы можете использовать ключевое слово axis
, чтобы применить разбиение вдоль ожидаемой оси.
# For a 2D array
indices = np.argpartition(arr, -N, axis=1)[:, -N:]
И для захвата элементов:
x = arr.shape[0]
arr[np.repeat(np.arange(x), N), indices.ravel()].reshape(x, N)
Но обратите внимание, что это не вернет отсортированный результат. В этом случае вы можете использовать np.argsort()
вдоль предполагаемой оси:
indices = np.argsort(arr, axis=1)[:, -N:]
# result
x = arr.shape[0]
arr[np.repeat(np.arange(x), N), indices.ravel()].reshape(x, N)
Вот пример:
In [42]: a = np.random.randint(0, 20, (10, 10))
In [44]: a
Out[44]:
array([[ 7, 11, 12, 0, 2, 3, 4, 10, 6, 10],
[16, 16, 4, 3, 18, 5, 10, 4, 14, 9],
[ 2, 9, 15, 12, 18, 3, 13, 11, 5, 10],
[14, 0, 9, 11, 1, 4, 9, 19, 18, 12],
[ 0, 10, 5, 15, 9, 18, 5, 2, 16, 19],
[14, 19, 3, 11, 13, 11, 13, 11, 1, 14],
[ 7, 15, 18, 6, 5, 13, 1, 7, 9, 19],
[11, 17, 11, 16, 14, 3, 16, 1, 12, 19],
[ 2, 4, 14, 8, 6, 9, 14, 9, 1, 5],
[ 1, 10, 15, 0, 1, 9, 18, 2, 2, 12]])
In [45]: np.argpartition(a, np.argmin(a, axis=0))[:, 1:] # 1 is because the first item is the minimum one.
Out[45]:
array([[4, 5, 6, 8, 0, 7, 9, 1, 2],
[2, 7, 5, 9, 6, 8, 1, 0, 4],
[5, 8, 1, 9, 7, 3, 6, 2, 4],
[4, 5, 2, 6, 3, 9, 0, 8, 7],
[7, 2, 6, 4, 1, 3, 8, 5, 9],
[2, 3, 5, 7, 6, 4, 0, 9, 1],
[4, 3, 0, 7, 8, 5, 1, 2, 9],
[5, 2, 0, 8, 4, 6, 3, 1, 9],
[0, 1, 9, 4, 3, 7, 5, 2, 6],
[0, 4, 7, 8, 5, 1, 9, 2, 6]])
In [46]: np.argpartition(a, np.argmin(a, axis=0))[:, -3:]
Out[46]:
array([[9, 1, 2],
[1, 0, 4],
[6, 2, 4],
[0, 8, 7],
[8, 5, 9],
[0, 9, 1],
[1, 2, 9],
[3, 1, 9],
[5, 2, 6],
[9, 2, 6]])
In [89]: a[np.repeat(np.arange(x), 3), ind.ravel()].reshape(x, 3)
Out[89]:
array([[10, 11, 12],
[16, 16, 18],
[13, 15, 18],
[14, 18, 19],
[16, 18, 19],
[14, 14, 19],
[15, 18, 19],
[16, 17, 19],
[ 9, 14, 14],
[12, 15, 18]])
Ответ 10
from operator import itemgetter
from heapq import nlargest
result = nlargest(N, enumerate(your_list), itemgetter(1))
Теперь список result будет содержать кортежи N (индекс, значение), где значение максимизировано
Ответ 11
def max_indices(arr, k):
'''
Returns the indices of the k first largest elements of arr
(in descending order in values)
'''
assert k <= arr.size, 'k should be smaller or equal to the array size'
arr_ = arr.astype(float) # make a copy of arr
max_idxs = []
for _ in range(k):
max_element = np.max(arr_)
if np.isinf(max_element):
break
else:
idx = np.where(arr_ == max_element)
max_idxs.append(idx)
arr_[idx] = -np.inf
return max_idxs
Работает также с 2D-массивами. Например.
In [0]: A = np.array([[ 0.51845014, 0.72528114],
[ 0.88421561, 0.18798661],
[ 0.89832036, 0.19448609],
[ 0.89832036, 0.19448609]])
In [1]: max_indices(A, 8)
Out[1]:
[(array([2, 3], dtype=int64), array([0, 0], dtype=int64)),
(array([1], dtype=int64), array([0], dtype=int64)),
(array([0], dtype=int64), array([1], dtype=int64)),
(array([0], dtype=int64), array([0], dtype=int64)),
(array([2, 3], dtype=int64), array([1, 1], dtype=int64)),
(array([1], dtype=int64), array([1], dtype=int64))]
In [2]: A[max_indices(A, 8)[0]][0]
Out[2]: array([ 0.89832036])
Ответ 12
Я нашел наиболее интуитивно понятным использование np.unique
.
Идея заключается в том, что уникальный метод возвращает индексы входных значений. Затем из максимального уникального значения и указателей можно восстановить восходящие позиции исходных значений.
multi_max = [1,1,2,2,4,0,0,4]
uniques, idx = np.unique(multi_max, return_inverse=True)
print np.squeeze(np.argwhere(idx == np.argmax(uniques)))
>> [4 7]
Ответ 13
метод np.argpartition
возвращает только k самых больших индексов, выполняет локальную сортировку, быстрее, чем np.argsort
(выполняя полный сортировку), когда массив довольно велик. но возвращаемые индексы НЕ в порядке возрастания/убывания. Скажем, например:
![введите описание изображения здесь]()
мы можем видеть, что если вы хотите строгие индексы верхнего k верхнего индекса, np.argpartition
не вернет то, что вы хотите.
Помимо выполнения сортировки вручную после np.argpartition, моим решением является использование PyTorch, torch.topk
, инструмент для нейронной сети построение, предоставляющее многоподобные API с поддержкой как CPU, так и GPU. Это так же быстро, как numpy с MKL, и предлагает ускорение GPU, если вам нужен большой расчет матрицы/вектора.
Строгий код перехода с возрастанием/спуска вверх k будет:
![введите описание изображения здесь]()
Обратите внимание, что torch.topk
принимает тензор факела и возвращает как верхние k значения, так и верхние k-индексы в типе torch.Tensor
. Подобно np, torch.topk также принимает аргумент оси, так что вы можете обрабатывать многомерный массив/тензор.