2010-08-09 13 views
7

Para ilustrar, comienzo con una lista de 2-tuplas:Cómo usar itertools.groupby cuando el valor de la clave está en los elementos de iterable?

import itertools 
import operator 

raw = [(1, "one"), 
     (2, "two"), 
     (1, "one"), 
     (3, "three"), 
     (2, "two")] 

for key, grp in itertools.groupby(raw, key=lambda item: item[0]): 
    print key, list(grp).pop()[1] 

rendimientos:

1 one 
2 two 
1 one 
3 three 
2 two 

En un intento de investigar por qué:

for key, grp in itertools.groupby(raw, key=lambda item: item[0]): 
    print key, list(grp) 

# ---- OUTPUT ---- 
1 [(1, 'one')] 
2 [(2, 'two')] 
1 [(1, 'one')] 
3 [(3, 'three')] 
2 [(2, 'two')] 

Incluso esto me dará la misma salida:

for key, grp in itertools.groupby(raw, key=operator.itemgetter(0)): 
    print key, list(grp) 

quiero conseguir algo como:

1 one, one 
2 two, two 
3 three 

estoy pensando que esto se debe a que la clave está dentro de la tupla dentro de la lista, cuando en realidad la tupla se movió alrededor como uno. ¿Hay alguna manera de llegar a mi salida deseada? Tal vez groupby() no es adecuado para esta tarea?

Respuesta

9

groupby clústeres consecutivos elementos del iterable que tienen la misma clave. Para producir la salida que desea, primero debe ordenar raw.

for key, grp in itertools.groupby(sorted(raw), key=operator.itemgetter(0)): 
    print key, map(operator.itemgetter(1), grp) 

# 1 ['one', 'one'] 
# 2 ['two', 'two'] 
# 3 ['three'] 
+0

pensé 'grp' es un objeto' itertool._grouper'. ¿Qué otros tipos de acciones 'integradas' puedo hacer con un '_grouper'? Veo que lo trataste como un "iterable" también? ¡Ordenado! – Kit

+0

@Kit: Creo que el principal hecho útil sobre 'grp' es que es un' iterable'. Hasta que lo mencionó, no sabía que era un objeto 'itertools._grouper'. Esto parece ser un buen ejemplo de la conveniencia de escribir patos. No necesitamos saber el tipo de 'grp', solo que implementa la interfaz' iterable'. – unutbu

+0

+1 para 'itemgetter' – Krastanov

2

Desde el docs:

El funcionamiento de GroupBy() es similar al filtro uniq en Unix. Es genera un salto o un nuevo grupo cada vez que el valor de la función de la tecla cambia (por lo que suele ser necesario haber ordenado los datos usando la misma función de tecla). Ese comportamiento difiere del GROUP BY de SQL que agrega elementos comunes independientemente de su orden de entrada.

Puesto que usted está ordenando las tuplas lexicográfico de todos modos, sólo puede llamar a sorted:

for key, grp in itertools.groupby(sorted(raw), key = operator.itemgetter(0)): 
    print(key, list(map(operator.itemgetter(1), list(grp)))) 
+6

Quitar los espacios alrededor de los paréntesis me haría sentir abrigado y borroso por dentro;) –

+1

Soy un creyente en \ t \ n \ n, el Señor de Whitespace.Me dice que PEP-8 está equivocado, ¡y que el mundo necesita más espacios en blanco! – katrielalex

6

Creo que una forma más limpia para conseguir el resultado deseado es la siguiente.

>>> from collections import defaultdict 
>>> d=defaultdict(list) 
>>> for k,v in raw: 
... d[k].append(v) 
... 
>>> for k,v in sorted(d.items()): 
... print k, v 
... 
1 ['one', 'one'] 
2 ['two', 'two'] 
3 ['three'] 

construcción d es O (n), y ahora sorted() es un poco más de las claves únicas en lugar de todo el conjunto de datos

Cuestiones relacionadas