2013-08-05 58 views
12

Tôi đang cố gắng viết một hàm theo cách thanh lịch, sẽ nhóm một danh sách các từ điển và tổng hợp (tổng hợp) các giá trị của các khóa tương tự.Nhóm và tổng hợp các giá trị của danh sách từ điển trong Python

Ví dụ:

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 
    } 
] 
""" 

Tôi đã cố gắng làm điều này bằng itertools cho groupby và tổng hợp mỗi cặp giá trị như-key, nhưng đang thiếu một cái gì đó ở đây. Dưới đây là chức năng của tôi hiện tại:

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 

Trả lời

19

Bạn có thể sử dụng collections.Countercollections.defaultdict.

Sử dụng lệnh dict này có thể được thực hiện trong O(N), trong khi sắp xếp yêu cầu O(NlogN) thời gian.

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}) 
}) 

Ưu điểm của Counter là nó sẽ tự động tổng hợp các giá trị của phím tương tự .:

Ví dụ:

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

Điều này thật tuyệt vời! Bạn có suy nghĩ nào về việc nhóm theo 2 trường? Giống như nói trong ví dụ đó bạn muốn nhóm theo id và ngày? Ngay bây giờ ý tưởng của tôi là nối hai trường thành một, nhưng nó không có vẻ rất tao nhã. – aiguofer

3

Cảm ơn, tôi quên mất Counter. Tôi vẫn muốn duy trì định dạng đầu ra và sắp xếp dữ liệu được trả về của mình, vì vậy, đây là chức năng cuối cùng của tôi:

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 
Các vấn đề liên quan