1.15 通过�个字段将记录分组¶

问题¶

你有一个字典或者实例的�列,然�你想根��个特定的字段比如 date �分组迭代访问。

解决方案¶

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'},
]

现在�设你想在按 date 分组�的数��上进行迭代。为了这样�,你首先需�按照指定的字段(这里就是 date )排�, 然�调用 itertools.groupby() 函数:

from operator import itemgetter
from itertools import groupby

# Sort by the desired field first
rows.sort(key=itemgetter('date'))
# Iterate in groups
for date, items in groupby(rows, key=itemgetter('date')):
    print(date)
    for i in items:
        print(' ', i)

�行结果:

07/01/2012
  {'date': '07/01/2012', 'address': '5412 N CLARK'}
  {'date': '07/01/2012', 'address': '4801 N BROADWAY'}
07/02/2012
  {'date': '07/02/2012', 'address': '5800 E 58TH'}
  {'date': '07/02/2012', 'address': '5645 N RAVENSWOOD'}
  {'date': '07/02/2012', 'address': '1060 W ADDISON'}
07/03/2012
  {'date': '07/03/2012', 'address': '2122 N CLARK'}
07/04/2012
  {'date': '07/04/2012', 'address': '5148 N CLARK'}
  {'date': '07/04/2012', 'address': '1039 W GRANVILLE'}

讨论¶

groupby() 函数扫�整个�列并且查找连续相�值(或者根�指定 key 函数返回值相�)的元素�列。 在�次迭代的时候,它会返回一个值和一个迭代器对象, 这个迭代器对象�以生�元素值全部等于上�那个值的组中所有对象。

一个�常��的准备步骤是�根�指定的字段将数�排�。 因为 groupby() 仅仅检查连续的元素,如果事先并没有排�完�的�,分组函数将得�到想�的结果。

如果你仅仅�是想根� date 字段将数�分组到一个大的数�结构中去,并且�许�机访问, 那么你最好使用 defaultdict() �构建一个多值字典,关于多值字典已�在 1.6 �节有过详细的介�。比如:

from collections import defaultdict
rows_by_date = defaultdict(list)
for row in rows:
    rows_by_date[row['date']].append(row)

这样的�你�以很轻�的就能对�个指定日期访问对应的记录:

>>> for r in rows_by_date['07/01/2012']:
... print(r)
...
{'date': '07/01/2012', 'address': '5412 N CLARK'}
{'date': '07/01/2012', 'address': '4801 N BROADWAY'}
>>>

在上�这个例�中,我们没有必�先将记录排�。因此,如果对内存�用�是很关心, 这�方�会比先排�然��通过 groupby() 函数迭代的方��行得快一些。