2014-02-10 113 views
15

通過多個鍵對Python進行分組並彙總/平均值列表的最常用Python方法是什麼?說我有如下的詞典列表:按多個鍵進行分組並彙總字典列表的平均值

input = [ 
{'dept': '001', 'sku': 'foo', 'transId': 'uniqueId1', 'qty': 100}, 
{'dept': '001', 'sku': 'bar', 'transId': 'uniqueId2', 'qty': 200}, 
{'dept': '001', 'sku': 'foo', 'transId': 'uniqueId3', 'qty': 300}, 
{'dept': '002', 'sku': 'baz', 'transId': 'uniqueId4', 'qty': 400}, 
{'dept': '002', 'sku': 'baz', 'transId': 'uniqueId5', 'qty': 500}, 
{'dept': '002', 'sku': 'qux', 'transId': 'uniqueId6', 'qty': 600}, 
{'dept': '003', 'sku': 'foo', 'transId': 'uniqueId7', 'qty': 700} 
] 

所需的輸出爲聚集:

output=[ 
{'dept': '001', 'sku': 'foo', 'qty': 400}, 
{'dept': '001', 'sku': 'bar', 'qty': 200}, 
{'dept': '002', 'sku': 'baz', 'qty': 900}, 
{'dept': '002', 'sku': 'qux', 'qty': 600}, 
{'dept': '003', 'sku': 'foo', 'qty': 700} 
] 

或平均:

output=[ 
{'dept': '001', 'sku': 'foo', 'avg': 200}, 
{'dept': '001', 'sku': 'bar', 'avg': 200}, 
{'dept': '002', 'sku': 'baz', 'avg': 450}, 
{'dept': '002', 'sku': 'qux', 'avg': 600}, 
{'dept': '003', 'sku': 'foo', 'avg': 700} 
] 

我發現這個:Group by and aggregate the values of a list of dictionaries in Python,但它不似乎給了我想要的東西。

回答

25

爲了得到彙總結果

from itertools import groupby 
from operator import itemgetter 

grouper = itemgetter("dept", "sku") 
result = [] 
for key, grp in groupby(sorted(input_data, key = grouper), grouper): 
    temp_dict = dict(zip(["dept", "sku"], key)) 
    temp_dict["qty"] = sum(item["qty"] for item in grp) 
    result.append(temp_dict) 

from pprint import pprint 
pprint(result) 

輸出

[{'dept': '001', 'qty': 200, 'sku': 'bar'}, 
{'dept': '001', 'qty': 400, 'sku': 'foo'}, 
{'dept': '002', 'qty': 900, 'sku': 'baz'}, 
{'dept': '002', 'qty': 600, 'sku': 'qux'}, 
{'dept': '003', 'qty': 700, 'sku': 'foo'}] 

而得到的平均值,你可以簡單地將裏面的內容for循環,這樣

temp_dict = dict(zip(["dept", "sku"], key)) 
temp_list = [item["qty"] for item in grp] 
temp_dict["avg"] = sum(temp_list)/len(temp_list) 
result.append(temp_dict) 

輸出

[{'avg': 200, 'dept': '001', 'sku': 'bar'}, 
{'avg': 200, 'dept': '001', 'sku': 'foo'}, 
{'avg': 450, 'dept': '002', 'sku': 'baz'}, 
{'avg': 600, 'dept': '002', 'sku': 'qux'}, 
{'avg': 700, 'dept': '003', 'sku': 'foo'}] 

建議:無論如何,我會增加均在同dictqtyavg這樣

temp_dict = dict(zip(["dept", "sku"], key)) 
temp_list = [item["qty"] for item in grp] 
temp_dict["qty"] = sum(temp_list) 
temp_dict["avg"] = temp_dict["qty"]/len(temp_list) 
result.append(temp_dict) 

輸出

[{'avg': 200, 'dept': '001', 'qty': 200, 'sku': 'bar'}, 
{'avg': 200, 'dept': '001', 'qty': 400, 'sku': 'foo'}, 
{'avg': 450, 'dept': '002', 'qty': 900, 'sku': 'baz'}, 
{'avg': 600, 'dept': '002', 'qty': 600, 'sku': 'qux'}, 
{'avg': 700, 'dept': '003', 'qty': 700, 'sku': 'foo'}] 
2

