我想在一个函数中求和两个不同的变量,但是我希望基于其他多个项求和。
如果我有以下字典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)
如何使用此方法一次求和多个变量?
答案 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}]