Pythonでソートされた配列のインデックスを取得する方法


199

数値リストがあります:

myList = [1, 2, 3, 100, 5]

ここで、このリストを並べ替えて取得し[1, 2, 3, 5, 100]ます。私が欲しいのは、元のリストの要素のインデックスを並べ替えられた順序、つまり[0, 1, 2, 4, 3] 値とインデックスの両方を返すMATLABのsort関数です。



@unutbuこれはだまされたものではありません(IMO)。質問はNumpy.argsort()の使用と矛盾しません
amit

@amit:「矛盾しない」とはどういう意味ですか?
unutbu

@unutbu Numpy.argsort()はこの質問への細かい答えです。これは、Numpyのように、リンクされた他のスレッド(これもクローズしていて、私が持ってはならないはずのスレッド)と重複している可能性があります。 argsort()はこれらの2つの場合の良い答えですが、あなたが参照したものではありません。
アミット

1
残念ながら、この質問の例の選択には重大な欠陥があります。質問を読み取る2つの異なる方法は、入力がソートされた順序からの転置である場合、同じ答えを与えるからです。

回答:


188

numpyを使用している場合は、argsort()関数を使用できます。

>>> import numpy
>>> numpy.argsort(myList)
array([0, 1, 2, 4, 3])

http://docs.scipy.org/doc/numpy/reference/generated/numpy.argsort.html

これは、配列またはリストをソートする引数を返します。


これはあなたが望むものではないかもしれないことに注意してください!:この質問を参照してくださいstackoverflow.com/questions/54388972/...
ブラムVanroy

147

次のようなもの:

>>> myList = [1, 2, 3, 100, 5]
>>> [i[0] for i in sorted(enumerate(myList), key=lambda x:x[1])]
[0, 1, 2, 4, 3]

enumerate(myList) (index、value)のタプルを含むリストを提供します:

[(0, 1), (1, 2), (2, 3), (3, 100), (4, 5)]

