179 votes

Filtrage d'une liste basée sur 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]

ce qui entraîne :

print filtered_list
[1,4]

La ligne fonctionne mais semble (pour moi) un peu exagérée et je me demandais s'il y avait 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 pas nommer une liste filter comme je l'ai fait parce que 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. Il suffit d'utiliser if filter[idx] est suffisant.

244voto

Ashwini Chaudhary Points 94431

Vous êtes à la recherche de 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 <code>filter</code> comme nom de variable, il s'agit d'une fonction intégrée.

59voto

Bas Swinckels Points 7613

Comme ça :

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

Utilisation de zip es el pythonique une façon d'itérer sur plusieurs séquences en parallèle, sans avoir besoin d'indexation. Cela suppose que les deux séquences ont la même longueur (zip s'arrête après l'épuisement de la plus courte). Utilisation de itertools pour un cas aussi simple est un peu exagéré ...

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

45voto

Hammer Points 4435

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 un filtre

Numpy vous donne généralement un gros gain de 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

21voto

Alex Szatmary Points 745

Pour faire cela en utilisant 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])

4voto

Daniel Braun Points 163
filtered_list = [list_a[i] for i in range(len(list_a)) if filter[i]]

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X