Bagaimana cara saya mendapatkan indeks nilai maksimum N dalam array NumPy?

485

NumPy mengusulkan cara untuk mendapatkan indeks dari nilai maksimum sebuah array via np.argmax.

Saya ingin hal serupa, tetapi mengembalikan indeks dari nilai Nmaksimum.

Misalnya, jika saya memiliki array [1, 3, 2, 4, 5],, function(array, n=3)akan mengembalikan indeks [4, 3, 1]yang sesuai dengan elemen [5, 4, 3].

Alexis Métaireau
sumber
4
Pertanyaan Anda tidak didefinisikan dengan sangat baik. Misalnya, untuk apa indeks (yang Anda harapkan) untuk array([5, 1, 5, 5, 2, 3, 2, 4, 1, 5]), sedikit pun n= 3? Yang mana semua alternatif, seperti [0, 2, 3], [0, 2, 9], ...akan menjadi salah satu yang benar? Silakan uraikan lebih lanjut tentang persyaratan spesifik Anda. Terima kasih
makan
@ makan, saya tidak terlalu peduli yang mana yang seharusnya dikembalikan dalam kasus khusus ini. Bahkan jika tampaknya logis untuk mengembalikan yang pertama kali ditemui, itu bukan keharusan bagi saya.
Alexis Métaireau
argsortmungkin menjadi alternatif yang layak jika Anda tidak peduli dengan urutan indeces yang dikembalikan. Lihat jawaban saya di bawah ini.
biru

Jawaban:

348

Yang paling sederhana yang bisa saya lakukan adalah:

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

Ini melibatkan semacam array lengkap. Saya ingin tahu apakah numpymenyediakan cara bawaan untuk melakukan semacam parsial; sejauh ini saya belum dapat menemukannya.

Jika solusi ini ternyata terlalu lambat (terutama untuk yang kecil n), mungkin perlu melihat pengkodean sesuatu di Cython .

NPE
sumber
1
Bisakah baris 3 ditulis secara setara arr.argsort()[-1:-4:-1]? Saya sudah mencobanya dalam penerjemah dan muncul dengan hasil yang sama, tapi saya bertanya-tanya apakah itu tidak rusak oleh beberapa contoh.
abroekhof
44
@abroekhof Ya itu harus sama dengan daftar atau larik apa pun. Atau, ini bisa dilakukan tanpa pembalikan dengan menggunakan np.argsort(-arr)[:3], yang menurut saya lebih mudah dibaca dan to the point.
askewchan
6
apa artinya [:: - 1]? @NPE
1a1a11a
@ 1a1a11a artinya membalikkan array (secara harfiah, mengambil salinan array dari min yang tidak dibatasi ke maks yang tidak dibatasi dalam urutan terbalik)
FizBack
15
arr.argsort()[::-1][:n]lebih baik karena mengembalikan kosong untuk n=0bukan array penuh
abora
600

Versi NumPy yang lebih baru (1,8 dan lebih tinggi) memiliki fungsi yang disebut argpartitionuntuk ini. Untuk mendapatkan indeks dari empat elemen terbesar, lakukan

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

Tidak seperti argsort, fungsi ini berjalan dalam waktu linier dalam kasus terburuk, tetapi indeks yang dikembalikan tidak diurutkan, seperti yang dapat dilihat dari hasil evaluasi a[ind]. Jika Anda juga membutuhkannya, urutkan setelahnya:

>>> ind[np.argsort(a[ind])]
array([1, 8, 5, 0])

Untuk mendapatkan elemen topk dalam urutan diurutkan dengan cara ini membutuhkan waktu O ( n + k log k ).

