Python 中的 2 层嵌套字典求和
Summing 2 level of nested dictionaries in Python
我有 2 个具有相似键的嵌套字典变量,每个定义不同的值:
data1 = {
"2010":{
'A':2,
'B':3,
'C':5
},
"2011":{
'A':1,
'B':2,
'C':3
},
"2012":{
'A':1,
'B':2,
'C':4
}
}
data2 = {
"2010":{
'A':4,
'B':4,
'C':5
},
"2011":{
'A':1,
'B':1,
'C':3
},
"2012":{
'A':3,
'B':2,
'C':4
}
}
在我的例子中,我需要根据相同的键对两个字典值求和,所以答案是这样的:
data3 = {
"2010":{
'A':6,
'B':7,
'C':10
},
"2011":{
'A':2,
'B':3,
'C':6
},
"2012":{
'A':4,
'B':4,
'C':8
}
}
我该怎么做?
鉴于两个词典的结构相同,您可以使用词典理解:
data3 = {key:{key2:val1+data2[key][key2] for key2,val1 in subdic.items()} for key,subdic in data1.items()}
在回复中:
>>> {key:{key2:val1+data2[key][key2] for key2,val1 in subdic.items()} for key,subdic in data1.items()}
{'2010': {'B': 7, 'C': 10, 'A': 6}, '2012': {'B': 4, 'C': 8, 'A': 4}, '2011': {'B': 3, 'C': 6, 'A': 2}}
推导式的工作方式如下:在外循环中,我们遍历 data1
的 key,subdic
。因此,在您的情况下,key
是一年,subdic
是那一年的字典(data1
)。
现在,对于这些年中的每一年,我们迭代 subdic
的项目,这里 key2
是 'A'
、'B'
和 'C'
。 val1
是我们在 data1
中为这些键找到的值。我们通过查询 data2[key][key2]
得到另一个值。我们总结这些并为此构建新的词典。
另一个解决方案:)
您还可以使用 zip
to get both data1
and data2
in the same for
loop, and then use collections.Counter
添加每个字典的值。
from collections import Counter
>> {k1: Counter(v1) + Counter(v2) for (k1, v1), (k2, v2) in zip(sorted(data1.items()), sorted(data2.items()))}
{'2011': Counter({'C': 6, 'B': 3, 'A': 2}), '2010': Counter({'C': 10, 'B': 7, 'A': 6}), '2012': Counter({'C': 8, 'A': 4, 'B': 4})}
您将以 Counter
dict 结束,但由于它是 dict
的子类,您仍然可以使用与常规 dict
.
相同的方法
如果您将 dict() 添加到 Max Chrétiens 上面的简短解决方案中,您最终会得到常规词典:
data3 = {k1: dict(Counter(v1) + Counter(v2)) for (k1, v1), (k2, v2) in
zip(data1.items(), data2.items())}
但是,只有当两个词典共享上面已经讨论过的完全相同的键时,这才会正常工作。如果两个词典都没有共享任何键,Willem Van Onsem 的解决方案将不起作用(这将导致错误,而 Max Chrétiens 的解决方案在这种情况下会错误地合并项目)。现在你提到你正在使用 JSON 数据,它总是包含具有相似键的相同结构,所以这不应该构成问题,Max Chrétien 的解决方案应该工作得很好。
如果您确实想确保只使用两个词典(及其子词典)共享的键,则以下方法可行。请注意我是如何将 'X': 111111 作为键值对添加到 2012 子词典和 "1999": { 'Z': 999999 } 作为整个子词典的。
def sum_two_nested_dicts(d1, d2):
dicts = [d1, d2]
d_sum = {}
for topkey in dicts[0]:
if topkey in dicts[1]:
d_sum[topkey] = {}
for key in dicts[0][topkey]:
if key in dicts[1][topkey]:
new_val = sum([d[topkey][key] for d in dicts])
d_sum[topkey][key] = new_val
return d_sum
data1 = {
"2010": {
'A': 2,
'B': 3,
'C': 5
},
"2011": {
'A': 1,
'B': 2,
'C': 3
},
"2012": {
'A': 1,
'B': 2,
'C': 4,
'X': 111111
},
"1999": {
'Z': 999999
}
}
data2 = {
"2010": {
'A': 4,
'B': 4,
'C': 5
},
"2011": {
'A': 1,
'B': 1,
'C': 3
},
"2012": {
'A': 3,
'B': 2,
'C': 4
}
}
data3 = sum_two_nested_dicts(data1, data2)
print(data3)
# different order of arguments
data4 = sum_two_nested_dicts(data2, data1)
print(data4)
# {'2010': {'C': 10, 'A': 6, 'B': 7}, '2012': {'C': 8, 'A': 4, 'B': 4}, '2011': {'C': 6, 'A': 2, 'B': 3}}
# {'2010': {'C': 10, 'A': 6, 'B': 7}, '2012': {'C': 8, 'A': 4, 'B': 4}, '2011': {'C': 6, 'A': 2, 'B': 3}}
我意识到这远远不够简洁和优雅,但无论如何我已经写过了,我post在这里写它以防有人试图实现这个特定的功能。
冗长臃肿的版本,保留未共享keys/values,只是因为我已经写过了...
def sum_nested_dicts(dic1, dic2):
# create list of both dictionaries
dicts = [dic1, dic2]
# create a set of all unique keys from both dictionaries
topkeys = set(sum([list(dic.keys()) for dic in dicts], []))
# this is the merged dictionary to be returned
d_sum = {}
for topkey in topkeys:
# if topkey is shared by both dictionaries
if topkey in dic1 and topkey in dic2:
d_sum[topkey] = {}
keys = set(sum([list(dic[topkey].keys()) for dic in
dicts], []))
for key in keys:
# if key is shared by both subdictionaries
if key in dic1[topkey] and key in dic2[topkey]:
new_val = sum([d[topkey][key] for d in dicts])
d_sum[topkey][key] = new_val
# if key is only contained in one subdictionary
elif key in dic1[topkey]:
d_sum[topkey][key] = dic1[topkey][key]
elif key in dic2[topkey]:
d_sum[topkey][key] = dic2[topkey][key]
# if topkey is only contained in one dictionary
elif topkey in dic1:
d_sum[topkey] = dic1[topkey]
elif topkey in dic2:
d_sum[topkey] = dic2[topkey]
return d_sum
请参阅 Crystal 的解决方案,这是迄今为止 post 编辑的最简洁、最实用的解决方案。
希望对您有所帮助:
data1 = { "2010":{ 'A':2, 'B':3, 'C':5 }, "2011":{ 'A':1, 'B':2, 'C':3 }, "2012":{ 'A':1, 'B':2, 'C':4 } }
data2 = { "2010":{ 'A':4, 'B':4, 'C':5 }, "2011":{ 'A':1, 'B':1, 'C':3 }, "2012":{ 'A':3, 'B':2, 'C':4 } }
data3 = {}
for data in [data1,data2]:
for year in data.keys():
for x,y in data[year].items():
if not year in data3.keys():
data3[year] = {x:y}
else:
if not x in data3[year].keys():
data3[year].update({x:y})
else:
data3[year].update({x:data3[year][x] + y})
print data3
这适用于任意长度的内部和外部词典。
我有 2 个具有相似键的嵌套字典变量,每个定义不同的值:
data1 = {
"2010":{
'A':2,
'B':3,
'C':5
},
"2011":{
'A':1,
'B':2,
'C':3
},
"2012":{
'A':1,
'B':2,
'C':4
}
}
data2 = {
"2010":{
'A':4,
'B':4,
'C':5
},
"2011":{
'A':1,
'B':1,
'C':3
},
"2012":{
'A':3,
'B':2,
'C':4
}
}
在我的例子中,我需要根据相同的键对两个字典值求和,所以答案是这样的:
data3 = {
"2010":{
'A':6,
'B':7,
'C':10
},
"2011":{
'A':2,
'B':3,
'C':6
},
"2012":{
'A':4,
'B':4,
'C':8
}
}
我该怎么做?
鉴于两个词典的结构相同,您可以使用词典理解:
data3 = {key:{key2:val1+data2[key][key2] for key2,val1 in subdic.items()} for key,subdic in data1.items()}
在回复中:
>>> {key:{key2:val1+data2[key][key2] for key2,val1 in subdic.items()} for key,subdic in data1.items()}
{'2010': {'B': 7, 'C': 10, 'A': 6}, '2012': {'B': 4, 'C': 8, 'A': 4}, '2011': {'B': 3, 'C': 6, 'A': 2}}
推导式的工作方式如下:在外循环中,我们遍历 data1
的 key,subdic
。因此,在您的情况下,key
是一年,subdic
是那一年的字典(data1
)。
现在,对于这些年中的每一年,我们迭代 subdic
的项目,这里 key2
是 'A'
、'B'
和 'C'
。 val1
是我们在 data1
中为这些键找到的值。我们通过查询 data2[key][key2]
得到另一个值。我们总结这些并为此构建新的词典。
另一个解决方案:)
您还可以使用 zip
to get both data1
and data2
in the same for
loop, and then use collections.Counter
添加每个字典的值。
from collections import Counter
>> {k1: Counter(v1) + Counter(v2) for (k1, v1), (k2, v2) in zip(sorted(data1.items()), sorted(data2.items()))}
{'2011': Counter({'C': 6, 'B': 3, 'A': 2}), '2010': Counter({'C': 10, 'B': 7, 'A': 6}), '2012': Counter({'C': 8, 'A': 4, 'B': 4})}
您将以 Counter
dict 结束,但由于它是 dict
的子类,您仍然可以使用与常规 dict
.
如果您将 dict() 添加到 Max Chrétiens 上面的简短解决方案中,您最终会得到常规词典:
data3 = {k1: dict(Counter(v1) + Counter(v2)) for (k1, v1), (k2, v2) in
zip(data1.items(), data2.items())}
但是,只有当两个词典共享上面已经讨论过的完全相同的键时,这才会正常工作。如果两个词典都没有共享任何键,Willem Van Onsem 的解决方案将不起作用(这将导致错误,而 Max Chrétiens 的解决方案在这种情况下会错误地合并项目)。现在你提到你正在使用 JSON 数据,它总是包含具有相似键的相同结构,所以这不应该构成问题,Max Chrétien 的解决方案应该工作得很好。
如果您确实想确保只使用两个词典(及其子词典)共享的键,则以下方法可行。请注意我是如何将 'X': 111111 作为键值对添加到 2012 子词典和 "1999": { 'Z': 999999 } 作为整个子词典的。
def sum_two_nested_dicts(d1, d2):
dicts = [d1, d2]
d_sum = {}
for topkey in dicts[0]:
if topkey in dicts[1]:
d_sum[topkey] = {}
for key in dicts[0][topkey]:
if key in dicts[1][topkey]:
new_val = sum([d[topkey][key] for d in dicts])
d_sum[topkey][key] = new_val
return d_sum
data1 = {
"2010": {
'A': 2,
'B': 3,
'C': 5
},
"2011": {
'A': 1,
'B': 2,
'C': 3
},
"2012": {
'A': 1,
'B': 2,
'C': 4,
'X': 111111
},
"1999": {
'Z': 999999
}
}
data2 = {
"2010": {
'A': 4,
'B': 4,
'C': 5
},
"2011": {
'A': 1,
'B': 1,
'C': 3
},
"2012": {
'A': 3,
'B': 2,
'C': 4
}
}
data3 = sum_two_nested_dicts(data1, data2)
print(data3)
# different order of arguments
data4 = sum_two_nested_dicts(data2, data1)
print(data4)
# {'2010': {'C': 10, 'A': 6, 'B': 7}, '2012': {'C': 8, 'A': 4, 'B': 4}, '2011': {'C': 6, 'A': 2, 'B': 3}}
# {'2010': {'C': 10, 'A': 6, 'B': 7}, '2012': {'C': 8, 'A': 4, 'B': 4}, '2011': {'C': 6, 'A': 2, 'B': 3}}
我意识到这远远不够简洁和优雅,但无论如何我已经写过了,我post在这里写它以防有人试图实现这个特定的功能。
冗长臃肿的版本,保留未共享keys/values,只是因为我已经写过了...
def sum_nested_dicts(dic1, dic2):
# create list of both dictionaries
dicts = [dic1, dic2]
# create a set of all unique keys from both dictionaries
topkeys = set(sum([list(dic.keys()) for dic in dicts], []))
# this is the merged dictionary to be returned
d_sum = {}
for topkey in topkeys:
# if topkey is shared by both dictionaries
if topkey in dic1 and topkey in dic2:
d_sum[topkey] = {}
keys = set(sum([list(dic[topkey].keys()) for dic in
dicts], []))
for key in keys:
# if key is shared by both subdictionaries
if key in dic1[topkey] and key in dic2[topkey]:
new_val = sum([d[topkey][key] for d in dicts])
d_sum[topkey][key] = new_val
# if key is only contained in one subdictionary
elif key in dic1[topkey]:
d_sum[topkey][key] = dic1[topkey][key]
elif key in dic2[topkey]:
d_sum[topkey][key] = dic2[topkey][key]
# if topkey is only contained in one dictionary
elif topkey in dic1:
d_sum[topkey] = dic1[topkey]
elif topkey in dic2:
d_sum[topkey] = dic2[topkey]
return d_sum
请参阅 Crystal 的解决方案,这是迄今为止 post 编辑的最简洁、最实用的解决方案。
希望对您有所帮助:
data1 = { "2010":{ 'A':2, 'B':3, 'C':5 }, "2011":{ 'A':1, 'B':2, 'C':3 }, "2012":{ 'A':1, 'B':2, 'C':4 } }
data2 = { "2010":{ 'A':4, 'B':4, 'C':5 }, "2011":{ 'A':1, 'B':1, 'C':3 }, "2012":{ 'A':3, 'B':2, 'C':4 } }
data3 = {}
for data in [data1,data2]:
for year in data.keys():
for x,y in data[year].items():
if not year in data3.keys():
data3[year] = {x:y}
else:
if not x in data3[year].keys():
data3[year].update({x:y})
else:
data3[year].update({x:data3[year][x] + y})
print data3
这适用于任意长度的内部和外部词典。