NumPy配列のN個の最大値のインデックスを取得するにはどうすればよいですか?


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ますか?これは、すべての選択肢のうちの1つ、のような[0, 2, 3][0, 2, 9]...正しいものでしょうか?特定の要件について詳しく説明してください。ありがとう
食べる

@eat、私はこの特定のケースでどちらが返されることになっているのか本当に気にしません。最初に遭遇したものを返すことが論理的に思えても、それは私にとって必要条件ではありません。
AlexisMétaireau2011

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 2012

44
@abroekhofはい、それはどのリストまたは配列でも同等であるべきです。別の方法として、これはを使用して逆転せずに行うこともできますnp.argsort(-arr)[:3]
askewchan 2013年

6
[::-1]はどういう意味ですか?@NPE
1a1a11a

@ 1a1a11aこれは、配列を逆にすることを意味します(文字通り、配列のコピーを制約なしの最小から制約なしの最大に逆の順序で
取ります

15
arr.argsort()[::-1][:n]n=0配列全体ではなく空を返すため、より優れています
abora

599

新しいNumPyバージョン(1.8以上)には、argpartitionこのために呼び出される関数があります。最大の4つの要素のインデックスを取得するには、次のようにします。

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

このようにソートされた順序で上位k個の要素を取得するには、O(n + k log k)時間かかります。


27
@varela argpartitionは、introselectアルゴリズムを使用して線形時間O(n)で実行されます。後続のソートはk個の要素のみを処理するため、O(k log k)で実行されます。
Fred Foo

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アレイに対して実行できますか?そうでない場合は、おそらく方法を知っていますか?
Andrew Hundt 2015

2
@AndrewHundt:単に(-arr).argsort(axis = -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
Ashwini Chaudhary 2014年

一般にn次元配列の場合Aheapq.nlargest(3, range(len(A.ravel())), A.ravel().take)。(これがビューでのみ機能することを願っています。(ravel vs flatten](stackoverflow.com/a/28930580/603003)も参照してください)
ComFreek 2017年

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

クレジットはこの質問に行きます。

いくつかのテストを実行しましたが、配列のサイズとKの値が増加するにつれて、argpartitionパフォーマンスargsortが向上するように見えます。


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(この質問に回答したときには存在しなかった可能性が高い)を使用することで、ここでのインデックス作成を簡略化できると思います
Eric

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、あなたのソリューションは、すべての状況で明確なソリューションを提供することはありません。OPは、これらの明確なケースを処理する方法を説明する必要があります。ありがとう
食べる

@eat OPの質問は少しあいまいです。ただし、実装は解釈に対して実際にオープンではありません。:) OPは単にnp.argmax docs.scipy.org/doc/numpy/reference/generated/numpy.argmax.htmlの定義を参照して、この特定のソリューションが要件を満たしていることを確認する必要があります。それはOPの規定のreqirementを満たす任意のソリューションが受け入れ可能であることが可能です...
ポール・

まあ、1つの実装argmax(.)も明確であると考えるかもしれません。(IMHOはある種の短絡ロジックに追従しようとしますが、残念ながら普遍的に受け入れられる動作を提供できません)。ありがとう
食べる

3

メソッドnp.argpartitionは最大のk個のインデックスのみを返し、ローカルソートを実行し、np.argsort配列が非常に大きい場合(フルソートを実行する場合)より高速です。ただし、返されるインデックスは昇順/降順ではありません。例を挙げてみましょう:

ここに画像の説明を入力してください

厳密な昇順のトップkインデックスが必要な場合、必要なnp.argpartitionものが返されないことがわかります。

np.argpartitionの後に手動で並べ替えを行う以外に、私の解決策はtorch.topk、ニューラルネットワーク構築用のツールであるPyTorchを使用して、CPUとGPUの両方をサポートするNumPyのようなAPIを提供することです。MKLを備えたNumPyと同じくらい高速で、大規模な行列/ベクトル計算が必要な場合にGPUブーストを提供します。

厳密な上昇/下降トップkインデックスコードは次のようになります。

ここに画像の説明を入力してください

torch.topkはトーチテンソルを受け入れ、上位k値と上位kインデックスの両方をtypeで返しますtorch.Tensor。npと同様に、torch.topkも軸引数を受け入れるため、多次元配列/テンソルを処理できます。


2

使用する:

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

これで、resultリストにはN個のタプル(indexvalue)が含まれ、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])

うまく機能しますが、配列Aに重複した(最大の)値がある場合、より多くの結果が得られます。正確にk個の結果が期待されますが、重複した値の場合、k個より多くの結果が得られます。
グイド、

コードを少し修正しました。返されるインデックスのリストの長さは、kとまったく同じです。重複がある場合、それらは単一のタプルにグループ化されます。
XÆA-12

1

bottleneck N個の最大値を取得するためだけに配列全体をソートする費用が多すぎる場合、部分的なソート関数があります。

このモジュールについては何も知りません。ただググったnumpy partial sort


ボトルネックに部分的なソート関数が見つからず、パーティション関数がありますが、これはソートされません
nbecker

1

以下は、最大要素とその位置を確認する非常に簡単な方法です。これaxisがドメインです。axis= 0はaxis2Dケースの列ごとの最大数を意味し、= 1は行ごとの最大数を意味します。そして、より高い次元では、それはあなた次第です。

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


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を呼び出すと、2番目に大きい要素が返されます。また、これらの要素の元の値をログに記録し、必要に応じて回復することができます。


0

このコードは、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 

これは、行列配列からn_largest要素を抽出するためにも機能する真偽n_largest行列インデックスを生成します

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.