Фильтрация списка на основе списка логических элементов
У меня есть список значений, которые мне нужно фильтровать, учитывая значения в списке логических элементов:
list_a = [1, 2, 4, 6]
filter = [True, False, True, False]
Я создаю новый отфильтрованный список со следующей строкой:
filtered_list = [i for indx,i in enumerate(list_a) if filter[indx] == True]
что приводит к:
print filtered_list
[1,4]
Линия работает, но выглядит (мне) немного излишней, и мне было интересно, есть ли более простой способ добиться того же.
Советы
Резюме двух хороших советов, приведенных в ответах ниже:
1- Не называйте список filter
, как я, потому что это встроенная функция.
2- Не сравнивайте вещи с True
, как я делал с if filter[idx]==True..
, поскольку это не нужно. Просто использовать if filter[idx]
достаточно.
Ответы
Ответ 1
Вы ищете itertools.compress
:
>>> from itertools import compress
>>> list_a = [1, 2, 4, 6]
>>> fil = [True, False, True, False]
>>> list(compress(list_a, fil))
[1, 4]
Сравнение времени (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
Не используйте filter
как имя переменной, это встроенная функция.
Ответ 2
С 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])
или см. ответ Alex Szatmary, если list_a может быть массивом numpy, но не фильтровать
Numpy обычно дает вам большой прирост скорости
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
Ответ 3
Так же:
filtered_list = [i for (i, v) in zip(list_a, filter) if v]
Использование zip
- это "питонический" способ повторения нескольких последовательностей параллельно, без необходимости индексирования. Использование itertools для такого простого случая немного перебор...
Одна вещь, которую вы делаете в своем примере, вы должны прекратить делать, сравнивая вещи с True, это обычно не требуется. Вместо if filter[idx]==True: ...
вы можете просто написать if filter[idx]: ...
.
Ответ 4
Чтобы сделать это, используя numpy, т.е. если у вас есть массив, a
вместо 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])
Ответ 5
filtered_list = [list_a[i] for i in range(len(list_a)) if filter[i]]
Ответ 6
Встроенная функция zip
может быть использована для упрощения.
filtered_list = [a for a, f in zip(list_a, filter) if f]