Làm cách nào để có được các chỉ số của N giá trị tối đa trong một mảng NumPy?


482

NumPy đề xuất một cách để lấy chỉ số của giá trị tối đa của một mảng thông qua np.argmax.

Tôi muốn một điều tương tự, nhưng trả về các chỉ mục của các Ngiá trị tối đa.

Ví dụ, nếu tôi có một mảng [1, 3, 2, 4, 5], function(array, n=3)sẽ trả về các chỉ số [4, 3, 1]tương ứng với các phần tử [5, 4, 3].



4
Câu hỏi của bạn không thực sự được xác định rõ. Ví dụ, các chỉ số (bạn mong đợi) sẽ là gì array([5, 1, 5, 5, 2, 3, 2, 4, 1, 5]), whit n= 3? Mà một trong tất cả các lựa chọn thay thế, như [0, 2, 3], [0, 2, 9], ...sẽ là đúng? Xin hãy giải thích thêm về các yêu cầu cụ thể của bạn. Cảm ơn
ăn

@eat, tôi không thực sự quan tâm đến cái nào được cho là sẽ được trả lại trong trường hợp cụ thể này. Ngay cả khi việc trả lại cái đầu tiên gặp phải có vẻ hợp lý, đó không phải là một yêu cầu đối với tôi.
Alexis Métaireau

argsortcó thể là một sự thay thế khả thi nếu bạn không quan tâm đến thứ tự của phân được trả lại. Xem câu trả lời của tôi dưới đây.
màu xanh

Câu trả lời:


347

Đơn giản nhất tôi có thể nghĩ ra là:

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

Điều này liên quan đến một loại hoàn chỉnh của mảng. Tôi tự hỏi nếu numpycung cấp một cách tích hợp để thực hiện một phần sắp xếp; Cho đến nay tôi đã không thể tìm thấy một.

Nếu giải pháp này hóa ra quá chậm (đặc biệt là đối với nhỏ n), có thể đáng để xem xét mã hóa thứ gì đó trong Cython .


1
Dòng 3 có thể được viết tương đương như arr.argsort()[-1:-4:-1]? Tôi đã thử nó trong trình thông dịch và nó cũng cho kết quả tương tự, nhưng tôi tự hỏi liệu nó không bị phá vỡ bởi một số ví dụ.
abroekhof

44
@abroekhof Có, nó phải tương đương với bất kỳ danh sách hoặc mảng nào. Ngoài ra, điều này có thể được thực hiện mà không cần đảo ngược bằng cách sử dụng np.argsort(-arr)[:3], mà tôi thấy dễ đọc hơn và đến điểm.
askewchan

6
[:: - 1] có nghĩa là gì? @NPE
1a1a11a

@ 1a1a11a có nghĩa là đảo ngược một mảng (theo nghĩa đen, lấy một bản sao của một mảng từ tối thiểu không bị giới hạn thành tối đa không bị ràng buộc theo thứ tự đảo ngược)
FizBack

15
arr.argsort()[::-1][:n]tốt hơn bởi vì nó trả về trống n=0thay vì toàn bộ mảng
abora

599

Các phiên bản NumPy mới hơn (1.8 trở lên) có chức năng được gọi argpartitioncho việc này. Để có được các chỉ số của bốn yếu tố lớn nhất, hãy làm

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

Không giống như argsort, hàm này chạy trong thời gian tuyến tính trong trường hợp xấu nhất, nhưng các chỉ số được trả về không được sắp xếp, như có thể thấy từ kết quả đánh giá a[ind]. Nếu bạn cũng cần điều đó, hãy sắp xếp chúng sau:

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

Để có được các phần tử top- k theo thứ tự được sắp xếp theo cách này phải mất thời gian O ( n + k log k ).


27
@varela argpartitionchạy trong thời gian tuyến tính, O (n), sử dụng thuật toán introselect . Sắp xếp tiếp theo chỉ xử lý các phần tử k, do đó chạy trong O (k log k).
Fred Foo

2
Nếu bất cứ ai đang tự hỏi làm thế nào chính xác np.argpartitionvà thuật toán chị em của nó np.partitionhoạt động, có một lời giải thích chi tiết hơn trong câu hỏi được liên kết: stackoverflow.com/questions/10337533/ Kẻ
Ramon Martinez

