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.
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.
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]
>>> 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.
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
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]: ...
.
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])
filtered_list = [list_a[i] for i in range(len(list_a)) if filter[i]]