NumPy dizisindeki N maksimum değerlerin indekslerini nasıl alabilirim?


482

NumPy, bir dizinin maksimum değerinin dizinini yoluyla almanın bir yolunu önerir np.argmax.

Benzer bir şey istiyorum, ancak Nmaksimum değerlerin dizinlerini döndürme .

Bir dizi varsa Örneğin, [1, 3, 2, 4, 5], function(array, n=3)endeksleri dönmek [4, 3, 1]elemanlara karşılık gelir [5, 4, 3].



4
Sorunuz gerçekten iyi tanımlanmamış. Örneğin, endekslerin ne olmasını bekliyorsunuz array([5, 1, 5, 5, 2, 3, 2, 4, 1, 5]), whit n= 3? Tüm hangisi alternatifleri gibi [0, 2, 3], [0, 2, 9], ...doğru bir olurdu? Lütfen özel gereksinimleriniz hakkında daha fazla bilgi edinin. Teşekkürler
yemek

@eat, bu özel durumda hangisinin iade edileceği umurumda değil. Karşılaşılan ilkini döndürmek mantıklı görünse bile, bu benim için bir gereklilik değil.
Alexis Métaireau

argsortİade edilen kırbaçların sırasını umursamıyorsanız uygun bir alternatif olabilir. Cevabımı aşağıda görebilirsiniz.
mavi

Yanıtlar:


347

Gelebildiğim en basit şey:

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

Bu, dizinin tam bir türünü içerir. numpyKısmi bir tür yapmak için yerleşik bir yol sağlayıp sağlamadığını merak ediyorum ; şu ana kadar bir tane bulamadım.

Bu çözüm çok yavaş çıkıyorsa (özellikle küçük olanlar için n), Cython'da bir şey kodlamaya bakmaya değer olabilir .


1
3. satır aynı şekilde yazılabilir arr.argsort()[-1:-4:-1]mi? Tercümanda denedim ve aynı sonuç ortaya çıkıyor, ancak bazı örneklerle kırılmadığını merak ediyorum.
abroekhof

44
@abroekhof Evet, herhangi bir liste veya dizi için eşdeğer olmalıdır. Alternatif olarak, bu np.argsort(-arr)[:3]daha okunabilir bulduğum ve noktaya kadar kullanarak geri dönüş olmadan yapılabilir .
askewchan

6
[:: - 1] ne anlama geliyor? @NPE
1a1a11a

