Memfilter daftar berdasarkan daftar boolean


127

Saya memiliki daftar nilai yang perlu saya filter mengingat nilai dalam daftar boolean:

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

Saya membuat daftar yang difilter baru dengan baris berikut:

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

yang mengakibatkan:

print filtered_list
[1,4]

Garis bekerja tetapi terlihat (bagi saya) sedikit berlebihan dan saya bertanya-tanya apakah ada cara yang lebih sederhana untuk mencapai hal yang sama.


Saran

Ringkasan dua nasihat baik yang diberikan dalam jawaban di bawah ini:

1- Jangan beri nama daftar filterseperti yang saya lakukan karena ini adalah fungsi bawaan.

2- Jangan membandingkan hal-hal Trueseperti yang saya lakukan dengan if filter[idx]==True..karena itu tidak perlu. Cukup menggunakan if filter[idx]sudah cukup.


3
Hanya FYI, ini adalah komputasi paralel primitif umum yang disebut pemadatan aliran . (Ini disebut 'primitif' bukan karena sederhana, tetapi karena digunakan sebagai blok pembangun untuk banyak algoritma paralel lainnya)
BlueRaja - Danny Pflughoeft

2
Beberapa gaya catatan: if filter[indx] == TrueDo tidak menggunakan== jika Anda ingin memeriksa identitas dengan True, penggunaan is. Pokoknya dalam hal ini seluruh perbandingan tidak berguna, Anda bisa menggunakannya if filter[indx]. Terakhir: jangan pernah menggunakan nama built-in sebagai nama variabel / modul (saya mengacu pada nama filter). Menggunakan sesuatu seperti included, sehingga ifterbaca dengan baik ( if included[indx]).
Bakuriu

Jawaban:


184

Anda sedang mencari itertools.compress:

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

Perbandingan waktu (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

Jangan gunakan filtersebagai nama variabel, itu adalah fungsi bawaan.


@Mehdi Saya menemukan cara Matlab sangat tidak intuitif, tapi saya kira itu tergantung pada apa yang Anda terbiasa.
Ian Goldby

Bagaimana saya bisa memilih [2, 6]?
Florent

Saya mengerti, list(compress(list_a, [not i for i in fill]))harus kembali[2, 6]
Florent

42

Seperti itu:

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

Menggunakan zipadalah cara pythonic untuk beralih pada beberapa urutan secara paralel, tanpa perlu pengindeksan apa pun. Ini mengasumsikan kedua sekuens memiliki panjang yang sama (zip berhenti setelah kehabisan terpendek). Menggunakan itertoolsuntuk kasus sederhana semacam itu agak berlebihan ...

Satu hal yang Anda lakukan dalam contoh Anda, yang benar-benar harus Anda hentikan adalah membandingkan berbagai hal dengan True, ini biasanya tidak perlu. Alih-alih if filter[idx]==True: ..., Anda cukup menulis if filter[idx]: ....


40

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

atau lihat jawaban Alex Szatmary jika list_a dapat berupa array numpy tetapi tidak filter

Numpy biasanya memberi Anda dorongan kecepatan besar juga

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

Poin yang bagus, saya lebih suka menggunakan NumPylebih dari yang listmungkin. Tetapi jika Anda harus listtetap menggunakan , Anda memiliki (menggunakan NumPysolusi) buat np.arraydari kedua daftar, gunakan pengindeksan boolean dan akhirnya mengubah array kembali ke daftar dengan tolist()metode. Untuk lebih tepatnya, Anda harus memasukkan benda-benda itu ke dalam perbandingan waktu. Kemudian, menggunakan itertools.compressakan tetap menjadi solusi tercepat.
Nerxis

17

Untuk melakukan ini menggunakan numpy, yaitu, jika Anda memiliki array a,, bukannya 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
Jika Anda mengubah my_filter menjadi array boolean, Anda dapat menggunakan pengindeksan boolean langsung, tanpa perlu where.
Bas Swinckels


-1

Dengan python 3 Anda bisa menggunakan list_a[filter]untuk mendapatkan Truenilai. Untuk mendapatkan Falsenilai, gunakanlist_a[~filter]

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.