智能地合并dicts

时间:2013-11-20 13:27:39

标签: python list dictionary

我正在尝试合并某些特定要求的一些说明,这里是一些示例数据

data = [{"nid": 363, "cid": "509cd9aaad4d5", "count": 57, "value": 12.5},
        {"nid": 363, "cid": "509cd9aaad4d5", "count": 57, "value": 22},
        {"nid": 363, "cid": "cd9aaad4d5", "count": 57, "value": 49},
        {"nid": 570, "cid": "cd9aaad4d5", "count": 58, "value": 62},
    ]

我需要合并共享相同nidcid的所有词典,并将value求和,但保留count原样。

所以上面的例子将被返回为(或类似的,我手工完成它可能有一个错误)

[
    {'count': 58, 'value': 62, 'nid': 570, 'cid': 'cd9aaad4d5'},
    {'count': 57, 'value': 34.5, 'nid': 363, 'cid': '509cd9aaad4d5'},
    {'count': 57, 'value': 49, 'nid': 363, 'cid': 'cd9aaad4d5'}
]

到目前为止我的代码尝试很难看,而且我可以做一些指导,

tmp = defaultdict(lambda: defaultdict(lambda: [0, 0]))
for d in data:
    tmp[d["nid"]][d["cid"]][1] = d["count"]
    tmp[d["nid"]][d["cid"]][0] += d["value"]

print tmp

new_data = []

for key in tmp:
    for cid in tmp[key]:
        new_data.append({"nid": key, "cid": cid, "count": tmp[key][cid][1], "value": tmp[key][cid][0]})

print new_data

任何人都可以帮我识别一种更清晰,更智能的合并词典列表的方式。

2 个答案:

答案 0 :(得分:1)

您可以使用复合键来改善您的尝试:

from collections import defaultdict 

tmp = defaultdict(lambda: {'value': 0})
for d in data:
    tmp[d["nid"], d["cid"]]['count'] = d["count"]
    tmp[d["nid"], d["cid"]]['value'] += d["value"]

new_data = [{'nid': nid, 'cid': cid, 'count': v['count'], 'value': v['value']} 
            for (nid, cid), v in tmp.iteritems()]

替代方法是对data进行排序并使用itertools.groupby(),但由于排序费用较高。

答案 1 :(得分:1)

使用pandas

 import pandas as pd
 df = pd.DataFrame(data)
 s1 = df.groupby(['nid', 'cid']).sum().value   # sums of all values
 # assuming counts are the same for each nid/cid tuple
 s2 = df.groupby(['nid', 'cid']).count.first() # first element of counts
 pd.DataFrame({'value' : s1, 'count' : s2})

输出:

nid|cid              | count | value
---+-----------------+-------+------
363|509cd9aaad4d5    | 57    | 34.5
   |cd9aaad4d5       | 57    | 49.0
570|cd9aaad4d5       | 58    | 62.0

如果您不喜欢分层索引,可以展平数据框:

pd.DataFrame({'count' : df2, 'value' :df1}).reset_index()