按 python 键减少
Reduce by key in python
我正在尝试在 python 中思考最有效的方法。
假设我有一个元组列表:
[('dog',12,2), ('cat',15,1), ('dog',11,1), ('cat',15,2), ('dog',10,3), ('cat',16,3)]
假设我有一个函数接受其中两个元组并将它们组合起来:
def my_reduce(obj1, obj2):
return (obj1[0],max(obj1[1],obj2[1]),min(obj1[2],obj2[2]))
如何通过 'key' 执行有效的归约,其中此处的键可能是第一个值,因此最终结果将类似于:
[('dog',12,1), ('cat',16,1)]
如果你真的想使用 reduce 我认为这是可行的(它给你一个 dict 而不是一个列表但是 meh)
def my_reduce(obj1, obj2):
if not isinstance(obj1,dict):
return reduce(my_reduce,[{},obj1,obj2])
try:
obj1[obj2[0]] = max(obj1[obj2[0]][0],obj2[1]),min(obj1[obj2[0]][1],obj2[2])
except KeyError:
obj1[obj2[0]] = obj2[1:]
return obj1
my_list = [('dog',12,2), ('cat',15,1), ('dog',11,1), ('cat',15,2), ('dog',10,3), ('cat',16,3)]
print reduce(my_reduce,my_list)
不过我认为其他两个解决方案都更好
我认为 reduce
不是完成这项工作的好工具,因为您必须首先使用 itertools 或类似工具来按键对列表进行分组。否则你将比较 cats
和 dogs
并且一切都会崩溃!
相反,只需一个简单的循环就可以了:
>>> my_list = [('dog',12,2), ('cat',15,1), ('dog',11,1), ('cat',15,2)]
>>> output = {}
>>> for animal, high, low in my_list:
... try:
... prev_high, prev_low = output[animal]
... except KeyError:
... output[animal] = high, low
... else:
... output[animal] = max(prev_high, high), min(prev_low, low)
那么如果你想恢复原来的格式:
>>> output = [(k,) + v for k, v in output.items()]
>>> output
[('dog', 12, 1), ('cat', 15, 1)]
注意这会破坏原始列表的顺序。如果要保留键首次出现的顺序,请改为使用 OrderedDict
初始化输出。
或者,如果您安装了 pandas:
import pandas as pd
l = [('dog',12,2), ('cat',15,1), ('dog',11,1), ('cat',15,2), ('dog',10,3), ('cat',16,3)]
pd.DataFrame(data=l, columns=['animal', 'm', 'n']).groupby('animal').agg({'m':'max', 'n':'min'})
Out[6]:
m n
animal
cat 16 1
dog 12 1
获取原始格式:
zip(df.index, *df.values.T) # df is the result above
Out[14]: [('cat', 16, 1), ('dog', 12, 1)]
如果你想使用你的my_reduce
和reduce
,你可以这样做。实际上,它相当短:
准备:
from itertools import groupby
from operator import itemgetter
pets = [('dog',12,2), ('cat',15,1), ('dog',11,1), ('cat',15,2), ('dog',10,3), ('cat',16,3)]
def my_reduce(obj1, obj2):
return (obj1[0],max(obj1[1],obj2[1]),min(obj1[2],obj2[2]))
解决方案:
print [reduce(my_reduce, group)
for _, group in groupby(sorted(pets), key=itemgetter(0))]
输出:
[('cat', 16, 1), ('dog', 12, 1)]
我正在尝试在 python 中思考最有效的方法。
假设我有一个元组列表:
[('dog',12,2), ('cat',15,1), ('dog',11,1), ('cat',15,2), ('dog',10,3), ('cat',16,3)]
假设我有一个函数接受其中两个元组并将它们组合起来:
def my_reduce(obj1, obj2):
return (obj1[0],max(obj1[1],obj2[1]),min(obj1[2],obj2[2]))
如何通过 'key' 执行有效的归约,其中此处的键可能是第一个值,因此最终结果将类似于:
[('dog',12,1), ('cat',16,1)]
如果你真的想使用 reduce 我认为这是可行的(它给你一个 dict 而不是一个列表但是 meh)
def my_reduce(obj1, obj2):
if not isinstance(obj1,dict):
return reduce(my_reduce,[{},obj1,obj2])
try:
obj1[obj2[0]] = max(obj1[obj2[0]][0],obj2[1]),min(obj1[obj2[0]][1],obj2[2])
except KeyError:
obj1[obj2[0]] = obj2[1:]
return obj1
my_list = [('dog',12,2), ('cat',15,1), ('dog',11,1), ('cat',15,2), ('dog',10,3), ('cat',16,3)]
print reduce(my_reduce,my_list)
不过我认为其他两个解决方案都更好
我认为 reduce
不是完成这项工作的好工具,因为您必须首先使用 itertools 或类似工具来按键对列表进行分组。否则你将比较 cats
和 dogs
并且一切都会崩溃!
相反,只需一个简单的循环就可以了:
>>> my_list = [('dog',12,2), ('cat',15,1), ('dog',11,1), ('cat',15,2)]
>>> output = {}
>>> for animal, high, low in my_list:
... try:
... prev_high, prev_low = output[animal]
... except KeyError:
... output[animal] = high, low
... else:
... output[animal] = max(prev_high, high), min(prev_low, low)
那么如果你想恢复原来的格式:
>>> output = [(k,) + v for k, v in output.items()]
>>> output
[('dog', 12, 1), ('cat', 15, 1)]
注意这会破坏原始列表的顺序。如果要保留键首次出现的顺序,请改为使用 OrderedDict
初始化输出。
或者,如果您安装了 pandas:
import pandas as pd
l = [('dog',12,2), ('cat',15,1), ('dog',11,1), ('cat',15,2), ('dog',10,3), ('cat',16,3)]
pd.DataFrame(data=l, columns=['animal', 'm', 'n']).groupby('animal').agg({'m':'max', 'n':'min'})
Out[6]:
m n
animal
cat 16 1
dog 12 1
获取原始格式:
zip(df.index, *df.values.T) # df is the result above
Out[14]: [('cat', 16, 1), ('dog', 12, 1)]
如果你想使用你的my_reduce
和reduce
,你可以这样做。实际上,它相当短:
准备:
from itertools import groupby
from operator import itemgetter
pets = [('dog',12,2), ('cat',15,1), ('dog',11,1), ('cat',15,2), ('dog',10,3), ('cat',16,3)]
def my_reduce(obj1, obj2):
return (obj1[0],max(obj1[1],obj2[1]),min(obj1[2],obj2[2]))
解决方案:
print [reduce(my_reduce, group)
for _, group in groupby(sorted(pets), key=itemgetter(0))]
输出:
[('cat', 16, 1), ('dog', 12, 1)]