Як отримати індекси N максимальних значень у масиві NumPy?


482

NumPy пропонує спосіб отримати індекс максимального значення масиву через np.argmax.

Мені б хотілося подібного, але повертаючи індекси Nмаксимальних значень.

Наприклад, якщо у мене є масив, [1, 3, 2, 4, 5], function(array, n=3)буде повертати індекси , [4, 3, 1]які відповідають елементам [5, 4, 3].



4
Ваше запитання не дуже чітко визначене. Наприклад, що б показники (ви очікуєте) , щоб бути для array([5, 1, 5, 5, 2, 3, 2, 4, 1, 5]), йоти n= 3? Який з усіх альтернатив, як [0, 2, 3], [0, 2, 9], ...буде правильним? Будь ласка, детальніше уточнюйте ваші конкретні вимоги. Спасибі
їмо

@eat, мені зовсім не байдуже, хто з них повинен бути повернутий у цьому конкретному випадку. Навіть якщо здається логічним повернути зустрінене перше, це не є вимогою для мене.
Alexis Métaireau

argsortможе бути життєздатною альтернативою, якщо ви не піклуєтесь про порядок повернених індексів. Дивіться мою відповідь нижче.
синій

Відповіді:


347

Найпростіший, що мені вдалося придумати, це:

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 .


1
Чи може рядок 3 записати так само, як arr.argsort()[-1:-4:-1]? Я спробував це в інтерпретаторі, і він виходить з тим самим результатом, але мені цікаво, чи це не порушено якимсь прикладом.
abroekhof

44
@abroekhof Так, це повинно бути еквівалентно будь-якому списку або масиву. Крім того, це можна зробити без зворотного використання np.argsort(-arr)[:3], використовуючи те , що я вважаю більш зрозумілим і суттєвим.
askewchan

6
що означає [:: - 1]? @NPE
1a1a11a

@ 1a1a11a це означає зворотний масив (буквально, бере копію масиву з необмеженої хв до необмеженої максимуму в зворотному порядку)
FizBack

15
arr.argsort()[::-1][:n]краще, тому що він повертається порожнім для n=0замість повного масиву
abora

599

Новіші версії NumPy (1.8 і вище) мають argpartitionдля цього функцію . Щоб отримати індекси чотирьох найбільших елементів, зробіть

>>> a = np.array([9, 4, 4, 3, 3, 9, 0, 4, 6, 0])
>>> 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 ) час.


27
@varela argpartitionпрацює у лінійному часі, O (n), використовуючи алгоритм інтроселекта . Наступний сортування обробляє лише k елементів, так що працює в O (k log k).
Фред Фоо

2
Якщо когось цікавить, як саме працює np.argpartitionі його сестринський алгоритм, np.partitionє більш детальне пояснення у зв’язаному питанні: stackoverflow.com/questions/10337533/…
Рамон Мартінес

7
@FredFoo: чому ти використовував -4? ви зробили це для того, щоб почати назад? (оскільки k як позитивний чи негативний, так само і для мене! він виводить спочатку лише найменші цифри!
Rika

2
@LKT використовуйте, a=np.array([9, 4, 4, 3, 3, 9, 0, 4, 6, 0])тому що звичайні списки python не підтримують індексацію за списками, на відміну відnp.array
Marawan Okasha

2
@Umangsinghal np.argpartitionбере необов’язковий axisаргумент. Щоб знайти індекси найвищих n значень для кожного ряду:np.argpartition(a, -n, axis=1)[-n:]
Ральф

48

Ще простіше:

idx = (-arr).argsort()[:n]

де n - кількість максимальних значень.


7
Чи можна це зробити для 2d масиву? Якщо ні, то, можливо, ви знаєте, як?
Ендрю Хундт

2
@AndrewHundt: просто скористайся (-arr) .argsort (ось = -1) [:,: n]
MiniQuark

2
подібне було б arr[arr.argsort()[-n:]]замість того, щоб заперечувати масив, просто візьміть фрагмент останніх n елементів
loganjones16

35

Використання:

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

Джерело: heapq - Алгоритм черги купи


2
Там немає необхідності в циклі взагалі тут: heapq.nlargest(3, xrange(len(a)), a.take). Для списків Python ми можемо використовувати .__getitem__замість .take.
Ашвіні Шадхарі

Для п-мірних масивів Aв цілому: heapq.nlargest(3, range(len(A.ravel())), A.ravel().take). (Я сподіваюсь, що це працює лише на видах, див. Також ( ravel vs flatten] ( stackoverflow.com/a/28930580/603003 )).
ComFreek

31

Якщо вам трапляється працювати з багатовимірним масивом, вам знадобиться згладити та розгадати індекси:

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

9

Якщо вам не байдуже порядок K-го найбільшого елемента, який ви можете використовувати argpartition, який повинен працювати краще, ніж повний сорт argsort.

K = 4 # We want the indices 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])

Кредити переходять до цього питання .

Я провів кілька тестів, і це виглядає як argpartitionперевершує те argsort, що розмір масиву та значення K збільшуються.


7

Для багатовимірних масивів ви можете використовувати 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]])

