拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 1.16 过滤序列元素

1.16 过滤序列元素

白鹭 - 2022-02-12 2006 0 0

问题描述

你有一个资料序列,想利用一些规则从中提取出需要的值或是缩短的序列,

解决方案

最简答的过滤序列元素的方法就是使用串列推导式,比如:

mylist = [1, 4, -5, 10, -7, 2, 3, -1]
res = [n for n in mylist if n > 0]
"""
res = [1, 4, 10, 2, 3]
"""

如果你对存储器比较敏感,可以使用生成器表达式迭代产生过滤的元素,比如:

gen = (n for n in mylist if n > 0)
print(gen)
""" 输出结果:
<generator object <genexpr> at 0x000001E4C8796CF0>
"""

for i in gen:
    print(i)
""" 输出结果:
1
4
10
2
3
"""

有时候过滤规则比较复杂,不能简单的在串列推导式或者生成器表达式中表达出来,比如,过滤的时候需要处理一些例外或其他复杂情况,这时候你可以将过滤代码封装到一个函式中,然后使用内置函式filter(),例如:

values = ['1', '2', '-3', '-', '4', 'N/A', '5']
def is_int(v):
    try:
        x = int(v)
        return True
    except ValueError:
        return False

res = list(filter(is_int, values))
print(res)
"""输出结果:
['1', '2', '-3', '4', '5']
"""

需要注意,filter()函式创建的是一个迭代器,

讨论

由官方档案的介绍,串列推导式的方括号内包含以下内容:一个表达式,后面为一个for子句,然后是零个或多个for或if子句,结果是由表达式依据for和if子句求值计算而得出一个新串列,清楚了概念,再看下面几个例子,

串列推导式和生成器表达式通常是过滤资料最简单的方式,它们还能在过滤的时候转换资料,例如:

import math
mylist = [1, 4, -5, 10, -7, 2, 3, -1]
res = [math.sqrt(n) for n in mylist if n > 0]
print(res)
"""输出结果:
[1.0, 2.0, 3.1622776601683795, 1.4142135623730951, 1.7320508075688772]
"""

过滤操作的一个变种是将不符合条件的值用新的值替代,而不是丢掉它们,比如,你不仅想找到正数,还想将不是正数的数换成0,通过将过滤条件放到条件表达式中,就可以很容易地解决这个问题:

res = [n if n > 0 else 0 for n in mylist]
"""
res = [1, 4, 0, 10, 0, 2, 3, 0]
"""

res = [n if n < 0 else 0 for n in mylist]
"""
res = [0, 0, -5, 0, -7, 0, 0, -1]
"""

另外一个值得关注的过滤工具就是itertools.compress(),它以一个iterable物件和一个相对应的Boolean选择器序列作为输入自变量,然后输出iterable物件中对应选择器为True的元素,当你需要用另一个相关联的序列来过滤某个序列的时候,这个函式是非常有用的,例如,你有如下两组资料:

addresses = [
    '5412 N CLARK',
    '5148 N CLARK',
    '5800 E 58TH',
    '2122 N CLARK',
    '5645 N RAVENSWOOD',
    '1060 W ADDISON',
    '4801 N BROADWAY',
    '1039 W GRANVILLE',
]
counts = [ 0, 3, 10, 4, 1, 7, 6, 1]

这两个序列的元素个数是相等的,也就是它们的元素可以是一一对应的关系,现在你想将对应count值大于5的地址值全部输出,那么你可以:

for itertools import compress

more5 = [n > 5 for n in counts]
print(more5)
"""输出结果:
[False, False, True, False, False, True, True, False]
"""

res = list(compress(addresses, more5))
print(res)
"""输出结果:
['5800 E 58TH', '1060 W ADDISON', '4801 N BROADWAY']
"""

这里的关键点在于先创建一个Boolean序列,指示哪些元素符合条件,然后compress()函式根据这个序列去选择输出对应位置为True的元素,

filter()函式类似,compress()回传的也是一个迭代器,因此,如果你想得到一个串列,那么你需要使用list()来将结果转换为串列型别,


总结

本节介绍了过滤序列元素的几种方法,

最简单的方法就是利用串列推导式,如果你对存储器使用有要求,可以改用生成器表达式,如果过滤规则过于复杂,无法在串列推导式或生成器表达式中表达出来,可以使用内置函式filter()
另外还介绍了一个过滤工具itertools.compress(),当你需要通过另一个相关联的序列来过滤某个序列时,可以用到它,

标签:

0 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *