按 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 或类似工具来按键对列表进行分组。否则你将比较 catsdogs 并且一切都会崩溃!

相反,只需一个简单的循环就可以了:

>>> 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_reducereduce,你可以这样做。实际上,它相当短:

准备:

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)]