根据布尔值列表过滤列表

Posted

技术标签:

【中文标题】根据布尔值列表过滤列表【英文标题】:Filtering a list based on a list of booleans 【发布时间】:2013-09-11 00:46:00 【问题描述】:

我有一个值列表,给定布尔值列表中的值,我需要对其进行过滤:

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] 就足够了。

【问题讨论】:

仅供参考,这是一个常见的并行计算原语,称为stream compaction。 (它被称为“原始”不是因为它简单,而是因为它被用作许多其他并行算法的构建块) 一些风格注释:if filter[indx] == True 不要不要使用==,如果你想用True检查身份,使用is。无论如何,在这种情况下,整个比较是没有用的,你可以简单地使用if filter[indx]。最后:从不使用内置名称作为变量/模块名称(我指的是名称filter)。使用included 之类的东西,这样if 就可以很好地读取(if included[indx])。 【参考方案1】:

像这样:

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

使用zippythonic 方式来并行迭代多个序列,而不需要任何索引。这假设两个序列具有相同的长度(在最短的用完后拉链停止)。在这样一个简单的案例中使用itertools 有点矫枉过正......

您在示例中真正应该停止做的一件事是将事物与 True 进行比较,这通常不是必需的。你可以直接写if filter[idx]: ...,而不是if filter[idx]==True: ...

【讨论】:

【参考方案2】:

你正在寻找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] 'list' object is not callable【参考方案3】:

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

【讨论】:

如果把my_filter变成布尔数组,可以直接使用布尔索引,不需要where【参考方案4】:

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

如果 list_a 可以是 numpy 数组但不能过滤,请参阅 Alex Szatmary 的回答

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 仍然是最快的解决方案。【参考方案5】:
filtered_list = [list_a[i] for i in range(len(list_a)) if filter[i]]

【讨论】:

【参考方案6】:

使用 python 3,您可以使用 list_a[filter] 获取 True 值。要获取False 值,请使用list_a[~filter]

【讨论】:

用例如numpy.array,这可行,但对于内置列表则不行。

以上是关于根据布尔值列表过滤列表的主要内容,如果未能解决你的问题,请参考以下文章

删除具有绑定布尔值的列表项 - SwiftUI

数组列表按布尔值排序,然后按日期 JavaScript / TypeScript

使用 ComboBox 作为查询条件 - 布尔值

布尔值上的 ng-repeat 过滤器

获取布尔选项值

通过比较 2 个列表创建布尔列表