Fred Foo
sumber
27
@varela argpartitionberjalan dalam waktu linier, O (n), menggunakan algoritma introselect . Urutan berikutnya hanya menangani elemen k, sehingga berjalan di O (k log k).
Fred Foo
2
Jika ada yang bertanya-tanya bagaimana tepatnya np.argpartitiondan algoritma saudara np.partitionbekerja, ada penjelasan yang lebih rinci dalam pertanyaan terkait: stackoverflow.com/questions/10337533/...
Ramon Martinez
7
@FredFoo: mengapa Anda menggunakan -4? apakah Anda melakukan itu untuk memulai mundur (karena k menjadi positif atau negatif bekerja sama untuk saya! hanya mencetak angka terkecil dulu!
Rika
2
@LKT digunakan a=np.array([9, 4, 4, 3, 3, 9, 0, 4, 6, 0])karena daftar python normal tidak mendukung pengindeksan berdasarkan daftar, tidak sepertinp.array
Marawan Okasha
2
@Umangsinghal np.argpartitionmengambil axisargumen opsional . Untuk menemukan indeks nilai n teratas untuk setiap baris:np.argpartition(a, -n, axis=1)[-n:]
Ralph
48

Lebih sederhana:

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

di mana n adalah jumlah nilai maksimum.

Ketan
sumber
7
Bisakah ini dilakukan untuk array 2d? Jika tidak, mungkin Anda tahu caranya?
Andrew Hundt
2
@AndrewHundt: cukup gunakan (-arr) .argsort (sumbu = -1) [:,: n]
MiniQuark
2
serupa akan arr[arr.argsort()[-n:]]bukannya meniadakan array, hanya mengambil sepotong elemen n terakhir
loganjones16
35

Menggunakan:

>>> import heapq
>>> import numpy
>>> a = numpy.array([1, 3, 2, 4, 5])
>>> heapq.nlargest(3, range(len(a)), a.take)
[4, 3, 1]

Untuk daftar Python biasa:

>>> a = [1, 3, 2, 4, 5]
>>> heapq.nlargest(3, range(len(a)), a.__getitem__)
[4, 3, 1]

Jika Anda menggunakan Python 2, gunakan xrangesebagai ganti range.

Sumber: heapq - Heap queue algorithm

anishpatel
sumber
2
Tidak perlu loop sama sekali di sini: heapq.nlargest(3, xrange(len(a)), a.take). Untuk daftar Python, kita bisa menggunakan .__getitem__bukan .take.
Ashwini Chaudhary
Untuk array n-dimensi Apada umumnya: heapq.nlargest(3, range(len(A.ravel())), A.ravel().take). (Saya harap ini hanya beroperasi pada tampilan, lihat juga ( ravel vs flatten] ( stackoverflow.com/a/28930580/603003 ))
ComFreek
31

Jika Anda kebetulan menggunakan array multidimensi maka Anda harus meratakan dan mengurai indeks:

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)

Sebagai contoh:

>>> 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])
danvk
sumber
9

Jika Anda tidak peduli dengan urutan elemen terbesar K-th yang dapat Anda gunakan argpartition, yang seharusnya berkinerja lebih baik daripada memilah secara lengkap 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])

Kredit pergi ke pertanyaan ini .

Saya menjalankan beberapa tes dan sepertinya argpartitionmengungguli argsortukuran array dan nilai K meningkat.

biru
sumber
7

Untuk array multidimensi Anda dapat menggunakan axiskata kunci untuk menerapkan partisi di sepanjang sumbu yang diharapkan.

# For a 2D array
indices = np.argpartition(arr, -N, axis=1)[:, -N:]

Dan untuk mengambil item:

x = arr.shape[0]
arr[np.repeat(np.arange(x), N), indices.ravel()].reshape(x, N)

Tetapi perhatikan bahwa ini tidak akan mengembalikan hasil yang diurutkan. Dalam hal ini Anda dapat menggunakan np.argsort()sepanjang sumbu yang dimaksud:

indices = np.argsort(arr, axis=1)[:, -N:]

# Result
x = arr.shape[0]
arr[np.repeat(np.arange(x), N), indices.ravel()].reshape(x, N)

Berikut ini sebuah contoh:

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]])
Kasramvd
sumber
Saya pikir Anda dapat menyederhanakan pengindeksan di sini dengan menggunakan np.take_along_axis(yang kemungkinan tidak ada ketika Anda menjawab pertanyaan ini)
Eric
4

Ini akan lebih cepat daripada penyortiran penuh tergantung pada ukuran array asli Anda dan ukuran pilihan Anda:

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

Ini, tentu saja, melibatkan pengubahan array asli Anda. Yang dapat Anda perbaiki (jika perlu) dengan membuat salinan atau mengganti kembali nilai-nilai aslinya. ... mana yang lebih murah untuk kasus penggunaan Anda.

Paul
sumber
FWIW, solusi Anda tidak akan memberikan solusi jelas dalam semua situasi. OP harus menjelaskan cara menangani kasus-kasus yang tidak ambigu ini. Terima kasih
makan
@ makan Pertanyaan OP agak ambigu. Namun implementasi tidak sepenuhnya terbuka untuk interpretasi. :) OP harus merujuk pada definisi np.argmax docs.scipy.org/doc/numpy/reference/generated/numpy.argmax.html untuk memastikan solusi spesifik ini memenuhi persyaratan. Mungkin saja solusi apa pun yang memenuhi persyaratan yang dinyatakan OP dapat diterima ..
Paul
Yah, orang mungkin menganggap implementasi argmax(.)tidak ambigu juga. (IMHO ia mencoba mengikuti semacam logika hubungan pendek, tetapi sayangnya gagal memberikan perilaku yang dapat diterima secara universal). Terima kasih
makan
3