リストに渡しsorted、ソートキーを抽出する関数を指定してリストをソートします(各タプルの2番目の要素です。それが何のlambdaためにあるのかです。最後に、リストの内包を使用して、ソートされた各要素の元のインデックスが抽出され[i[0] for i in ...]ます。


7
itemgetter(1)ラムダ関数の代わりに使用できます
John La Rooy

4
@gnibblerはitemgetteroperatorモジュールFYIの関数を参照しています。だからそれfrom operator import itemgetterを使用してください。
Lauritz V. Thaulow

1
あなたは、zipファイルを使用してソートされたリストとなインデックスを取得することができます:sorted_items, sorted_inds = zip(*sorted([(i,e) for i,e in enumerate(my_list)], key=itemgetter(1)))
チャールズL.

@RomanBodnarchukこれは機能せず、x = [3,1,2]; numpy.argsort(x)[1,2,0]が得られます。
shahar_m


24

での答えenumerateは素晴らしいですが、私は個人的に値でソートするために使用されるラムダが好きではありません。次の例では、インデックスと値を逆にして並べ替えます。したがって、最初に値で、次にインデックスでソートされます。

sorted((e,i) for i,e in enumerate(myList))

11

答えを更新enumerateしてitemgetter

sorted(enumerate(a), key=lambda x: x[1])
# [(0, 1), (1, 2), (2, 3), (4, 5), (3, 100)]

リストを一緒に圧縮する:タプルの最初の要素がインデックスになり、2番目が値になります(次に、タプルの2番目の値を使用してソートしますx[1]。xはタプルです)

またはモジュールitemgetterから使用operator`:

from operator import itemgetter
sorted(enumerate(a), key=itemgetter(1))

1
この場合、enumerateはzipよりも適切と思われます
njzk2 2013年

10

私はこれらについてperfplot(私のプロジェクト)で簡単なパフォーマンスチェックを行いましたが、numpy(ログスケールに注意)以外は何も推奨するのは難しいことがわかりました。

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


プロットを再現するコード:

import perfplot
import numpy


def sorted_enumerate(seq):
    return [i for (v, i) in sorted((v, i) for (i, v) in enumerate(seq))]


def sorted_enumerate_key(seq):
    return [x for x, y in sorted(enumerate(seq), key=lambda x: x[1])]


def sorted_range(seq):
    return sorted(range(len(seq)), key=seq.__getitem__)


def numpy_argsort(x):
    return numpy.argsort(x)


perfplot.save(
    "argsort.png",
    setup=lambda n: numpy.random.rand(n),
    kernels=[sorted_enumerate, sorted_enumerate_key, sorted_range, numpy_argsort],
    n_range=[2 ** k for k in range(15)],
    xlabel="len(x)",
)


5

基本的にargsort、外部ライブラリ(NumPyなど)を使用する場合、または依存関係のない純粋なPythonを維持する場合は、どの実装が必要かによって異なります。

あなたが自問する必要がある質問は、次のとおりです。

  • 配列/リストをソートするインデックス
  • 要素がソートされた配列/リストで持つインデックス

残念ながら、質問の例ではどちらも同じ結果が得られるため、何が望ましいのか明確にされていません。

>>> arr = np.array([1, 2, 3, 100, 5])

>>> np.argsort(np.argsort(arr))
array([0, 1, 2, 4, 3], dtype=int64)

>>> np.argsort(arr)
array([0, 1, 2, 4, 3], dtype=int64)

argsort実装の選択

NumPyを自由に使用できる場合は、関数numpy.argsortまたはメソッドを使用できますnumpy.ndarray.argsort

NumPyを使用しない実装は他のいくつかの回答ですでに言及されているので、ここでのベンチマークの回答に従って、最速のソリューションを要約します

def argsort(l):
    return sorted(range(len(l)), key=l.__getitem__)

配列/リストをソートするインデックスを取得する

配列/リストをソートするインデックスを取得するargsortには、配列またはリストを呼び出すだけです。ここではNumPyバージョンを使用していますが、Python実装でも同じ結果が得られるはずです

>>> arr = np.array([3, 1, 2, 4])
>>> np.argsort(arr)
array([1, 2, 0, 3], dtype=int64)

結果には、ソートされた配列を取得するために必要なインデックスが含まれています。

ソートされた配列は[1, 2, 3, 4]argsorted 配列になるため、元の配列のこれらの要素のインデックスが含まれます。

  • 最小値はで、元の1インデックス1にあるため、結果の最初の要素は1です。
  • 2指標である2結果の2番目の要素であるので、元に2
  • 3指標である0結果の第三の要素であるので、元に0
  • 最大値で4あり3、元のインデックスにあるため、結果の最後の要素は3です。

要素がソートされた配列/リストで持つインデックスを取得する

この場合、argsort 2回適用する必要があります。

>>> arr = np.array([3, 1, 2, 4])
>>> np.argsort(np.argsort(arr))
array([2, 0, 1, 3], dtype=int64)

この場合 :

  • オリジナルの最初の要素は3であり、これは3番目に大きい値であるため2、ソートされた配列/リストにインデックスがあり、最初の要素は2です。
  • 元の2番目の要素は1であり、これは最小値であるため0、ソートされた配列/リストにインデックスがあり、2番目の要素はになり0ます。
  • オリジナルの3番目の要素は2であり、これは2番目に小さい値であるため1、ソートされた配列/リストにインデックスがあり、3番目の要素は1です。
  • 元の4番目の要素は、4これが最大の値であるため3、ソートされた配列/リストにインデックスがあり、最後の要素はになり3ます。

4

他の答えは間違っています。

argsort1回実行するだけでは解決できません。たとえば、次のコード:

import numpy as np
x = [3,1,2]
np.argsort(x)

array([1, 2, 0], dtype=int64)私たちが望むものではない利回り。

答えはargsort2回実行する必要があります。

import numpy as np
x = [3,1,2]
np.argsort(np.argsort(x))

array([2, 0, 1], dtype=int64)期待どおりに与えます。


あなたの主張は、x[2](3)最小の要素、およびx[1](1)最大の要素にします(整数を並べ替えると、最小値から最大値に並べ替えられるため)。また、OPの例では、単一のnp.argsort([1, 2, 3, 100, 5])yieldsがarray([0, 1, 2, 4, 3])、OPが必要とするインデックスのように見えます。
0 0

1
@ 0 0あなたの例は特定のケースです。実行するとarr = [1,2,3,100, 5, 9] res = np.argsort(arr) print(res)[0 1 2 4 5 3]どちらが間違っているかがわかります。
shahar_m

結果の配列が(増加する)順序になっているため、何が悪いのかarr[res]わかりarray([ 1, 2, 3, 5, 9, 100])ません。
0 0

@ 0 0の場合arr=[1,2,3,100, 5, 9]inds=[0,1,2,5,3,4]これは要素を(次第に)順序付ける順序であるため、出力はになると期待しています。 3位と4位の9。その出力(inds)を取得するには、argsort前述のように2回実行する必要があります。
shahar_m

したがって、これらのインデックスは配列要素の一種のランキングです(0位、1位など)。OPがMATLABにsort言及していることを考えると、np.argsort通常使用されるのと同じように、OPが他の機能を必要としていると考えます(arr[np.argsort[arr]]最後のMATLABの例のように、ソートされた配列を取得するために使用できます)。あなたの答えはこのケース/質問に代わりに適用されます。
0 0

0

numpyをnpとしてインポートする

インデックス用

S=[11,2,44,55,66,0,10,3,33]

r=np.argsort(S)

[output]=array([5, 1, 7, 6, 0, 8, 2, 3, 4])

argsortソートされた順序でSのインデックスを返します

価値のために

np.sort(S)

[output]=array([ 0,  2,  3, 10, 11, 33, 44, 55, 66])

0

0からn-1までのインデックスの別の配列を作成し、これを元の配列に圧縮して、元の値に基づいて並べ替えます

ar = [1,2,3,4,5]
new_ar = list(zip(ar,[i for i in range(len(ar))]))
new_ar.sort()

`

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