同时将多个字典中的两个键相加

时间:2018-07-29 10:01:04

标签: python list dictionary sum

我想在一个函数中求和两个不同的变量,但是我希望基于其他多个项求和。

如果我有以下字典x列表:

print(len([iq for iq in os.scandir('PATH')]))

然后我可以使用以下函数基于两个其他变量('var1'和'var2')对单个变量('var3')求和:

x=[{'id':1, 'var1':'a', 'var2':'left', 'var3':0.1, 'var4':1},
   {'id':2, 'var1':'a', 'var2':'right', 'var3':0.1, 'var4':1},
   {'id':2, 'var1':'a', 'var2':'right', 'var3':0.2, 'var4':3},
   {'id':4, 'var1':'b', 'var2':'left', 'var3':0.4, 'var4':4},
   {'id':5, 'var1':'b', 'var2':'right', 'var3':0.1, 'var4':5},
   {'id':5, 'var1':'b', 'var2':'right', 'var3':0.4, 'var4':2}]

但是,我想对多个类别(“ var1”和“ var2”)中的多个变量(“ var3”和“ var4”)求和,以便输出看起来像这样:

from operator import itemgetter
from itertools import groupby

def aggregate_var3_by_var1_and_var2(data):
    my_data= []
    grouper = itemgetter("id", "var1", "var2")
    for key, grp in groupby(sorted(data, key = grouper), grouper):
        temp_dict = dict(zip(["id", "var1", "var2"], key))
        temp_dict["var3"] = sum(item["var3"] for item in grp)
        my_data.append(temp_dict)
    return my_data

my_output = aggregate_var3_by_var1_and_var2(x)

如何使用此方法一次求和多个变量?

4 个答案:

答案 0 :(得分:1)

您的方法的直接扩展,提供了分组键和值键作为参数:

from operator import itemgetter
from itertools import groupby
from itertools import chain

def reducer(ts):
    return map(sum, zip(*ts))

def agg(data, keys, aggfields):
    my_data = []
    getter = itemgetter(*aggfields)
    grouper = itemgetter(*keys)
    for ks, grp in groupby(sorted(data, key=grouper), grouper):
        vs = map(getter, grp)
        kvs = chain(zip(keys,ks), zip(aggfields, reducer(vs)))
        my_data.append(dict(kvs))
    return my_data

在副本中:

In [9]: x=[{'id':1, 'var1':'a', 'var2':'left', 'var3':0.1, 'var4':1},
   ...:    {'id':2, 'var1':'a', 'var2':'right', 'var3':0.1, 'var4':1},
   ...:    {'id':2, 'var1':'a', 'var2':'right', 'var3':0.2, 'var4':3},
   ...:    {'id':4, 'var1':'b', 'var2':'left', 'var3':0.4, 'var4':4},
   ...:    {'id':5, 'var1':'b', 'var2':'right', 'var3':0.1, 'var4':5},
   ...:    {'id':5, 'var1':'b', 'var2':'right', 'var3':0.4, 'var4':2}]

In [10]: agg(x, ['var1','var2'], ['var3','var4'])
Out[10]:
[{'var1': 'a', 'var2': 'left', 'var3': 0.1, 'var4': 1},
 {'var1': 'a', 'var2': 'right', 'var3': 0.30000000000000004, 'var4': 4},
 {'var1': 'b', 'var2': 'left', 'var3': 0.4, 'var4': 4},
 {'var1': 'b', 'var2': 'right', 'var3': 0.5, 'var4': 7}]

这是使用字典进行分组的另一种方法(Counter dicts的默认dict ...)

from collections import Counter, defaultdict
from itertools import chain
from operator import itemgetter

def agg(data, keys, aggfields):

    grouper = defaultdict(Counter)
    pluck_keys = itemgetter(*keys)
    pluck_vals = itemgetter(*aggfields)

    for d in data:
        ctr = grouper[pluck_keys(d)]
        for k, v in zip(aggfields, pluck_vals(d)):
            ctr[k] += v

    return [
        {k:v for k,v in chain(zip(keys, ks), ctr.items())}
        for ks, ctr in grouper.items()
    ]

答案 1 :(得分:0)

您可以使用Pandas作为有效的矢量化解决方案。

itertools.groupby的缺点是,它需要排序[额外的复杂性],而不能实现矢量化的计算[效率低的求和]。

如果您想走循环路线,我建议collections.defaultdict以确保您仍然具有O(n)复杂度。

import pandas as pd

df = pd.DataFrame(x)

res = df.groupby(['id', 'var1', 'var2']).agg({'var3': 'sum', 'var4': 'sum'}).reset_index()

print(res.to_dict('records'))

[{'id': 1, 'var1': 'a', 'var2': 'left', 'var3': 0.1, 'var4': 1},
 {'id': 2, 'var1': 'a', 'var2': 'right', 'var3': 0.3, 'var4': 4},
 {'id': 4, 'var1': 'b', 'var2': 'left', 'var3': 0.4, 'var4': 4},
 {'id': 5, 'var1': 'b', 'var2': 'right', 'var3': 0.5, 'var4': 7}]

答案 2 :(得分:0)

from itertools import groupby
x=[{'id':1, 'var1':'a', 'var2':'left', 'var3':0.1, 'var4':1},
   {'id':2, 'var1':'a', 'var2':'right', 'var3':0.1, 'var4':1},
   {'id':2, 'var1':'a', 'var2':'right', 'var3':0.2, 'var4':3},
   {'id':4, 'var1':'b', 'var2':'left', 'var3':0.4, 'var4':4},
   {'id':5, 'var1':'b', 'var2':'right', 'var3':0.1, 'var4':5},
   {'id':5, 'var1':'b', 'var2':'right', 'var3':0.4, 'var4':2}]

res = []

for key, value in groupby(x, lambda x: x["id"]):
    d = None
    for i in value:
        if not d:
            d = i
        else:
            d["var3"] += i["var3"]
            d["var4"] += i["var4"]
    res.append(d)
print(res)

输出:

[{'id': 1, 'var1': 'a', 'var2': 'left', 'var3': 0.1, 'var4': 1},
 {'id': 2,
  'var1': 'a',
  'var2': 'right',
  'var3': 0.30000000000000004,
  'var4': 4},
 {'id': 4, 'var1': 'b', 'var2': 'left', 'var3': 0.4, 'var4': 4},
 {'id': 5, 'var1': 'b', 'var2': 'right', 'var3': 0.5, 'var4': 7}]

答案 3 :(得分:0)

您可以将collections.defaultdict用于O(n)解决方案。与itertools.groupby相对,这不需要事先排序。

想法是按预定义的group_keys进行分组。然后,使用列表推导来组合defaultdict的键和值。语法{**d1, **d2}用于组合两个字典。

from collections import defaultdict
from operator import itemgetter

d = defaultdict(lambda: defaultdict(int))

group_keys = ['id', 'var1', 'var2']
sum_keys = ['var3', 'var4']

for item in x:
    for key in sum_keys:
        d[itemgetter(*group_keys)(item)][key] += item[key]

res = [{**dict(zip(group_keys, k)), **v} for k, v in d.items()]

print(res)

[{'id': 1, 'var1': 'a', 'var2': 'left', 'var3': 0.1, 'var4': 1},
 {'id': 2, 'var1': 'a', 'var2': 'right', 'var3': 0.3, 'var4': 4},
 {'id': 4, 'var1': 'b', 'var2': 'left', 'var3': 0.4, 'var4': 4},
 {'id': 5, 'var1': 'b', 'var2': 'right', 'var3': 0.5, 'var4': 7}]