@ 1a1a11a, bir diziyi tersine çevirmek anlamına gelir (kelimenin tam anlamıyla, bir dizinin kopyasını sınırsız dakikadan sınırlandırılmamış maks.
Değerine

15
arr.argsort()[::-1][:n]daha iyi çünkü n=0tam dizi yerine boş döner
abora

599

Yeni NumPy sürümleri (1.8 ve üstü) bunun için bir işleve sahiptir argpartition. En büyük dört elementin endekslerini almak için

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

Bunun aksine argsort, bu işlev en kötü durumda doğrusal zamanda çalışır, ancak değerlendirmenin sonucundan da görülebileceği gibi, döndürülen endeksler sıralanmaz a[ind]. Buna ihtiyacınız varsa, daha sonra bunları sıralayın:

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

Üstteki almak için k bu şekilde sıralanmış sırayla elemanları O (alır n + k log k ) zamanı.


27
@varela introselect algoritmasını argpartitionkullanarak doğrusal zamanda (O (n)) çalışır . Sonraki sıralama yalnızca k öğelerini işler, böylece O (k log k) içinde çalışır.
Fred Foo

2
Birisi tam olarak nasıl çalıştığını merak ediyorsa np.argpartitionve kardeş algoritması np.partitionbağlantılı soruda daha ayrıntılı bir açıklama var: stackoverflow.com/questions/10337533/…
Ramon Martinez

7
@FredFoo: neden -4 kullandınız? Geriye başlamak için bunu yaptın mı? (k pozitif veya negatif olmak benim için aynı şekilde çalışır! önce sadece en küçük sayıları yazdırır!)
Rika

2
@LKT, a=np.array([9, 4, 4, 3, 3, 9, 0, 4, 6, 0])normal python listelerinin aksine listeler tarafından endekslemeyi desteklememesi nedeniyle kullanılırnp.array
Marawan Okasha

2
@Umangsinghal np.argpartitionisteğe bağlı bir axisargüman alır . Her satır için en üstteki n değerlerinin indekslerini bulmak için:np.argpartition(a, -n, axis=1)[-n:]
Ralph

48

Daha basit:

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

burada n , maksimum değerlerin sayısıdır.


7
Bu bir 2d dizisi için yapılabilir mi? Değilse, belki nasıl olduğunu biliyor musunuz?
Andrew Hundt

2
@AndrewHundt: sadece (-arr) .argsort (eksen = -1) kullanın [:,: n]
MiniQuark

2
arr[arr.argsort()[-n:]]diziyi reddetmek yerine benzer olacaktır , sadece son n öğenin bir dilimini alın
loganjones16

35

kullanın:

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

Normal Python listeleri için:

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

Python 2 kullanıyorsanız, xrangeyerine kullanın range.

Kaynak: heapq - Yığın kuyruğu algoritması


2
Hepsi burada bir döngünün gerek yoktur: heapq.nlargest(3, xrange(len(a)), a.take). Python listeleri için .__getitem__bunun yerine kullanabiliriz .take.
Ashwini Chaudhary

N-boyutlu diziler için Agenel olarak: heapq.nlargest(3, range(len(A.ravel())), A.ravel().take). (Umarım bu yalnızca görünümler üzerinde çalışır, ayrıca bkz . ravel vs flatten( Stackoverflow.com/a/28930580/603003 ))
ComFreek

31

Çok boyutlu bir dizi ile çalışıyorsanız, endeksleri düzleştirmeniz ve çözmeniz gerekir:

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)

Örneğin:

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

Kullanabileceğiniz K-th en büyük öğelerinin sırasını umursamıyorsanız argpartition, tam bir sıralamadan daha iyi performans göstermelidir 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])

Kredi bu soruya gider .

Birkaç test yaptım ve dizinin boyutu ve K değeri arttıkça argpartitiondaha iyi görünüyor argsort.


7

Çok boyutlu dizilerde axis, bölümlemeyi beklenen eksen boyunca uygulamak için anahtar sözcüğü kullanabilirsiniz .

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

Ve öğeleri kapmak için:

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

Ancak bunun sıralı bir sonuç döndürmeyeceğini unutmayın. Bu durumda np.argsort(), amaçlanan eksen boyunca kullanabilirsiniz :

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

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

İşte bir örnek:

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

Sanırım burada endekslemeyi basitleştirebilirsiniz np.take_along_axis(bu soruyu cevapladığınızda muhtemelen yoktu)
Eric

4

Bu, orijinal dizinizin boyutuna ve seçiminizin boyutuna bağlı olarak tam bir sıralamadan daha hızlı olacaktır:

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

Tabii ki, orijinal dizinizle oynanmayı içerir. Bir kopya oluşturarak veya orijinal değerleri değiştirerek (gerekirse) düzeltebileceğiniz. ... kullanım durumunuz için hangisi daha ucuzsa.


FWIW, çözümünüz her durumda kesin çözüm sunmayacaktır. OP bu açık vakaların nasıl ele alınacağını açıklamalıdır. Teşekkürler
yemek

@eat OP'nin sorusu biraz belirsiz. Ancak bir uygulama, yoruma gerçekten açık değildir. :) OP sadece bu özel çözümün gereksinimleri karşıladığından emin olmak için np.argmax docs.scipy.org/doc/numpy/reference/generated/numpy.argmax.html tanımına başvurmalıdır . OP'nin belirtilen gereksinimlerini karşılayan herhangi bir çözüm kabul edilebilir.
Paul

Birisi de uygulamanın argmax(.)açık olduğu düşünülebilir . (IMHO bir tür kısa devre mantığını takip etmeye çalışıyor, ancak maalesef evrensel olarak kabul edilebilir davranış sağlayamıyor). Teşekkürler
yemek

