我有一个字典列表,字典中的每个值都是一个四元素列表:
my_dict=[
{
'prop1': [1, 2, 3, 4],
'prop2': [1, 1, 0, 0]
},
{
'prop1': [2, 3, 3, 1],
'prop3': [1, 1, 0, 0]
}
]
可不可以不写显式迭代就求和?
我想要得到:
my_dict_sum={
'prop1': [3, 5, 6, 5],
'prop2': [1, 1, 0, 0],
'prop3': [1, 1, 0, 0]
}
UPD:类似这样的工作,但我想知道如何使用map
或zip
或functools
来做同样的事情,而不编写两层迭代:
my_dict_sum = {}
for val in my_dict:
for key,counts in val.items():
if key in my_dict_sum :
sum_dict[key] = list(map(lambda x,y: x+y, my_dict_sum[key], counts))
else:
my_dict_sum[key] = counts
EDIT:
这是一个没有显式循环的可怕的单行程序。我不知道你为什么想这么做。你这样做。假设您不将理解计算为显式循环,尽管存在单词"for"
from functools import reduce
output = {k: reduce(
lambda a, b:
map(lambda d, e:
d+e, a, b[k] if k in b else [0,0,0,0]
),
dicts,
[0, 0, 0, 0]
)
for k in reduce(
lambda s, b:
s | set(b.keys()),
dicts, set()
)
}
这是一个带有显式循环的简单版本,我觉得比你的代码清晰得多,所以也许这就足够了。
from collections import defaultdict
dicts = [
{
'prop1': [1, 2, 3, 4],
'prop2': [1, 1, 0, 0]
},
{
'prop1': [2, 3, 3, 1],
'prop3': [1, 1, 0, 0]
}
]
output = defaultdict(lambda: [0, 0, 0, 0])
for d in dicts:
for k, l in d.items():
for i, v in enumerate(l):
output[k][i] += v
注意,只有当所有列表都恰好有四个项目时,它才会起作用。要处理不知道列表中有多少项的情况,可以使用稍微复杂一点的版本:
output = defaultdict(list)
for d in dicts:
for k, l in d.items():
for i, v in enumerate(l):
if i >= len(output[k]):
output[k].append(v)
else:
output[k][i] += v
您可以作为使用reduce
和map
以及一堆lambda的一行代码来完成此操作,但这将是一个难以理解的混乱。如果你担心你的代码中有一堆循环,那就把它放在一个函数中,然后调用这个函数。
考虑到这不是要找到最显式的,或更python化的,或最高效的方法,但您只是好奇它是否可以在任何其他替代方法中实现,这里有一个简短的技巧,没有任何提及for
循环:
from itertools import chain
d = {}
items_chain = chain.from_iterable(map(lambda d: list(d.items()), my_dicts))
list(map(lambda t, d=d: d.update([t]) if t[0] not in d else
d.update([(t[0], list(map(sum, zip(d[t[0]], t[1]))))]), items_chain))
print(d)
{'prop1': [3, 5, 6, 5], 'prop2': [1, 1, 0, 0], 'prop3': [1, 1, 0, 0]}
更新:也许更好的方法是使用内置的UserDict类定义自己的字典,如下所示:
from collections import UserDict
class mydict(UserDict):
def __add__(self, other):
for k, v in other.items():
if k in self.data:
self.data[k] = list(map(sum, zip_longest(self.data[k], other[k], fillvalue=0)))
else:
self.data[k] = other.data[k]
return self
def __radd__(self, other):
return self if other == 0 else self.__add__(other)
和现在要转换为新的mydict类的输入列表:(如果数据最初创建为mydict而不是dict,则可以跳过此步骤)
new_dict = [mydict(v) for v in my_dict]
现在你唯一需要做的就是求和:
sum(new_dict)
results:
{'prop1': [3, 5, 6, 5], 'prop2': [1, 1, 0, 0], 'prop3': [1, 1, 0, 0]}
以前的解决方案:
你可以这样做:
from itertools import zip_longest
from collections import defaultdict
out = defaultdict(list)
for line in my_dict:
for key, val in line.items():
out[key] = list(map(sum, zip_longest(val, out[key], fillvalue=0)))
和结果:
defaultdict(list,
{'prop1': [3, 5, 6, 5],
'prop2': [1, 1, 0, 0],
'prop3': [1, 1, 0, 0]})