第一章 数据结构和算法(16-20)

1.16 过滤序列元素

问题描述:你有一个数据序列,想利用一些规则从中提取出需要的值或者是缩短序列
解决方案
最简单的过滤序列元素的方法是使用列表推导。如

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

一个潜在缺陷是如果输入非常大时会产生一个非常大的结果,占用大量内存。 故可使用生成器表达式迭代产生过滤的元素。例如:

>>>pos=(n for n in mylist if n>0)
>>>pos
<generator object <genexpr> at 0x0000024E040ECA40>
>>>for x in pos:
       print(x)
    
1
4
10
2
3

当过滤规则比较复杂时,可以将过滤代码放到一个函数中,然后使用内建的**filter()**函数。如下所示:

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

ivals=list(filter(is_int,values))
print(ivals)

结果如下:

['1', '2', '-3', '4', '5']

讨论
过滤操作的一个变种是将不符合条件的值用新值代替。比如,在一系列数据中可能不仅需要正数,还想将不是正数的值替换成指定的数。通过将过滤条件放到条件表达式中去,可以很容易解决问题。如:

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

另外一个值得关注的过滤工具是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]

from itertools import compress
more5=[n>5 for n in counts]
print(more5)
print(list(compress(addresses,more5)))

结果如下:

[False, False, True, False, False, True, True, False]
['5800 E 58TH', '1060 W ADDISON', '4801 N BROADWAY']

关键点在于先创建一个Boolean序列,指出符合条件的元素。然后根据这个序列去选择输出对应位置为True的元素。
compress() 函数返回一个迭代器。

1.17 从字典中提取子集

问题描述:构造一个字典,它是另外一个字典的子集。
解决方案
最简单的方式是使用字典推导:

>>>prices={
    'ACME':45.23,
    'AAPL':612.78,
    'IBM':205.55,
    'HPQ':37.20,
    'FB':10.75
}
>>>p1={key: value for key,value in prices.items() if value>200}
>>>tech_names={'AAPL','IBM','HPQ','MSFT'}
>>>p2={key:value for key,value in prices.items() if key in tech_names}
>>>p1
{'AAPL': 612.78, 'IBM': 205.55}
>>>p2
{'AAPL': 612.78, 'IBM': 205.55, 'HPQ': 37.2}

1.18 映射名称到序列元素

问题描述:有一段通过下标访问列表或者元组中元素的代码,但这样有时会使得代码难以阅读,想通过名称来访问元素。
解决方案
collections.namedtuple() 函数通过使用一个普通的元组对象来解决此问题。这个函数实际上返回Python中标准元组类型子类的一个工厂方法。需要传递一个类型名和需要的字段,然后它会返回一个类,可以初始化此类,为定义的字段传递值等。例如:

>>>from collections import namedtuple
>>>Subscriber=namedtuple('Subscriber',['addr','joined'])
>>>sub=Subscriber('jonesy@example.com','2012-10-19')
>>>sub
Subscriber(addr='jonesy@example.com', joined='2012-10-19')
>>>sub.addr
'jonesy@example.com'
>>>sub.joined
'2012-10-19'

与元组类型是可交换的,支持所有的普通元组操作。
命名元组的一个主要用途是将代码从下标操作中解脱出来。
讨论
命名元组的另一个用途是作为字典的替代,因为字典存储需要更多的内存空间。但是一个命名元组是不可更改的。若真的需要改变属性,则可使用命名元组实例的 _replace()方法,它会创建一个全新的命名元组并将对应的字段用新的值取代。

1.19 转换并同时计算数据

问题描述:需要在数据序列上执行聚集函数(如sum(),min(),max()),但是首先需要先转换或者过滤数据
解决方案
使用生成器表达式参数,比如想计算平方和,可以像下面这样做:

>>>nums=[1,2,3,4,5]
>>>s=sum(x*x for x in nums)
>>>s
55

讨论
上面的示例演示了当生成器表达式作为一个单独参数传递给函数时候的巧妙语法,并不需要多加一个括号
使用一个生成器表达式作为参数会比先创建一个临时列表更加高效和优雅

1.20 合并多个字典或映射

问题描述:现在有多个字典或者映射,想将它们从逻辑上合并为一个单一的映射后执行某些操作,比如查找值或者检查某些键是否存在。
解决方案
假如现在有如下两字典:

>>>a={'x':1,'z':3}
>>>b={'y':2,'z':4}

现在假设必须在两个字典中执行查找操作(比如先从a中查找,如果找不到再去b中找)。一个非常简单的解决方案是使用collections模块中ChainMap类

>>>from collections import ChainMap
>>>c=ChainMap(a,b)
>>>print(c['x'])
1
>>>print(c['y'])
2
>>>print(c['z'])
3

讨论
一个ChainMap接受多个字典并将它们在逻辑上变为一个字典。大部分字典操作可以正常使用,如:

>>>len(c)
3
>>>list(c.keys())
['z', 'y', 'x']
>>>list(c.values())
[3, 2, 1]

如果出现重复键,则第一次出现的映射值会被返回。
作为ChainMap的代替,可能会考虑使用update() 方法将两字典合并,如:

>>>merged=dict(b)
>>>merged.update(a)
>>>merged['x']
1
>>>merged['y']
2
>>>merged['z']
3

也可行,但要创建一个完全不同的字典对象。同时,如果原字典做了更新,这种改变不会反应到新合并字典中。
ChainMap使用原来的字典,不自己创建新字典。

>>>a['x']=13
>>>merged['x']
1
>>>c['x']
13
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值