Bir listeyi boole listesine göre filtreleme


127

Bir boole listesindeki değerlere göre filtrelemem gereken değerlerin bir listesi var:

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

Aşağıdaki satırla yeni bir filtrelenmiş liste oluşturuyorum:

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

sonuç:

print filtered_list
[1,4]

Çizgi çalışıyor ama (bana) biraz abartılı görünüyor ve aynısını başarmanın daha basit bir yolu olup olmadığını merak ediyordum.


Tavsiyeler

Aşağıdaki cevaplarda verilen iki iyi tavsiyenin özeti:

1- Bir listeyi filterbenim yaptığım gibi adlandırmayın çünkü bu yerleşik bir işlevdir.

2- Gereksiz olduğu için Trueyaptığım gibi şeyleri karşılaştırmayın if filter[idx]==True... Sadece kullanmak if filter[idx]yeterli.


3
Bilginize, bu akış sıkıştırma adı verilen ortak bir paralel hesaplama ilkesidir . (Basit olduğu için değil, diğer birçok paralel algoritma için yapı taşı olarak kullanıldığı için 'ilkel' olarak adlandırılır)
BlueRaja - Danny Pflughoeft

2
Bazı stil notlar: if filter[indx] == TrueDo not kullanmak ==sizinle kimlik kontrol etmek istiyorsanız Truekullanımı is. Her neyse, bu durumda tüm karşılaştırma işe yaramaz, basitçe kullanabilirsiniz if filter[indx]. Son olarak: hiçbir zaman bir yerleşik aygıtın adını değişken / modül adı olarak kullanmayın (isme atıfta bulunuyorum filter). Gibi bir şey kullanarak included, ifgüzelce okur ( if included[indx])
Bakuriu

Yanıtlar:


184

Aradığınız itertools.compress:

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

Zamanlama karşılaştırmaları (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

filterDeğişken adı olarak kullanmayın , yerleşik bir işlevdir.


@Mehdi Matlab yolunu oldukça mantıksız buluyorum, ama sanırım bu alıştığınız şeye bağlı.
Ian Goldby

Nasıl seçebilirim [2, 6]?
Florent

Anladım, list(compress(list_a, [not i for i in fill]))geri dönmeliyim[2, 6]
Florent

42

Şöyle:

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

Kullanımı zip, herhangi bir indekslemeye gerek kalmadan birden fazla diziyi paralel olarak yinelemenin pitonik yoludur. Bu, her iki dizinin de aynı uzunluğa sahip olduğunu varsayar (zip, en kısa bittikten sonra durur). itertoolsBöyle basit bir durum için kullanmak biraz abartılı ...

Örneğinizde gerçekten yapmayı bırakmanız gereken bir şey, şeyleri True ile karşılaştırmaktır, bu genellikle gerekli değildir. Bunun yerine if filter[idx]==True: ...sadece yazabilirsiniz if filter[idx]: ....


40

Numpy ile:

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

veya list_a bir uyuşmuş dizi olabilir ancak filtre edilemezse Alex Szatmary'nin yanıtına

Numpy genellikle size büyük bir hız artışı sağlar

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

İyi bir nokta, ben kullanmayı tercih NumPyüzerinde listmümkünse. Ama listyine de kullanmanız gerekiyorsa , her iki listeden de ( NumPyçözümü kullanarak ) oluşturdunuz np.array, boole indekslemeyi kullanın ve son olarak diziyi tolist()yöntemle listeye geri dönüştürmelisiniz . Kesin olarak, bu nesnelerin oluşturulmasını zaman karşılaştırmasına dahil etmelisiniz. O zaman kullanmak itertools.compresshala en hızlı çözüm olacaktır.
Nerxis

17

Bunu numpy kullanarak yapmak için, yani bir diziniz varsa a, bunun yerine 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'ı bir boolean dizisine çevirirseniz, gerek kalmadan doğrudan boolean indekslemeyi kullanabilirsiniz where.
Bas Swinckels


-1

Python 3 ile değerleri list_a[filter]almak için kullanabilirsiniz True. Değer almak için şunu Falsekullanınlist_a[~filter]

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.