2015-04-29 11 views
6

Próbuję przemyśleć najskuteczniejszy sposób to zrobić w python.Zmniejszenie o klucz w pytonie

Załóżmy, że mam listę krotek:

[('dog',12,2), ('cat',15,1), ('dog',11,1), ('cat',15,2), ('dog',10,3), ('cat',16,3)] 

I Załóżmy, że mam funkcję, która pobiera dwa z tych krotek i łączy je:

def my_reduce(obj1, obj2): 
    return (obj1[0],max(obj1[1],obj2[1]),min(obj1[2],obj2[2])) 

Jak wykonać efektywne zmniejszenie o "klucz", gdzie kluczem może być tutaj pierwsza wartość, więc końcowy wynik będzie wyglądał następująco:

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

miałeś na myśli min (obj1 [2], obj2 [2]) – wim

+0

dobry połów, dzięki! Naprawiłem to powyżej – mgoldwasser

+2

to wygląda jak coś dobrze pasuje do pandy –

Odpowiedz

5

Jeśli chcesz użyć my_reduce i reduce, można to zrobić w ten sposób . Jest to dość krótki, właściwie:

Przygotowanie:

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

Rozwiązanie:

print [reduce(my_reduce, group) 
     for _, group in groupby(sorted(pets), key=itemgetter(0))] 

wyjściowa:

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

Czy mogę wiedzieć, jaka jest twoja składnia/skrót używany w wyrażeniu print? Wydaje się, że wywołanie funkcji następuje po pętli for, a zmienna zdefiniowana przez pętlę for jest przekazywana w wywołaniu funkcji. zredukuj (my_reduce, grupa) dla _, grupa w groupby (posortowane (zwierzęta domowe), klucz = itemgetter (0)) – Lee

+0

@Lee To jest "zrozumienie listy". –

0

, jeśli naprawdę chcesz używać zmniejszyć myślę, że to działa (to daje dict powrotem zamiast listy, ale 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) 

myślę oba inne rozwiązania są lepsze jednak

+0

Nie, ponieważ to zepsułoby wszystko do jednego elementu, ale chcę jeden element na klucz – mgoldwasser

+0

ok naprawione ... sortowanie ... –

7

nie wiem Myślę, że reduce jest dobrym narzędziem do tej pracy, ponieważ będziesz musiał najpierw użyć itertools lub podobnego, aby zgrupować listę według klucza. W przeciwnym razie będziesz porównywać cats i dogs i całe piekło się rozpadnie!

Zamiast tylko prosta pętla jest w porządku:

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

Następnie, jeśli chcesz oryginalnego formatu powrotem:

>>> output = [(k,) + v for k, v in output.items()] 
>>> output 
[('dog', 12, 1), ('cat', 15, 1)] 

Uwaga To zniszczy kolejność z oryginalnej listy. Jeśli chcesz zachować porządek, najpierw pojawiają się klawisze, zainicjuj wyjściowo za pomocą OrderedDict.

9

Ewentualnie, jeśli masz pandy zainstalowane:

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 

Aby uzyskać oryginalny format:

zip(df.index, *df.values.T) # df is the result above 
Out[14]: [('cat', 16, 1), ('dog', 12, 1)] 
+2

fajne! – wim

+0

Zgadzam się :) .. .głupi wim i jego przestrzenie o szerokości 0: P –