2013-08-05 175 views
12

我试图用一种优雅的方式编写一个函数,它将对字典列表进行分组,并聚合(总和)类似于键的值。按Python聚合字典列表的值

例子:

my_dataset = [ 
    { 
     'date': datetime.date(2013, 1, 1), 
     'id': 99, 
     'value1': 10, 
     'value2': 10 
    }, 
    { 
     'date': datetime.date(2013, 1, 1), 
     'id': 98, 
     'value1': 10, 
     'value2': 10 
    }, 
    { 
     'date': datetime.date(2013, 1, 2), 
     'id' 99, 
     'value1': 10, 
     'value2': 10 
    } 
] 

group_and_sum_dataset(my_dataset, 'date', ['value1', 'value2']) 

""" 
Should return: 
[ 
    { 
     'date': datetime.date(2013, 1, 1), 
     'value1': 20, 
     'value2': 20 
    }, 
    { 
     'date': datetime.date(2013, 1, 2), 
     'value1': 10, 
     'value2': 10 
    } 
] 
""" 

我试着这样做使用itertools为GROUPBY,总结每个样键值对,但我失去了一些东西。下面是我的功能目前是这样的:

def group_and_sum_dataset(dataset, group_by_key, sum_value_keys): 
    keyfunc = operator.itemgetter(group_by_key) 
    dataset.sort(key=keyfunc) 
    new_dataset = [] 
    for key, index in itertools.groupby(dataset, keyfunc): 
     d = {group_by_key: key} 
     d.update({k:sum([item[k] for item in index]) for k in sum_value_keys}) 
     new_dataset.append(d) 
    return new_dataset 

回答

19

您可以使用collections.Countercollections.defaultdict

使用字典可以在O(N)中完成,而排序需要O(NlogN)时间。

from collections import defaultdict, Counter 
def solve(dataset, group_by_key, sum_value_keys): 
    dic = defaultdict(Counter) 
    for item in dataset: 
     key = item[group_by_key] 
     vals = {k:item[k] for k in sum_value_keys} 
     dic[key].update(vals) 
    return dic 
... 
>>> d = solve(my_dataset, 'date', ['value1', 'value2']) 
>>> d 
defaultdict(<class 'collections.Counter'>, 
{ 
datetime.date(2013, 1, 2): Counter({'value2': 10, 'value1': 10}), 
datetime.date(2013, 1, 1): Counter({'value2': 20, 'value1': 20}) 
}) 

Counter的优点是,它会自动总结类似的键:

例中的值:

>>> c = Counter(**{'value1': 10, 'value2': 5}) 
>>> c.update({'value1': 7, 'value2': 3}) 
>>> c 
Counter({'value1': 17, 'value2': 8}) 
+2

这太棒了!你有任何关于按2个字段分组的想法吗?就像在那个例子中说的,你想按ID和日期分组?现在我的想法是将两个字段连接成一个,但看起来不太优雅。 – aiguofer

3

谢谢,我忘了柜台。我仍然想维护输出格式和排序返回的数据集,所以这里是我的最终功能看起来像:

def group_and_sum_dataset(dataset, group_by_key, sum_value_keys): 

    container = defaultdict(Counter) 

    for item in dataset: 
     key = item[group_by_key] 
     values = {k:item[k] for k in sum_value_keys} 
     container[key].update(values) 

    new_dataset = [ 
     dict([(group_by_key, item[0])] + item[1].items()) 
      for item in container.items() 
    ] 
    new_dataset.sort(key=lambda item: item[group_by_key]) 

    return new_dataset