Python:优雅而有效的屏蔽列表的方法
Posted
技术标签:
【中文标题】Python:优雅而有效的屏蔽列表的方法【英文标题】:Python: Elegant and efficient ways to mask a list 【发布时间】:2012-05-03 17:22:35 【问题描述】:示例:
from __future__ import division
import numpy as np
n = 8
"""masking lists"""
lst = range(n)
print lst
# the mask (filter)
msk = [(el>3) and (el<=6) for el in lst]
print msk
# use of the mask
print [lst[i] for i in xrange(len(lst)) if msk[i]]
"""masking arrays"""
ary = np.arange(n)
print ary
# the mask (filter)
msk = (ary>3)&(ary<=6)
print msk
# use of the mask
print ary[msk] # very elegant
结果是:
>>>
[0, 1, 2, 3, 4, 5, 6, 7]
[False, False, False, False, True, True, True, False]
[4, 5, 6]
[0 1 2 3 4 5 6 7]
[False False False False True True True False]
[4 5 6]
如你所见,对数组进行屏蔽的操作比列表更优雅。如果你尝试使用 list 上的数组掩码方案,你会得到一个错误:
>>> lst[msk]
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
TypeError: only integer arrays with one element can be converted to an index
问题是为list
s 找到一个优雅的掩码。
更新:jamylak
的回答因引入 compress
而被接受,但 Joel Cornett
提到的要点使解决方案完全符合我的兴趣。
>>> mlist = MaskableList
>>> mlist(lst)[msk]
>>> [4, 5, 6]
【问题讨论】:
【参考方案1】:如果您使用的是numpy
:
>>> import numpy as np
>>> a = np.arange(8)
>>> mask = np.array([False, False, False, False, True, True, True, False], dtype=np.bool)
>>> a[mask]
array([4, 5, 6])
如果你没有使用 numpy,你正在寻找 itertools.compress
>>> from itertools import compress
>>> a = range(8)
>>> mask = [False, False, False, False, True, True, True, False]
>>> list(compress(a, mask))
[4, 5, 6]
【讨论】:
迄今为止最好的解决方案 @jamylak :从 Python 3+ 开始,它似乎是zip()
,而不是 izip()
@Pierre 我现在已经更新了答案。我删除了该代码 sn-p 因为它可能无论如何都具有误导性,并且可以通过链接查看。另外我认为由于原始问题使用了numpy
,因此突出显示numpy
很重要【参考方案2】:
以下内容在 Python 3 中运行良好:
np.array(lst)[msk]
如果您需要返回列表作为结果:
np.array(lst)[msk].tolist()
【讨论】:
来这里是为了专门解决这种方法对于大型列表的效率低下大声笑【参考方案3】:你也可以只使用 list 和 zip
-
定义一个函数
def masklist(mylist,mymask):
return [a for a,b in zip(mylist,mymask) if b]
-
使用它!
n = 8
lst = range(n)
msk = [(el>3) and (el<=6) for el in lst]
lst_msk = masklist(lst,msk)
print(lst_msk)
【讨论】:
【参考方案4】:如果您使用的是 Numpy,您可以使用 Numpy 数组轻松完成,而无需安装任何其他库:
>> a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>> msk = [ True, False, False, True, True, True, True, False, False, False]
>> a = np.array(a) # convert list to numpy array
>> result = a[msk] # mask a
>> result.tolist()
[0, 3, 4, 5, 6]
【讨论】:
【参考方案5】:我不认为它优雅。它很紧凑,但容易混淆,因为结构与大多数语言非常不同。
正如 Rossum 所说的语言设计,我们花在阅读上的时间比编写它的时间要多。一行代码的构造越模糊,对其他人来说就越容易混淆,他们可能对 Python 不熟悉,尽管他们对任何其他语言都有完全的能力。
在服务代码的现实世界中,可读性每天都胜过短格式符号。就像修车一样。包含大量信息的大型图纸使故障排除变得更加容易。
对我来说,我更愿意对使用长格式的某人的代码进行故障排除
print [lst[i] for i in xrange(len(lst)) if msk[i]]
比 numpy 短符号掩码。我不需要对特定的 Python 包有任何特殊的知识来解释它。
【讨论】:
【参考方案6】:由于 jamylak 已经用一个实用的答案回答了这个问题,这里是我的一个带有内置屏蔽支持的列表示例(完全没有必要,顺便说一句):
from itertools import compress
class MaskableList(list):
def __getitem__(self, index):
try: return super(MaskableList, self).__getitem__(index)
except TypeError: return MaskableList(compress(self, index))
用法:
>>> myList = MaskableList(range(10))
>>> myList
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> mask = [0, 1, 1, 0]
>>> myList[mask]
[1, 2]
请注意,compress
在数据或掩码用完时停止。如果您希望保留超出掩码长度的列表部分,您可以尝试以下操作:
from itertools import izip_longest
[i[0] for i in izip_longest(myList, mask[:len(myList)], fillvalue=True) if i[1]]
【讨论】:
+1 感谢您通过提议 MaskableList 解决了在列表上与数组相同的屏蔽使用问题。它看起来非常有趣,并且非常符合我的愿望。快速说明的是,与数组屏蔽相比,这些速度要慢一些。我将您的积分添加为更新。 我尝试了MaskableList
的解决方案,但我在重新实例化它时遇到了一些问题。对于循环中的每个元素,我想用一个新列表来屏蔽它:for i in arange(0,n): fts = MaskableList(F) sorter = argsort(A) result[i] = zip(fts[sorter],A[sorter])
但每次迭代, fts[sorter] 包含相同的值,而 sorter 每次都不同。我通常使用 python 作为脚本语言,因此我对对象不太熟悉。
@Developer:我没有专门测试过它,但是MaskableList
可能会慢很多的一个原因是因为正在进行的异常处理稍微昂贵。尝试切换try...except
,使其默认尝试屏蔽。
@MillaWell:我不熟悉argsort
。还有,A
是什么,F
的内容是什么?
@JoelCornett :argsort 对数组进行排序并返回原始索引列表。 A=[3.5,2.0,1.1,4.0]
; argsort(A)
将返回 [2,1,0,3]
。 F
只是一个不可屏蔽的列表,让我们说 ["A","B","C","D"],因此 zip(fts[sorter],A[sorter])
应该输出:"A":1.1,"B":2.0,"C":3.5,"D":4.0
以上是关于Python:优雅而有效的屏蔽列表的方法的主要内容,如果未能解决你的问题,请参考以下文章