Filtrowanie listy na podstawie listy wartości logicznych

127

Mam listę wartości, które muszę filtrować, biorąc pod uwagę wartości na liście wartości logicznych:

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

Generuję nową listę filtrowaną z następującym wierszem:

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

Co skutkuje w:

print filtered_list
[1,4]

Linia działa, ale wygląda (dla mnie) trochę przesadzona i zastanawiałem się, czy istnieje prostszy sposób na osiągnięcie tego samego.


Porady

Podsumowanie dwóch dobrych rad zawartych w poniższych odpowiedziach:

1- Nie nazywaj listy filtertak jak ja, ponieważ jest to funkcja wbudowana.

2- Nie porównuj rzeczy z Truetymi, które zrobiłem, if filter[idx]==True..ponieważ jest to niepotrzebne. Wystarczy użyć if filter[idx].

Gabriel
źródło
3
Do Twojej wiadomości, jest to powszechny prymityw obliczeniowy równoległy zwany kompaktowaniem strumieniowym . (Nazywa się „prymitywnym” nie dlatego, że jest prosty, ale dlatego, że jest używany jako element
składowy
2
Kilka uwag w stylu: if filter[indx] == TrueCzy nie używać ==, jeśli chcesz sprawdzić identyczności True, zastosowania is. Zresztą w tym przypadku całe porównanie jest bezużyteczne, możesz po prostu użyć if filter[indx]. Wreszcie: nigdy nie używaj nazwy wbudowanej jako nazwy zmiennej / modułu (mam na myśli nazwę filter). Używając czegoś w stylu included, żeby ifładnie się czyta ( if included[indx]).
Bakuriu,

Odpowiedzi:

184

Szukasz itertools.compress:

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

Porównanie czasów (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

Nie używaj filter jako nazwy zmiennej, jest to funkcja wbudowana.

Ashwini Chaudhary
źródło
@Mehdi Uważam, że sposób Matlab jest wysoce nieintuicyjny, ale przypuszczam, że zależy to od tego, do czego jesteś przyzwyczajony.
Ian Goldby,
Jak mogę wybrać [2, 6]?
Florent
Rozumiem, list(compress(list_a, [not i for i in fill]))powinienem wrócić[2, 6]
Florent
42

Tak jak to:

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

Używanie zipjest pythonowym sposobem na iterację wielu sekwencji równolegle, bez konieczności indeksowania. Zakłada się, że obie sekwencje mają tę samą długość (suwak zatrzymuje się po najkrótszym wyczerpaniu). Za pomocąitertools w tak prostym przypadku to trochę przesada ...

Jedną z rzeczy, które robisz w swoim przykładzie, naprawdę powinieneś przestać, jest porównywanie rzeczy z Prawdą, zwykle nie jest to konieczne. Zamiast tego if filter[idx]==True: ...możesz po prostu napisać if filter[idx]: ....

Bas Swinckels
źródło
40

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

lub zobacz odpowiedź Alexa Szatmary'ego, jeśli list_a może być tablicą numpy, ale nie filtrem

Numpy zwykle daje również duże przyspieszenie

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
Młotek
źródło
Dobra uwaga, wolę używać NumPygo listtam, gdzie to możliwe. Ale jeśli i tak musisz użyć list, musisz (używając NumPyrozwiązania) utworzyć np.arrayz obu list, użyć indeksowania boolowskiego i ostatecznie przekonwertować tablicę z powrotem na listę za pomocą tolist()metody. Aby być precyzyjnym, należy uwzględnić te utworzone obiekty w porównaniu czasu. Wtedy używanie itertools.compressbędzie nadal najszybszym rozwiązaniem.
Nerxis
17

Aby to zrobić za pomocą numpy, tj. Jeśli masz tablicę a, zamiast 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])
Alex Szatmary
źródło
3
Jeśli zmienisz my_filter w tablicę boolowską, możesz użyć bezpośredniego indeksowania boolowskiego, bez potrzeby where.
Bas Swinckels,
1
filtered_list = [list_a[i] for i in range(len(list_a)) if filter[i]]
Daniel Braun
źródło
-1

W Pythonie 3 możesz używać list_a[filter]do pobierania Truewartości. Aby uzyskać Falsewartości, użyjlist_a[~filter]

Franklin'j Gil'z
źródło