2013-08-05 177 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