使用numpy的EP你可以找到here,你可以寫:

inputs = dict((k, [i[k] for i in input ]) for k in input[0].keys()) 
print group_by((inputs['dept'], inputs['sku'])).mean(inputs['qty']) 

然而,你可能要考慮使用熊貓包,如果你做了很多這樣的關係操作。

0

像往常一樣,有很多有效的解決方案,我喜歡defaultdict,因爲我覺得它更容易理解。

from collections import defaultdict as df 
food = df(lambda:df(lambda:df(int))) 
for dct in input: food[dct['transId']][dct['sku']][dct['dept']]=dct['qty'] 
output_tupl=[(d1,d2,sum(food[d1][d2][d3] for d3 in food[d1][d2]))for d1 in food for d2 in food[d1]] 
5

靈感來自Eelco Hoogendoorn的回答。這是使用Pandas軟件包解決此問題的另一種方法。代碼更具可讀性。

import numpy as np 
import pandas as pd 

def sum_by_cusip_and_dept(data): 
    df = pd.DataFrame(data) 
    grouped = df.groupby(['sku', 'dept'])  
    sum = grouped.sum() 
    return [{'sku': r[0], 'dept': r[1], 'qty': kv.to_dict().get('qty')} for r, kv in sum.iterrows()]  
0

你可以把數量和它們的出現在一個大的默認字典數:

from collections import defaultdict 

counts = defaultdict(lambda: [0, 0]) 
for line in input_data: 
    entry = counts[(line['dept'], line['sku'])] 
    entry[0] += line['qty'] 
    entry[1] += 1 

現在是隻拿到號碼爲類型的字典列表中的問題:

sums_dict = [{'dept': k[0], 'sku': k[1], 'qty': v[0]} 
       for k, v in counts.items()] 
avg_dict = [{'dept': k[0], 'sku': k[1], 'avg': float(v[0])/v[1]} for 
      k, v in counts.items()] 

的總和結果:

sums_dict 

[{'dept': '002', 'qty': 600, 'sku': 'qux'}, 
{'dept': '001', 'qty': 400, 'sku': 'foo'}, 
{'dept': '003', 'qty': 700, 'sku': 'foo'}, 
{'dept': '002', 'qty': 900, 'sku': 'baz'}, 
{'dept': '001', 'qty': 200, 'sku': 'bar'}] 

和平均值:

avg_dict 

[{'avg': 600.0, 'dept': '002', 'sku': 'qux'}, 
{'avg': 200.0, 'dept': '001', 'sku': 'foo'}, 
{'avg': 700.0, 'dept': '003', 'sku': 'foo'}, 
{'avg': 450.0, 'dept': '002', 'sku': 'baz'}, 
{'avg': 200.0, 'dept': '001', 'sku': 'bar'}] 

另一種版本,而默認的字典:

counts = {} 
for line in input_data: 
    entry = counts.setdefault((line['dept'], line['sku']), [0, 0]) 
    entry[0] += line['qty'] 
    entry[1] += 1 

的其餘部分是相同的:

sums_dict = [{'dept': k[0], 'sku': k[1], 'qty': v[0]} 
       for k, v in counts.items()] 
avg_dict = [{'dept': k[0], 'sku': k[1], 'avg': float(v[0])/v[1]} for 
      k, v in counts.items()] 
0

我對上面一些額外的要求原來的問題。如果您需要將分組鍵作爲字典重新構建,我想要傳遞石斑魚,而不必傳遞字段的原始順序。

namedtuple()工作得很好,因爲它可以讓你排序和使用._asdict()

from collections import namedtuple 

def get_grouper(fields): 

    key = namedtuple('GroupingKey', fields) 

    def get_key(row): 
     return key(**{field: row[field] for field in fields}) 

    return get_key 

rows = [ 
    {'a': 1, 'b': 1, 'c': 1}, 
    {'a': 1, 'b': 2, 'c': 3}, 
    {'a': 1, 'b': 1, 'c': 2}, 
    {'a': 1, 'b': 0}, 
    {'a': 1, 'b': 2, 'c': 4} 
] 

grouper = get_grouper(['a','b']) 

rows = sorted(rows, key=grouper) 

for k, g in groupby(rows, key=grouper): 
    print(k, list(g)) 
相關問題