web-dev-qa-db-fra.com

Filtrer une liste en fonction d'une liste de booléens

J'ai une liste de valeurs que je dois filtrer en fonction des valeurs d'une liste de booléens:

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

Je génère une nouvelle liste filtrée avec la ligne suivante:

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

qui se traduit par:

print filtered_list
[1,4]

La ligne fonctionne mais me semble un peu exagérée et je me demandais s’il existait un moyen plus simple d’obtenir la même chose.


Conseils

Résumé de deux bons conseils donnés dans les réponses ci-dessous:

1- Ne nommez pas une liste filter comme je le faisais car c'est une fonction intégrée.

2- Ne comparez pas les choses à True comme je l’ai fait avec if filter[idx]==True.. puisque c'est inutile. Juste en utilisant if filter[idx] est assez.

104
Gabriel

Vous cherchez itertools.compress :

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

Comparaisons de temps (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

N'utilisez pas filter comme nom de variable, c'est une fonction intégrée.

156
Ashwini Chaudhary

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

ou voir la réponse d'Alex Szatmary si list_a peut être un tableau numpy mais pas filtrer

Numpy vous donne généralement un grand coup de pouce en vitesse

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
36
Hammer

Ainsi:

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

Utiliser Zip est le moyen 'Pythonic' d'itérer plusieurs séquences en parallèle, sans aucune indexation. Utiliser itertools pour un cas aussi simple est un peu exagéré ...

Une chose que vous devriez vraiment arrêter dans votre exemple est de comparer les choses à True, ce n'est généralement pas nécessaire. Au lieu de if filter[idx]==True: ..., vous pouvez simplement écrire if filter[idx]: ....

31
Bas Swinckels

Pour ce faire, utilisez numpy, c'est-à-dire si vous avez un tableau, a, au lieu de 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])
14
Alex Szatmary
filtered_list = [list_a[i] for i in range(len(list_a)) if filter[i]]
1
Daniel Braun