Python学习笔记——数据结构和算法(二)
1、字典中一个键映射多个值
可以使用collections中的defaultdict来实现,defalultdict接受list或者set为参数
from collections import defaultdict d = defaultdict(list) d['a'].append(1) d['a'].append(2) d['b'].append(4)
>>> d
defaultdict(<type 'list'>, {'a': [1, 2], b: [4]})
2、字典排序
使用 collections
模块中的 OrderedDict
类,会保持元素的插入顺序。可以控制json编码后的程序
from collections import OrderedDict d = OrderedDict() d['foo'] = 1 d['bar'] = 2 d['spam'] = 3 d['grok'] = 4 # Outputs "foo 1", "bar 2", "spam 3", "grok 4" >>> import json >>> json.dumps(d) '{"foo": 1, "bar": 2, "spam": 3, "grok": 4}'
注意,一个 OrderedDict
的大小是一个普通字典的两倍,因为它内部维护着另外一个链表。
3、字典的运算
d = {'a':1},d.keys()、d.values(), d.items()在python 2中返回的是列表。在python3中,d.items()在返回的是一个个包含(键,值)对的元素视图对象,支持集合操作,d.keys()返回一个展现键集合的键视图对象,同样支持集合操作,d.values()返回值得视图对象,但是不支持集合操作。
如在python3中
# Find keys in common a.keys() & b.keys() # { 'x', 'y' } # Find keys in a that are not in b a.keys() - b.keys() # { 'z' } # Find (key,value) pairs in common a.items() & b.items() # { ('y', 2) }
4、删除序列相同元素并保持顺序
如果序列上的值都是 hashable
类型,可以用下面的方式来实现:
def dedupe(items): seen = set() for item in items: if item not in seen:
yield item
seen.add(item)
如果序列的元素不是hashable的,则不能直接放进set中,我们可以通过传入一个转换成hashable的方法来处理序列中的元素。上面的实例改成下面的样子:
def dedupe(items, key=None): seen = set() for item in items: val = item if not key else key(item) if val not in item: yield item seen.add(val) >>> a = [ {'x':1, 'y':2}, {'x':1, 'y':3}, {'x':1, 'y':2}, {'x':2, 'y':4}] >>> dedupe(a, key=lambda d: (d['x'],d['y'])) #同样可以过来文件中重复行 with open(somefile,'r') as f: for line in dedupe(f): ...
5、序列中出现次数最多的元素
可以使用循环和一个字典进行统计,也可以直接使用collections.Counter
类来完成。Counter底层也是用字典来实现的。
words = [ 'look', 'into', 'my', 'eyes', 'look', 'into', 'my', 'eyes', 'the', 'eyes', 'the', 'eyes', 'the', 'eyes', 'not', 'around', 'the', 'eyes', "don't", 'look', 'around', 'the', 'eyes', 'look', 'into', 'my', 'eyes', "you're", 'under' ] from collections import Counter word_counts = Counter(words) # 出现频率最高的3个单词 top_three = word_counts.most_common(3) >>>word_counts Counter({'eyes': 8, 'the': 5, 'look': 4, 'into': 3, 'my': 3, 'around': 2, "you're": 1, "don't": 1, 'under': 1, 'not': 1}) morewords = ['why','are','you','not','looking','in','my','eyes'] #更新 word_counts.update(morewords) #运算 a = Counter(words) b = Counter(morewords) a+b a-b
6、通过字段将记录分组
itertools.groupby()
函数对于这样的数据分组操作非常实用。
rows = [ {'address': '5412 N CLARK', 'date': '07/01/2012'}, {'address': '5148 N CLARK', 'date': '07/04/2012'}, {'address': '5800 E 58TH', 'date': '07/02/2012'}, {'address': '2122 N CLARK', 'date': '07/03/2012'}, {'address': '5645 N RAVENSWOOD', 'date': '07/02/2012'}, {'address': '1060 W ADDISON', 'date': '07/02/2012'}, {'address': '4801 N BROADWAY', 'date': '07/01/2012'}, {'address': '1039 W GRANVILLE', 'date': '07/04/2012'}, ] from operator import itemgetter from itertools import groupby # Sort by the desired field first rows.sort(key=itemgetter('date')) # 等同于rows.sort(key=lambda item:item.get('date')) # Iterate in groups for date, items in groupby(rows, key=itemgetter('date')): print date for i in items: print i
注意,groupby()
仅仅检查连续的元素,所以序列必须先排序。
groupby()
函数扫描整个序列并且查找连续相同值(或者根据指定key函数返回值相同)的元素序列。 在每次迭代的时候,它会返回一个值和一个迭代器对象, 这个迭代器对象可以生成元素值全部等于上面那个值的组中所有对象。
如果想构造分组并允许随机访问,可以考虑使用defaultdict()。
7、过滤序列元素
通常使用列表推导式或者生成器表达式,可以过滤或者缩短序列,同时过滤的同时可以转换元素。
>>> mylist = [1, 4, -5, 10, -7, 2, 3, -1] >>> [n * n for n in mylist if n > 0] [1, 16, 100, 4, 9] >>> [n if n > 0 else 0 for n in mylist] [1, 4, 0, 10, 0, 2, 3, 0]
对于复杂的过滤,可以考虑filter函数,接受一个过滤函数作用域序列每个元素,仅返回序列中结果为True的值。
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) # Outputs ['1', '2', '-3', '4', '5']
8、从字典中提取子集
使用字典推导式可以快速实现需求
prices = { 'ACME': 45.23, 'AAPL': 612.78, 'IBM': 205.55, 'HPQ': 37.20, 'FB': 10.75 } # Make a dictionary of all prices over 200 p1 = {key: value for key, value in prices.items() if value > 200}
9、命名元组
collections.namedtuple()提供了可以通过名称访问元组中元素的方式,可以使代码易于阅读。
>>> 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'