2.3.9 根据记录进行分组

在Python程序中,可以将字典或对象实例中的信息根据某个特定的字段(比如日期)分组迭代数据。在Python的itertools模块中提供了内置函数groupby(),能够方便地对数据进行分组处理。使用函数groupby()的语法格式如下所示:

groupby(iterable [,key]):

函数groupby()能够创建一个迭代器,对iterable生成的连续项进行分组,在分组过程中会查找重复项。如果iterable在多次连续迭代中生成同一项,则会定义一个组。如果将函数groupby()应用到一个分类列表中,那么分组将定义该列表中的所有唯一项。key(如果已提供)是一个函数,被应用于每一项,如果此函数存在返回值,该值将用于后续项而不是该项本身的比较,此函数返回的迭代器生成元素(key, group),其中key是分组的键值,group是迭代器,生成组成该组的所有项。

下面的实例文件fen.py演示了使用函数groupby()对数据进行分组的过程。

源码路径:daima\第2章\fen.py

from itertools import groupby
from operator import itemgetter
things = [('2012-05-21', 11), ('2012-05-21', 3), ('2012-05-22', 10),
             ('2012-05-22', 4), ('2012-05-22', 22),('2012-05-23', 33)]
for key, items in groupby(things, itemgetter(0)):
    print(key)
for subitem in items:
  print(subitem)
print('-' * 20)

执行后会输出:

2012-05-21
2012-05-22
2012-05-23
('2012-05-23', 33)

再看下面的实例文件fenzu.py,演示了使用函数groupby()分组复杂数据的过程。

源码路径:daima\第2章\fenzu.py

①rows = [
    {'address': '5412 N CLARK', 'data': '07/01/2018'},
    {'address': '5232 N CLARK', 'data': '07/04/2018'},
    {'address': '5542 E 58ARK', 'data': '07/02/2018'},
    {'address': '5152 N CLARK', 'data': '07/03/2018'},
    {'address': '7412 N CLARK', 'data': '07/02/2018'},
    {'address': '6789 w CLARK', 'data': '07/03/2018'},
    {'address': '9008 N CLARK', 'data': '07/01/2018'},
    {'address': '2227 W CLARK', 'data': '07/04/2018'}
]
②from operator import itemgetter
  from itertools import groupby
  rows.sort(key=itemgetter('data'))
  for data, items in groupby(rows, key=itemgetter('data')):
    print(data)
    for i in items:
③            print(' ', i)
④from collections import defaultdict
  rows_by_date = defaultdict(list)
  for row in rows:
⑤     rows_by_date[row['data']].append(row)
⑥for r in rows_by_date['07/04/2018']:
    print(r)

在①中创建包含时间和地址的一系列字典数据。

在②~③中根据日期以分组的方式迭代数据,首先以目标字段date对序列进行排序,然后使用itertools.groupby()函数进行分组。这里的重点是,首先要根据感兴趣的字段对数据进行排序,因为函数groupby()只能检查连续的项,如果不首先排序的话,将无法按照预想的方式对记录进行分组。

如果只是想简单地根据日期将数据分组到一起,并放进一个大的数据结构以允许进行随机访问,那么建议像④~⑤那样使用函数defaultdict()构建一个一键多值字典。

在⑥中访问每个日期的记录。

执行上述代码后会输出:

07/01/2018
  {'address': '5412 N CLARK', 'data': '07/01/2018'}
  {'address': '9008 N CLARK', 'data': '07/01/2018'}
07/02/2018
  {'address': '5542 E 58ARK', 'data': '07/02/2018'}
  {'address': '7412 N CLARK', 'data': '07/02/2018'}
07/03/2018
  {'address': '5152 N CLARK', 'data': '07/03/2018'}
  {'address': '6789 w CLARK', 'data': '07/03/2018'}
07/04/2018
  {'address': '5232 N CLARK', 'data': '07/04/2018'}
  {'address': '2227 W CLARK', 'data': '07/04/2018'}
{'address': '5232 N CLARK', 'data': '07/04/2018'}
{'address': '2227 W CLARK', 'data': '07/04/2018'}