Pythonリスト内包表記を使用した条件に基づく要素のインデックスの検索


119

次のPythonコードは、Matlabのバックグラウンドから取得した場合、非常に長い時間がかかるようです

>>> a = [1, 2, 3, 1, 2, 3]
>>> [index for index,value in enumerate(a) if value > 2]
[2, 5]

Matlabにいるとき私は書くことができます:

>> a = [1, 2, 3, 1, 2, 3];
>> find(a>2)
ans =
     3     6

これをPythonで書く簡単な方法はありますか、それとも長いバージョンをそのまま使うのですか?


Pythonの構文の理論的根拠に関するすべての提案と説明をありがとうございます。

numpyのウェブサイトで次のことを見つけた後、私は私が好きな解決策を見つけたと思います:

http://docs.scipy.org/doc/numpy/user/basics.indexing.html#boolean-or-mask-index-arrays

そのWebサイトからの情報を上記の私の問題に適用すると、次のようになります。

>>> from numpy import array
>>> a = array([1, 2, 3, 1, 2, 3])
>>> b = a>2 
array([False, False, True, False, False, True], dtype=bool)
>>> r = array(range(len(b)))
>>> r(b)
[2, 5]

これで次のように動作するはずです(ただし、テストするためのPythonインタープリターが手元にありません)。

class my_array(numpy.array):
    def find(self, b):
        r = array(range(len(b)))
        return r(b)


>>> a = my_array([1, 2, 3, 1, 2, 3])
>>> a.find(a>2)
[2, 5]

6
いかが[idx for idx in range(len(a)) if a[idx] > 2]ですか?Pythonでこれを行うのが少し厄介なのは、他の言語ほどインデックスを使用しないためです。
NullUserException

回答:


77
  • Pythonでは、これにはインデックスをまったく使用せず、値を処理するだけです[value for value in a if value > 2]。通常、インデックスを扱うということは、何か最善の方法をとっていないことを意味します。

  • Matlabと同様のAPI 必要な場合は、多次元配列のパッケージであるnumpyと、Matlabに強く影響を受けたPythonの数値計算を使用します。リストの代わりにnumpy配列を使用することになります。

    >>> import numpy
    >>> a = numpy.array([1, 2, 3, 1, 2, 3])
    >>> a
    array([1, 2, 3, 1, 2, 3])
    >>> numpy.where(a > 2)
    (array([2, 5]),)
    >>> a > 2
    array([False, False,  True, False, False,  True], dtype=bool)
    >>> a[numpy.where(a > 2)]
    array([3, 3])
    >>> a[a > 2]
    array([3, 3])

2
リストがあり、1つは範囲用で、もう1つは角度用で、あるしきい値を超える範囲の値を除外する必要があります。これらの範囲に対応する角度を「最善の方法」でどのようにフィルタリングするのですか?
Mehdi

3
filtered_ranges_and_angles = [(range, angle) for range, angle in zip(ranges, angles) if should_be_kept(range)]
マイクグラハム

7
「Pythonでは、このためにインデックスをまったく使用せず、値を処理するだけです」このステートメントは、十分なデータ分析と機械学習モデリングを行っていないことを示しています。特定の条件に基づく1つのテンソルのインデックスを使用して、別のテンソルをフィルター処理します。
horaceT 2018

63

別の方法:

>>> [i for i in range(len(a)) if a[i] > 2]
[2, 5]

一般的に、ということを覚えている間は、findすぐ調理機能で、リストの内包表記は一般的であるため、非常に強力なソリューションfindPythonで関数を記述し、後で必要に応じてそれを使用することを妨げるものはありません。すなわち:

>>> def find_indices(lst, condition):
...   return [i for i, elem in enumerate(lst) if condition(elem)]
... 
>>> find_indices(a, lambda e: e > 2)
[2, 5]

ここではMatlabを模倣するためにリストを使用していることに注意してください。ジェネレーターとイテレーターを使用するほうがPythonicになるでしょう。


2
OPは[i for i,v in enumerate(a) if v > 2]代わりにそれを記述できたはずです。
NullUserException

それは短くはなく、長くなります。元のとを置き換えindexて、文字を数えます。ivaluev
agf

@ NullUser、agf:そうですが、要点は2番目の部分です:)
Eli Bendersky

1
enumerateoverの使用range(len(...))は、より堅牢で効率的です。
マイクグラハム

1
@Mike Graham:同意find_indicesしますenumerate
Eli Bendersky、2011


6

多分別の質問は、「それらを取得したら、それらのインデックスをどうするのですか?」それらを使用して別のリストを作成する場合、Pythonでは、これらは不要な中間ステップです。特定の条件に一致するすべての値が必要な場合は、組み込みのフィルターを使用します。

matchingVals = filter(lambda x : x>2, a)

または、独自のリスト内包表記を記述します。

matchingVals = [x for x in a if x > 2]

リストからそれらを削除したい場合、Pythonicの方法は必ずしもリストから削除するのではなく、新しいリストを作成しているかのようにリスト内包を記述listvar[:]し、左側の-側:

a[:] = [x for x in a if x <= 2]

Matlabが提供するのはfind、その配列中心のモデルが配列インデックスを使用して項目を選択することによって機能するためです。確かにこれはPythonでも行えます、@ EliBenderskyで既に述べたように、よりPython的な方法はイテレータとジェネレータを使用することです。


ポール、私はまだスクリプト/関数/クラスでこれの必要性に出会っていません。私が書いているクラスのインタラクティブなテストのためのものです。
Lee

@Mike-編集に感謝しますが、私は本当にそうしましたa[:] = ...-この質問に対するAlex Martelliの回答を参照してくださいstackoverflow.com/questions/1352885/…
PaulMcG 2011

@Paul、私はあなたが説明から「新しいリストを作成する」ことを実際には意味していないと思いました(そして望みました!)。プログラムは、既存のデータを非常に慎重に変更する場合、理解し、維持する方が簡単である傾向があります。いずれにしても、申し訳ありませんが、投稿を編集して好きなように編集できます。
マイクグラハム

6

それが遅い答えであっても、私はこれは非常に良い質問だと思いますし、IMHO Python(numpyのような追加のライブラリやツールキットなし)には、手動で定義されたフィルターに従ってリスト要素のインデックスにアクセスする便利な方法がまだありません。

その機能を提供する関数を手動で定義できます。

def indices(list, filtr=lambda x: bool(x)):
    return [i for i,x in enumerate(list) if filtr(x)]

print(indices([1,0,3,5,1], lambda x: x==1))

収量:[0、4]

私の想像では、完璧な方法は、リストの子クラスを作成し、クラス関数としてインデックス関数を追加することです。この方法では、filterメソッドのみが必要になります。

class MyList(list):
    def __init__(self, *args):
        list.__init__(self, *args)
    def indices(self, filtr=lambda x: bool(x)):
        return [i for i,x in enumerate(self) if filtr(x)]

my_list = MyList([1,0,3,5,1])
my_list.indices(lambda x: x==1)

私はここでそのトピックについてもう少し詳しく説明しました:http : //tinyurl.com/jajrr87

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