7
@FredFoo: tại sao bạn sử dụng -4? Bạn đã làm điều đó để bắt đầu lạc hậu chưa? (vì k là tích cực hay tiêu cực đều giống nhau đối với tôi! nó chỉ in những con số nhỏ nhất trước!
Rika

2
@LKT sử dụng a=np.array([9, 4, 4, 3, 3, 9, 0, 4, 6, 0])vì danh sách trăn bình thường không hỗ trợ lập chỉ mục theo danh sách, không giống nhưnp.array
Marawan Okasha

2
@Umangsinghal np.argpartitioncó một axisđối số tùy chọn . Để tìm các chỉ số của n giá trị hàng đầu cho mỗi hàng:np.argpartition(a, -n, axis=1)[-n:]
Ralph

48

Đơn giản hơn:

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

Trong đó n là số lượng giá trị tối đa.


7
Điều này có thể được thực hiện cho một mảng 2d? Nếu không, có lẽ bạn biết làm thế nào?
Andrew Hundt

2
@AndrewHundt: chỉ cần sử dụng (-arr) .argsort (trục = -1) [:,: n]
MiniQuark

2
tương tự sẽ arr[arr.argsort()[-n:]]thay vì phủ định mảng, chỉ cần lấy một lát của n phần tử cuối cùng
loganjones16

35

Sử dụng:

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

Đối với danh sách Python thông thường:

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

Nếu bạn sử dụng Python 2, hãy sử dụng xrangethay vì range.

Nguồn: heapq - Thuật toán hàng đợi heap


2
Không cần một vòng lặp nào cả ở đây : heapq.nlargest(3, xrange(len(a)), a.take). Đối với danh sách Python chúng ta có thể sử dụng .__getitem__thay vì .take.
Ashwini Chaudhary

Đối với mảng n chiều Anói chung : heapq.nlargest(3, range(len(A.ravel())), A.ravel().take). (Tôi hy vọng điều này chỉ hoạt động trên các lượt xem, xem thêm ( ravel vs flatten] ( stackoverflow.com/a/28930580/603003 )).
ComFalet

31

Nếu bạn tình cờ làm việc với một mảng nhiều chiều thì bạn sẽ cần phải làm phẳng và làm sáng tỏ các chỉ số:

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)

Ví dụ:

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

Nếu bạn không quan tâm đến thứ tự của các phần tử lớn nhất K-th bạn có thể sử dụng argpartition, phần tử này sẽ hoạt động tốt hơn so với sắp xếp đầy đủ 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])

Tín dụng đi đến câu hỏi này .

Tôi đã chạy một vài thử nghiệm và có vẻ như argpartitionvượt trội hơn argsortkhi kích thước của mảng và giá trị của K tăng lên.


7

Đối với các mảng nhiều chiều, bạn có thể sử dụng axistừ khóa để áp dụng phân vùng dọc theo trục dự kiến.

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

Và để lấy các mặt hàng:

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

Nhưng lưu ý rằng điều này sẽ không trả về một kết quả được sắp xếp. Trong trường hợp đó, bạn có thể sử dụng np.argsort()dọc theo trục dự định:

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

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

Đây là một ví dụ:

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

Tôi nghĩ bạn có thể đơn giản hóa việc lập chỉ mục ở đây bằng cách sử dụng np.take_along_axis(có thể không tồn tại khi bạn trả lời câu hỏi này)
Eric

4

Điều này sẽ nhanh hơn một loại đầy đủ tùy thuộc vào kích thước của mảng ban đầu của bạn và kích thước của lựa chọn của bạn:

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

Nó, tất nhiên, liên quan đến việc giả mạo mảng ban đầu của bạn. Mà bạn có thể sửa (nếu cần) bằng cách tạo một bản sao hoặc thay thế lại các giá trị ban đầu. ... Cái nào rẻ hơn cho trường hợp sử dụng của bạn.


FWIW, giải pháp của bạn sẽ không cung cấp giải pháp rõ ràng trong mọi tình huống. OP nên mô tả cách xử lý các trường hợp không rõ ràng này. Cảm ơn
ăn

@eat Câu hỏi của OP hơi mơ hồ. Một triển khai, tuy nhiên, không thực sự mở để giải thích. :) OP chỉ cần tham khảo định nghĩa của np.argmax docs.scipy.org/doc/numpy/reference/generated/numpy.argmax.html để đảm bảo giải pháp cụ thể này đáp ứng các yêu cầu. Có thể mọi giải pháp đáp ứng yêu cầu bồi thường đã nêu của OP đều được chấp nhận ..
Paul

Vâng, người ta có thể coi việc thực hiện argmax(.)là không rõ ràng là tốt. (IMHO nó cố gắng tuân theo một số loại logic ngắn mạch, nhưng không may không cung cấp hành vi được chấp nhận phổ biến). Cảm ơn
ăn

