ブール値のリストに基づくリストのフィルタリング


127

ブール値のリストの値を指定してフィルタリングする必要がある値のリストがあります。

list_a = [1, 2, 4, 6]
filter = [True, False, True, False]

次の行を使用して、新しいフィルター済みリストを生成します。

filtered_list = [i for indx,i in enumerate(list_a) if filter[indx] == True]

その結果:

print filtered_list
[1,4]

ラインは機能しますが、(私にとって)少しやり過ぎに見え、同じことを達成するためのより簡単な方法があるかどうか疑問に思っていました。


アドバイス

以下の回答で与えられる2つの良いアドバイスの要約:

1- filter組み込み関数であるため、リストに名前を付けないでください。

2- それは不必要なので、True私がしたように物事を比較しないでくださいif filter[idx]==True..。使うif filter[idx]だけで十分です。


3
ちなみに、これはストリーム圧縮と呼ばれる一般的な並列計算プリミティブです。(単純なためではなく、他の多くの並列アルゴリズムのビルディングブロックとして使用されるため、「プリミティブ」と呼ばれます)
BlueRaja-Danny Pflughoeft

2
いくつかのスタイルのノート:if filter[indx] == TrueDOはない使用==あなたが身元をチェックしたい場合にTrue使用is。とにかく、この場合、比較全体が役に立たないので、単にを使用できますif filter[indx]。最後に、組み込みの名前を変数/モジュール名として使用しないでください(私は名前を参照していますfilter)。のようなものを使用しincludedて、if読み取りがうまくいくようにします(if included[indx])。
Bakuriu 2013

回答:


184

あなたが探していますitertools.compress

>>> from itertools import compress
>>> list_a = [1, 2, 4, 6]
>>> fil = [True, False, True, False]
>>> list(compress(list_a, fil))
[1, 4]

タイミング比較(py3.x):

>>> list_a = [1, 2, 4, 6]
>>> fil = [True, False, True, False]
>>> %timeit list(compress(list_a, fil))
100000 loops, best of 3: 2.58 us per loop
>>> %timeit [i for (i, v) in zip(list_a, fil) if v]  #winner
100000 loops, best of 3: 1.98 us per loop

>>> list_a = [1, 2, 4, 6]*100
>>> fil = [True, False, True, False]*100
>>> %timeit list(compress(list_a, fil))              #winner
10000 loops, best of 3: 24.3 us per loop
>>> %timeit [i for (i, v) in zip(list_a, fil) if v]
10000 loops, best of 3: 82 us per loop

>>> list_a = [1, 2, 4, 6]*10000
>>> fil = [True, False, True, False]*10000
>>> %timeit list(compress(list_a, fil))              #winner
1000 loops, best of 3: 1.66 ms per loop
>>> %timeit [i for (i, v) in zip(list_a, fil) if v] 
100 loops, best of 3: 7.65 ms per loop

filter変数名として使用しないでください。これは組み込み関数です。


@Mehdi Matlabの方法は非常に直感的でないと思いますが、これは慣れ親しんでいることによると思います。
Ian Goldby 2017

どうすれば選択できます[2, 6]か?
フロラン

わかった、list(compress(list_a, [not i for i in fill]))戻ってくるはずだ[2, 6]
フロレント

42

そのようです:

filtered_list = [i for (i, v) in zip(list_a, filter) if v]

を使用すると、インデックス付けを必要とせずに複数のシーケンスを並行して反復zipするPythonの方法です。これは、両方のシーケンスの長さが同じであることを前提としています(最短で実行すると、zipが停止します)。itertoolsそのような単純なケースに使用するのは少しやりすぎです...

あなたがあなたの例であなたが本当にやめるべきである一つのことは、物事をTrueと比較することです、これは通常必要ではありません。の代わりにif filter[idx]==True: ...、単に書くことができますif filter[idx]: ...


40

numpyで:

In [128]: list_a = np.array([1, 2, 4, 6])
In [129]: filter = np.array([True, False, True, False])
In [130]: list_a[filter]

Out[130]: array([1, 4])

または、list_aをnumpy配列にすることはできるがフィルタはできない場合は、Alex Szatmaryの回答を参照してください。

Numpyは通常、速度も大幅に向上します

In [133]: list_a = [1, 2, 4, 6]*10000
In [134]: fil = [True, False, True, False]*10000
In [135]: list_a_np = np.array(list_a)
In [136]: fil_np = np.array(fil)

In [139]: %timeit list(itertools.compress(list_a, fil))
1000 loops, best of 3: 625 us per loop

In [140]: %timeit list_a_np[fil_np]
10000 loops, best of 3: 173 us per loop

良い点、私は可能な限り使用NumPyすることを好むlist。しかし、listとにかく使用する必要がある場合は、両方のリストから(NumPyソリューションを使用して)作成np.arrayし、ブールインデックスを使用し、最後にtolist()メソッドで配列をリストに変換します。正確には、これらのオブジェクトの作成を時間比較に含める必要があります。次に、使用itertools.compressすることは依然として最速のソリューションになります。
Nerxis

17

この使用numpyのを行うには、つまり、あなたは配列を持っている場合、a代わりのlist_a

a = np.array([1, 2, 4, 6])
my_filter = np.array([True, False, True, False], dtype=bool)
a[my_filter]
> array([1, 4])

3
my_filterをブール配列に変換すると、を使用せずに直接ブールインデックスを使用できますwhere
Bas Swinckels 2013


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