Я думаю, ви можете спростити тут індексацію, скориставшись np.take_along_axis(чого, ймовірно, не було, коли ви відповіли на це запитання)
Ерік

4

Це буде швидше, ніж повний сорт, залежно від розміру вихідного масиву та розміру вашого вибору:

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

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


FWIW, ваше рішення не забезпечить однозначного рішення у всіх ситуаціях. ОП має описати, як поводитися з цими однозначними справами. Спасибі
їмо

@eat Питання ОП дещо неоднозначне. Однак реалізація насправді не піддається інтерпретації. :) ОП має просто посилатися на визначення np.argmax docs.scipy.org/doc/numpy/reference/generated/numpy.argmax.html, щоб переконатися, що це конкретне рішення відповідає вимогам. Можливо, що будь-яке рішення, що відповідає вимогам ОП, є прийнятним ..
Павло

Ну, можна також вважати реалізацію argmax(.)однозначною. (IMHO намагається керуватися якоюсь логікою короткого замикання, але, на жаль, не забезпечує універсально прийнятну поведінку). Спасибі
їмо

3

Метод np.argpartitionповертає лише k найбільші індекси, виконує локальне сортування та швидше, ніж np.argsort(виконання повного сортування), коли масив досить великий. Але повернені індекси НЕ в порядку зростання / зменшення . Скажімо з прикладом:

Введіть тут опис зображення

Ми можемо побачити, що якщо ви хочете суворого порядку індексів висхідного порядку, k np.argpartitionповернення не повернеться.

Крім того, щоб робити сортування вручну після np.argpartition, моє рішення полягає у використанні PyTorch, torch.topkінструменту для побудови нейронної мережі, що забезпечує API, схожі на NumPy, як з підтримкою процесора, так і з GPU. Це так само швидко, як NumPy з MKL, і пропонує збільшення графічного процесора, якщо вам потрібні великі матричні / векторні обчислення.

Суворий код k індексів підйому / спуску буде:

Введіть тут опис зображення

Зверніть увагу, що torch.topkприймає тензор факела і повертає як верхні значення k, так і топ k показники torch.Tensor. Подібно до np, torch.topk також приймає аргумент осі, щоб ви могли обробляти багатовимірні масиви / тензори.


2

Використання:

from operator import itemgetter
from heapq import nlargest
result = nlargest(N, enumerate(your_list), itemgetter(1))

Тепер resultсписок міститиме N кортежів ( index, value), де valueмаксимізовано.


2

Використання:

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

Працює добре, але дає більше результатів, якщо у вашому масиві є дублікати (максимальні) значення. Я очікував би саме k результатів, але у випадку дублювання значень ви отримуєте більше, ніж k результатів.
Гідо

Я трохи змінив код. Список індексів, що повертається, має довжину, рівну точно k. Якщо у вас є дублікати, вони згруповані в один кортеж.
X Æ A-12,

1

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

Я нічого не знаю про цей модуль; Я просто гугла numpy partial sort.


Я не знаходжу часткової функції сортування у вузькому місці, є функція розділення, але це не сортує
nbecker

1

Далі - дуже простий спосіб побачити максимальні елементи та їх положення. Ось axisдомен; axis= 0 означає максимальне максимальне число стовпців і axis= 1 означає максимальне число рядків для 2D випадку. А для вищих розмірів це залежить від вас.

M = np.random.random((3, 4))
print(M)
print(M.max(axis=1), M.argmax(axis=1))

Я використовував це посилання jakevdp.github.io/PythonDataScienceHandbook/…
ліберал

0

Мені це було найінтуїтивніше у використанні 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]

0

Я думаю, що найбільш ефективний спосіб ефективності - це вручну перебирати масив та зберігати міні-купу розміром до k, як говорили інші.

І я також придумав грубу силу підходу:

top_k_index_list = [ ]
for i in range(k):
    top_k_index_list.append(np.argmax(my_array))
    my_array[top_k_index_list[-1]] = -float('inf')

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


0

Цей код працює для матричного матричного масиву:

mat = np.array([[1, 3], [2, 5]]) # numpy matrix

n = 2  # n
n_largest_mat = np.sort(mat, axis=None)[-n:] # n_largest 
tf_n_largest = np.zeros((2,2), dtype=bool) # all false matrix
for x in n_largest_mat: 
  tf_n_largest = (tf_n_largest) | (mat == x) # true-false  

n_largest_elems = mat[tf_n_largest] # true-false indexing 

Це дає справжню помилкову індексацію матриці n_largest, яка також працює для вилучення n_largest елементів з матричного масиву

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.