Metode np.argpartitionhanya mengembalikan indeks terbesar k, melakukan pengurutan lokal, dan lebih cepat daripada np.argsort(melakukan pengurutan penuh) ketika array cukup besar. Tetapi indeks yang dikembalikan TIDAK dalam urutan naik / turun . Katakanlah dengan sebuah contoh:

Masukkan deskripsi gambar di sini

Kita dapat melihat bahwa jika Anda menginginkan indeks top k pesanan naik yang ketat, np.argpartitiontidak akan mengembalikan apa yang Anda inginkan.

Selain melakukan pengurutan secara manual setelah np.argpartition, solusi saya adalah menggunakan PyTorch,, torch.topkalat untuk konstruksi jaringan saraf, menyediakan API mirip NumPy dengan dukungan CPU dan GPU. Ini secepat NumPy dengan MKL, dan menawarkan peningkatan GPU jika Anda membutuhkan perhitungan matriks / vektor yang besar.

Kode indeks kenaikan ascend / descend ketat adalah:

Masukkan deskripsi gambar di sini

Perhatikan bahwa torch.topkmenerima tensor obor, dan mengembalikan kedua nilai k atas dan indeks k atas dalam jenis torch.Tensor. Mirip dengan np, torch.topk juga menerima argumen sumbu sehingga Anda dapat menangani array / tensor multi-dimensi.

masa depan
sumber
2

Menggunakan:

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

Sekarang resultdaftar akan berisi N tupel ( index, value) di mana valuedimaksimalkan.

off99555
sumber
2

Menggunakan:

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

Ini juga bekerja dengan array 2D. Sebagai contoh,

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])
X Æ A-12
sumber
Berfungsi baik, tetapi memberikan hasil lebih banyak jika Anda memiliki nilai duplikat (maksimum) dalam array Anda A. Saya akan mengharapkan hasil k persis tetapi dalam kasus nilai duplikat, Anda mendapatkan lebih dari hasil k.
Guido
Saya sedikit mengubah kodenya. Daftar indeks yang dikembalikan memiliki panjang yang sama persis dengan k. Jika Anda memiliki duplikat, mereka dikelompokkan menjadi satu tupel.
X Æ A-12
1

bottleneck memiliki fungsi sortir parsial, jika biaya memilah seluruh array hanya untuk mendapatkan nilai N terbesar terlalu besar.

Saya tidak tahu apa-apa tentang modul ini; Saya baru saja googled numpy partial sort.

Katriel
sumber
Saya tidak menemukan fungsi pengurutan parsial di bottleneck, ada fungsi partisi, tapi ini tidak mengurutkan
nbecker
1

Berikut ini adalah cara yang sangat mudah untuk melihat elemen maksimum dan posisinya. Ini axisdomainnya; axis= 0 berarti jumlah maksimum bijaksana kolom dan axis= 1 berarti jumlah maksimum baris bijaksana untuk kasus 2D. Dan untuk dimensi yang lebih tinggi itu tergantung pada Anda.

M = np.random.random((3, 4))
print(M)
print(M.max(axis=1), M.argmax(axis=1))
liberal
sumber
Saya menggunakan tautan ini jakevdp.github.io/PythonDataScienceHandbook/…
liberal
0

Saya menemukan ini paling intuitif untuk digunakan np.unique.

Idenya adalah, bahwa metode unik mengembalikan indeks nilai input. Kemudian dari nilai unik maks dan indeks, posisi nilai asli dapat dibuat kembali.

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]
phi
sumber
0

Saya pikir cara efisiensi waktu yang paling banyak adalah secara manual beralih melalui array dan menyimpan min-heap k-size, seperti yang orang lain katakan.

Dan saya juga datang dengan pendekatan brute force:

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

Atur elemen terbesar ke nilai negatif besar setelah Anda menggunakan argmax untuk mendapatkan indeksnya. Dan kemudian panggilan argmax selanjutnya akan mengembalikan elemen terbesar kedua. Dan Anda dapat mencatat nilai asli dari elemen-elemen ini dan memulihkannya jika Anda mau.

Zhenghao Zhao
sumber
0

Kode ini berfungsi untuk array matriks numpy:

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 

Ini menghasilkan pengindeksan matriks n_largest true-false yang juga berfungsi untuk mengekstrak elemen n_largest dari array matriks

Yi Xiang Chong
sumber