3

Phương thức np.argpartitionchỉ trả về k chỉ số lớn nhất, thực hiện sắp xếp cục bộ và nhanh hơn np.argsort(thực hiện sắp xếp đầy đủ) khi mảng khá lớn. Nhưng các chỉ số được trả về KHÔNG theo thứ tự tăng / giảm dần . Hãy nói với một ví dụ:

Nhập mô tả hình ảnh ở đây

Chúng ta có thể thấy rằng nếu bạn muốn một chỉ số k hàng đầu tăng dần nghiêm ngặt, np.argpartitionsẽ không trả lại những gì bạn muốn.

Ngoài việc thực hiện sắp xếp thủ công sau np.argpartition, giải pháp của tôi là sử dụng PyTorch torch.topk, một công cụ để xây dựng mạng thần kinh, cung cấp API giống như NumPy với cả hỗ trợ CPU và GPU. Nó nhanh như NumPy với MKL và cung cấp khả năng tăng GPU nếu bạn cần các phép tính ma trận / vectơ lớn.

Mã chỉ số k nghiêm ngặt tăng dần / giảm dần sẽ là:

Nhập mô tả hình ảnh ở đây

Lưu ý rằng torch.topkchấp nhận một tenor ngọn đuốc và trả về cả giá trị k hàng đầu và chỉ số k hàng đầu trong loại torch.Tensor. Tương tự với np, Torch.topk cũng chấp nhận một đối số trục để bạn có thể xử lý các mảng / thang đo đa chiều.


2

Sử dụng:

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

Bây giờ resultdanh sách sẽ chứa N tuples ( index, value) valueđược tối đa hóa.


2

Sử dụng:

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

Nó cũng hoạt động với mảng 2D. Ví dụ,

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

Hoạt động tốt, nhưng cho nhiều kết quả hơn nếu bạn có các giá trị trùng lặp (tối đa) trong mảng A. Tôi sẽ mong đợi chính xác k kết quả nhưng trong trường hợp giá trị trùng lặp, bạn sẽ nhận được nhiều hơn k kết quả.
Guido

Tôi hơi sửa đổi mã. Danh sách các chỉ số được trả về có độ dài chính xác bằng k. Nếu bạn có các bản sao, chúng được nhóm thành một bộ duy nhất.
X Æ A-12

1

bottleneck có một hàm sắp xếp một phần, nếu chi phí sắp xếp toàn bộ mảng chỉ để lấy N giá trị lớn nhất là quá lớn.

Tôi không biết gì về mô-đun này; Tôi chỉ googled numpy partial sort.


Tôi thấy không có chức năng sắp xếp một phần nào trong nút cổ chai, có chức năng phân vùng, nhưng điều này không sắp xếp
nbecker

1

Sau đây là một cách rất dễ dàng để xem các yếu tố tối đa và vị trí của nó. Đây axislà tên miền; axis= 0 có nghĩa là số tối đa khôn ngoan của cột và axis= 1 có nghĩa là số tối đa thông minh hàng cho trường hợp 2D. Và đối với kích thước cao hơn, nó phụ thuộc vào bạn.

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

Tôi đã sử dụng liên kết này jakevdp.github.io/PythonDataScienceHandbook/ Lời
tự do

0

Tôi thấy nó trực quan nhất để sử dụng np.unique.

Ý tưởng là phương thức duy nhất trả về các chỉ số của các giá trị đầu vào. Sau đó, từ giá trị duy nhất tối đa và các chỉ báo, vị trí của các giá trị ban đầu có thể được tạo lại.

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

Tôi nghĩ rằng cách hiệu quả nhất về thời gian là lặp lại thủ công thông qua mảng và giữ một heap min-size, như những người khác đã đề cập.

Và tôi cũng đưa ra một cách tiếp cận vũ phu:

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

Đặt phần tử lớn nhất thành giá trị âm lớn sau khi bạn sử dụng argmax để lấy chỉ mục của nó. Và sau đó, cuộc gọi tiếp theo của argmax sẽ trả về phần tử lớn thứ hai. Và bạn có thể đăng nhập giá trị ban đầu của các yếu tố này và phục hồi chúng nếu bạn muốn.


0

Mã này hoạt động cho một mảng ma trận 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 

Điều này tạo ra một chỉ mục ma trận n_largest đúng-sai cũng hoạt động để trích xuất các phần tử n_largest từ một mảng ma trận

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.