根据布尔值列表过滤列表


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
仅供参考,这是一个称为流压缩的通用并行计算原语。(之所以称其为“原始”不是因为它很简单,而是因为它被用作许多其他并行算法的
构件

2
有些款式注意事项:if filter[indx] == True千万不能使用==,如果你想检查与身份True,使用is。无论如何,在这种情况下,整个比较都没有用,您可以简单地使用if filter[indx]。最后:永远不要将内置名称用作变量/模块名称(我指的是name filter)。使用类似的东西included,这样可以if很好地读取(if included[indx])。
Bakuriu

Answers:


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是在多个索引上并行迭代的pythonic方式,无需任何索引。假设两个序列的长度相同(最短用完后拉链停止)。使用itertools这种简单的情况有点过分...

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


40

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

好一点,我更喜欢使用NumPylist在可能的情况。但是,如果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


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.