Фільтрування списку на основі списку булевих


127

У мене є список значень, які мені потрібно відфільтрувати з урахуванням значень у списку булевих:

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]достатньо.


3
Тільки FYI, це звичайний паралельний обчислювальний примітив, який називається ущільненням потоку . (Це називається "примітивом" не тому, що він простий, а тому, що він використовується як будівельний блок для багатьох інших паралельних алгоритмів)
BlueRaja - Danny Pflughoeft

2
Деякі зауваження типу: if filter[indx] == TrueЄ чи НЕ використовувати , ==якщо ви хочете , щоб перевірити ідентичність з True, використанням is. У будь-якому випадку все порівняння марне, ви можете просто скористатися if filter[indx]. Нарешті: ніколи не використовуйте ім’я вбудованого в якості назви змінної / модуля (я маю на увазі ім'я filter). Використовуючи щось на зразок included, щоб ifчитати було добре ( if included[indx]).
Бакуріу

Відповіді:


184

Ви шукаєте 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як ім'я змінної, це вбудована функція.


@Mehdi Я вважаю спосіб Matlab дуже неінтуїтивним, але я вважаю, що це залежить від того, до чого ти звик.
Ян Голдбі

Як я можу вибрати [2, 6]?
Флорент

Я розумію, list(compress(list_a, [not i for i in fill]))повинен повернутися[2, 6]
Флорент

42

Так:

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

Використання zip- це пітонічний спосіб перебирати паралельно декілька послідовностей, не потребуючи жодної індексації. Це передбачає, що обидві послідовності мають однакову довжину (блискавка зупиняється після закінчення найкоротшого терміну). Використання itertoolsдля такого простого випадку трохи непосильне ...

Одне, що ви робите у своєму прикладі, вам справді слід перестати займатися, - це порівнювати речі з True, зазвичай це не потрібно. Замість цього if filter[idx]==True: ...можна просто написати if filter[idx]: ....


40

З нумером:

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

або подивіться відповідь Алекса Сатмарма, якщо list_a може бути нумерованим масивом, але не фільтром

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

Хороше запитання, я вважаю за краще використовувати NumPyбільш , listде це можливо. Але якщо вам все-таки потрібно використовувати list, ви (використовуючи NumPyрішення) створюєте np.arrayз обох списків, використовуйте булеву індексацію і, нарешті, перетворюєте масив назад у список tolist()методом. Для точності слід включити створення цих об'єктів у порівняння часу. Тоді використання itertools.compressвсе одно буде найшвидшим рішенням.
Nerxis

17

Для цього використовуйте 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])

3
Якщо ви перетворите my_filter в бульний масив, ви можете використовувати пряме булеве індексування, без необхідності where.
Bas Swinckels


-1

З python 3 ви можете використовувати list_a[filter]для отримання Trueзначень. Щоб отримати Falseзначення, використовуйтеlist_a[~filter]

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.