3

Yöntem np.argpartitionyalnızca k en büyük indeksleri döndürür, yerel bir sıralama gerçekleştirir ve np.argsortdizi oldukça büyük olduğunda (tam sıralama gerçekleştirme) daha hızlıdır . Ancak döndürülen endeksler artan / azalan sırada DEĞİLDİR . Bir örnekle diyelim:

Resim açıklamasını buraya girin

Sıkı bir artan sipariş üst k endeksleri istiyorsanız, np.argpartition istiyorsanız, istediğinizi geri döndürmeyeceğinizi görebiliriz.

Np.argpartition'dan sonra elle sıralama yapmanın dışında çözümüm PyTorch'u kullanmak, torch.topk sinir ağı yapımı için bir araç olan hem CPU hem de GPU desteği ile NumPy benzeri API'ler sağlamak. MKL ile NumPy kadar hızlıdır ve büyük matris / vektör hesaplamalarına ihtiyacınız varsa GPU desteği sunar.

Sıkı yükseliş / iniş üst k endeksleri kodu:

Resim açıklamasını buraya girin

torch.topkBir torç tensörünü kabul ettiğine ve hem üst k değerlerini hem de üst k indekslerini tür olarak döndürdüğünü unutmayın torch.Tensor. Np ile benzer şekilde, torch.topk da çok boyutlu dizileri / tensörleri işleyebilmeniz için bir eksen bağımsız değişkenini kabul eder.


2

kullanın:

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

Şimdi resultliste büyütülmüş olan N tuples ( index, value) içerecektir value.


2

kullanın:

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

Ayrıca 2D dizilerle de çalışır. Örneğin,

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

İyi çalışır, ancak A dizinizde yinelenen (maksimum) değerler varsa daha fazla sonuç verir. Tam olarak k sonuçları beklerim, ancak yinelenen değerler durumunda, k'den fazla sonuç alırsınız.
Guido

Kodu biraz değiştirdim. Döndürülen endekslerin listesi tam olarak k'ye eşittir. Kopyalarınız varsa, bunlar tek bir grupta gruplanır.
X Æ A-12

1

bottleneck yalnızca en büyük N değerini almak için tüm diziyi sıralama masrafı çok büyükse kısmi bir sıralama işlevi vardır.

Bu modül hakkında hiçbir şey bilmiyorum; Ben sadece googled numpy partial sort.


Darboğazda kısmi sıralama işlevi bulamıyorum, bir bölüm işlevi var, ancak bu sıralama yok
nbecker

1

Aşağıda, maksimum elemanları ve konumlarını görmenin çok kolay bir yoludur. İşte axisetki alanı; axis= 0 sütun bazında maksimum sayı ve axis= 1 satır bazında maksimum sayı anlamına gelir. Ve daha yüksek boyutlar için bu size bağlıdır.

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


0

Kullanmayı en sezgisel buldum np.unique.

Fikir, benzersiz yöntemin girdi değerlerinin indekslerini döndürmesidir. Ardından maksimum benzersiz değer ve göstergelerden orijinal değerlerin konumu yeniden oluşturulabilir.

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

Ben en zaman verimlilik yolu dizi aracılığıyla manuel olarak yinelenen ve diğer insanların belirttiği gibi, bir k boyutu min-yığın tutmak olduğunu düşünüyorum.

Ayrıca kaba kuvvet yaklaşımı da ortaya koyuyorum:

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

Dizini almak için argmax kullandıktan sonra en büyük öğeyi büyük bir negatif değere ayarlayın. Ve sonra argmax'ın bir sonraki çağrısı ikinci en büyük unsuru döndürecektir. Ve bu öğelerin orijinal değerini kaydedebilir ve isterseniz onları kurtarabilirsiniz.


0

Bu kod bir numpy matris dizisi için çalışır:

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 

Bu, bir matris dizisinden n_largest öğelerini ayıklamak için de çalışan true-false n_largest matris dizinini üretir

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.