Уменьшить по ключу в питоне

Я пытаюсь продумать наиболее эффективный способ сделать это в 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]))

Как мне выполнить эффективное сокращение по «ключу», где ключ здесь может быть первым значением, поэтому окончательный результат будет примерно таким:

[('dog',12,1), ('cat',16,1)]

person mgoldwasser    schedule 29.04.2015    source источник
comment
Вы имели в виду мин (obj1 [2], obj2 [2])   -  person wim    schedule 29.04.2015
comment
хороший улов, спасибо! я исправил это выше   -  person mgoldwasser    schedule 29.04.2015
comment
это похоже на то, что хорошо подходит для панд   -  person Haleemur Ali    schedule 29.04.2015
comment
Привет @mgoldwasser, опоздал на 2 года, но есть еще один способ: stackoverflow.com/a/48343896/5858851. Кстати, я являюсь бывшим коллегой, которым вы меня считаете.   -  person pault    schedule 19.01.2018


Ответы (4)


Если вы хотите использовать свои 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)]
person Stefan Pochmann    schedule 02.05.2015
comment
Могу я узнать, какой у тебя синтаксис/сокращение, которое ты использовал в операторе печати? Похоже, вызов функции, за которым следует цикл for, и переменная, определенная циклом for, передается в вызове функции. уменьшить (my_reduce, group) для _, сгруппировать в groupby (отсортировано (домашние животные), key = itemgetter (0)) - person Lee; 13.12.2017
comment
@Lee Это понимание списка. - person Stefan Pochmann; 13.12.2017

В качестве альтернативы, если у вас установлены 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)]
person Anzel    schedule 29.04.2015
comment
Я согласен :) ... глупый Вим и его пробелы 0-ширины: P - person Joran Beasley; 29.04.2015

Я не думаю, что 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.

person wim    schedule 29.04.2015

если вы действительно хотите использовать сокращение, я думаю, что это работает (он возвращает вам дикт вместо списка, но не так)

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)

Я думаю, что оба других решения лучше, однако

person Joran Beasley    schedule 29.04.2015
comment
Нет, потому что это свернет все в один элемент, но мне нужен один элемент на ключ - person mgoldwasser; 29.04.2015