¿Cómo uso itertools.groupby () de Python?

No he podido encontrar una explicación comprensible de cómo utilizar la función itertools.groupby() Python. Lo que estoy tratando de hacer es esto:

  • Tome una lista: en este caso, los hijos de un elemento lxml objetivado
  • Divídelo en grupos según algunos criterios.
  • Luego, itera más tarde sobre cada uno de estos grupos por separado.

He revisado la documentación y los ejemplos , pero he tenido problemas tratando de aplicarlos más allá de una simple lista de números.

Entonces, ¿cómo uso de itertools.groupby() ? ¿Hay otra técnica que debería estar usando? También se agradecerán los indicadores de una buena lectura de “requisitos previos”.

NOTA IMPORTANTE: Primero debes ordenar tus datos .


La parte que no entendí es que en la construcción de ejemplo

 groups = [] uniquekeys = [] for k, g in groupby(data, keyfunc): groups.append(list(g)) # Store group iterator as a list uniquekeys.append(k) 

k es la clave de agrupación actual, y g es un iterador que puede usar para iterar sobre el grupo definido por esa clave de agrupación. En otras palabras, el iterador groupby mismo devuelve iteradores.

Aquí hay un ejemplo de eso, usando nombres de variables más claros:

 from itertools import groupby things = [("animal", "bear"), ("animal", "duck"), ("plant", "cactus"), ("vehicle", "speed boat"), ("vehicle", "school bus")] for key, group in groupby(things, lambda x: x[0]): for thing in group: print "A %s is a %s." % (thing[1], key) print " " 

Esto le dará la salida:

Un oso es un animal.
Un pato es un animal.

Un cactus es una planta.

Una lancha rápida es un vehículo.
Un autobús escolar es un vehículo.

En este ejemplo, las things son una lista de tuplas donde el primer elemento de cada tupla es el grupo al que pertenece el segundo elemento.

La función groupby() toma dos argumentos: (1) los datos para agrupar y (2) la función para groupby() .

Aquí, lambda x: x[0] le dice a groupby() que use el primer elemento de cada tupla como clave de agrupación.

En lo anterior for statement, groupby devuelve tres pares (clave, iterador de grupo), una vez por cada clave única. Puede usar el iterador devuelto para iterar sobre cada elemento individual en ese grupo.

Aquí hay un ejemplo ligeramente diferente con los mismos datos, utilizando una lista de comprensión:

 for key, group in groupby(things, lambda x: x[0]): listOfThings = " and ".join([thing[1] for thing in group]) print key + "s: " + listOfThings + "." 

Esto le dará la salida:

Animales: oso y pato.
Plantas: Cactus.
Vehículos: lancha rápida y autobús escolar.

¿Nos puedes mostrar tu código?

El ejemplo en los documentos de Python es bastante sencillo:

 groups = [] uniquekeys = [] for k, g in groupby(data, keyfunc): groups.append(list(g)) # Store group iterator as a list uniquekeys.append(k) 

Entonces, en su caso, los datos son una lista de nodos, la función clave es donde va la lógica de su función de criterios y luego groupby() agrupa los datos.

Debe tener cuidado de ordenar los datos según el criterio antes de llamar a groupby o no funcionará. groupby método groupby realidad solo itera a través de una lista y cada vez que la clave cambia crea un nuevo grupo.

Un truco nuevo con groupby es ejecutar la encoding de longitud en una línea:

 [(c,len(list(cgen))) for c,cgen in groupby(some_string)] 

le dará una lista de 2 tuplas donde el primer elemento es el carácter y el segundo es el número de repeticiones.

Edición: tenga en cuenta que esto es lo que separa itertools.groupby de la semántica GROUP BY : itertools no (y en general no puede) ordenar el iterador por adelantado, por lo que los grupos con la misma “clave” no se fusionan.

itertools.groupby es una herramienta para agrupar elementos.

De los documentos , adivinamos más de lo que podría hacer:

# [k for k, g in groupby('AAAABBBCCDAABBB')] --> ABCDAB

# [list(g) for k, g in groupby('AAAABBBCCD')] --> AAAA BBB CC D

groupby objetos groupby generan pares de grupos de claves donde el grupo es un generador.

Caracteristicas

  • A. Agrupar elementos consecutivos juntos
  • B. Agrupe todas las apariciones de un elemento, dado un iterable ordenado
  • C. Especifique cómo agrupar elementos con una función clave

Comparaciones

 # Define a printer for comparing outputs >>> def print_groupby(iterable, key=None): ... for k, g in it.groupby(iterable, key): ... print("key: '{}'--> group: {}".format(k, list(g))) 

 # Feature A: group consecutive occurrences >>> print_groupby("BCAACACAADBBB") key: 'B'--> group: ['B'] key: 'C'--> group: ['C'] key: 'A'--> group: ['A', 'A'] key: 'C'--> group: ['C'] key: 'A'--> group: ['A'] key: 'C'--> group: ['C'] key: 'A'--> group: ['A', 'A'] key: 'D'--> group: ['D'] key: 'B'--> group: ['B', 'B', 'B'] # Feature B: group all occurrences >>> print_groupby(sorted("BCAACACAADBBB")) key: 'A'--> group: ['A', 'A', 'A', 'A', 'A'] key: 'B'--> group: ['B', 'B', 'B', 'B'] key: 'C'--> group: ['C', 'C', 'C'] key: 'D'--> group: ['D'] # Feature C: group by a key function >>> key = lambda x: x.islower() >>> print_groupby(sorted("bCAaCacAADBbB"), key) key: 'False'--> group: ['A', 'A', 'A', 'B', 'B', 'C', 'C', 'D'] key: 'True'--> group: ['a', 'a', 'b', 'b', 'c'] 

Usos

  • Anagtwigs ( ver cuaderno )
  • Binning
  • Grupo de números impares y pares
  • Agrupar una lista por valores
  • Eliminar elementos duplicados
  • Encuentra índices de elementos repetidos en una matriz
  • Dividir una matriz en trozos de tamaño n
  • Encuentra elementos correspondientes entre dos listas
  • Algoritmo de compresión ( ver cuaderno ) / Ejecutar encoding de longitud
  • Agrupar letras por longitud, función clave ( ver cuaderno )
  • Valores consecutivos sobre un umbral ( ver cuaderno )
  • Encuentre rangos de números en una lista o elementos continuos (ver documentos )
  • Encuentra todas las secuencias más largas relacionadas
  • Tomar secuencias consecutivas que cumplan una condición ( ver publicación relacionada )

Nota: Varios de los últimos ejemplos se derivan de PyCon (conversación) (en español) de Víctor Terrón, “Kung Fu at Dawn with Itertools”. Véase también el código fuente groupby escrito en C.


Respuesta

 # OP: Yes, you can use `groupby`, eg [do_something(list(g)) for _, g in groupby(lxml_elements, key=criteria_func)] 

Otro ejemplo:

 for key, igroup in itertools.groupby(xrange(12), lambda x: x // 5): print key, list(igroup) 

resultados en

 0 [0, 1, 2, 3, 4] 1 [5, 6, 7, 8, 9] 2 [10, 11] 

Tenga en cuenta que igroup es un iterador (un sub-iterador como lo llama la documentación).

Esto es útil para fragmentar un generador:

 def chunker(items, chunk_size): '''Group items in chunks of chunk_size''' for _key, group in itertools.groupby(enumerate(items), lambda x: x[0] // chunk_size): yield (g[1] for g in group) with open('file.txt') as fobj: for chunk in chunker(fobj): process(chunk) 

Otro ejemplo de groupby – cuando las claves no están ordenadas. En el siguiente ejemplo, los elementos en xx se agrupan por valores en yy. En este caso, primero se genera un conjunto de ceros, seguido de un conjunto de ceros, seguido de nuevo por un conjunto de ceros.

 xx = range(10) yy = [0, 0, 0, 1, 1, 1, 0, 0, 0, 0] for group in itertools.groupby(iter(xx), lambda x: yy[x]): print group[0], list(group[1]) 

Produce:

 0 [0, 1, 2] 1 [3, 4, 5] 0 [6, 7, 8, 9] 

ADVERTENCIA:

La lista de syntax (groupby (…)) no funcionará de la forma que pretendía. Parece destruir los objetos iteradores internos, por lo que usar

 for x in list(groupby(range(10))): print(list(x[1])) 

Producirá:

 [] [] [] [] [] [] [] [] [] [9] 

En su lugar, de list (groupby (…)), intente [(k, list (g)) para k, g en groupby (…)], o si usa esa syntax a menudo,

 def groupbylist(*args, **kwargs): return [(k, list(g)) for k, g in groupby(*args, **kwargs)] 

y obtenga acceso a la funcionalidad groupby evitando esos iteradores molestos (para datos pequeños) todos juntos.

Me gustaría dar otro ejemplo donde groupby sin orden no está funcionando. Adaptado del ejemplo por James Sulak.

 from itertools import groupby things = [("vehicle", "bear"), ("animal", "duck"), ("animal", "cactus"), ("vehicle", "speed boat"), ("vehicle", "school bus")] for key, group in groupby(things, lambda x: x[0]): for thing in group: print "A %s is a %s." % (thing[1], key) print " " 

la salida es

 A bear is a vehicle. A duck is a animal. A cactus is a animal. A speed boat is a vehicle. A school bus is a vehicle. 

Hay dos grupos con vehículo, mientras que uno podría esperar solo un grupo.

@CaptSolo, probé tu ejemplo, pero no funcionó.

 from itertools import groupby [(c,len(list(cs))) for c,cs in groupby('Pedro Manoel')] 

Salida:

 [('P', 1), ('e', 1), ('d', 1), ('r', 1), ('o', 1), (' ', 1), ('M', 1), ('a', 1), ('n', 1), ('o', 1), ('e', 1), ('l', 1)] 

Como puede ver, hay dos oes y dos e, pero se dividieron en grupos separados. Fue entonces cuando me di cuenta de que necesita ordenar la lista pasada a la función groupby. Entonces, el uso correcto sería:

 name = list('Pedro Manoel') name.sort() [(c,len(list(cs))) for c,cs in groupby(name)] 

Salida:

 [(' ', 1), ('M', 1), ('P', 1), ('a', 1), ('d', 1), ('e', 2), ('l', 1), ('n', 1), ('o', 2), ('r', 1)] 

Solo recuerde, si la lista no está ordenada, ¡la función groupby no funcionará !

¿Cómo uso itertools.groupby () de Python?

Puedes usar groupby para agrupar cosas para iterar sobre. Le da al grupo una función iterable, y una tecla opcional / llamable por medio de la cual puede verificar los elementos a medida que salen de la iterable, y devuelve un iterador que proporciona una doble tupla del resultado de la clave que se puede llamar y los elementos reales en otro iterable. De la ayuda:

 groupby(iterable[, keyfunc]) -> create an iterator which returns (key, sub-iterator) grouped by each value of key(value). 

Aquí hay un ejemplo de groupby usando una coroutine para agrupar por un conteo, usa una clave que se puede llamar (en este caso, coroutine.send ) para simplemente escupir el conteo para muchas iteraciones y un sub-iterador de elementos agrupados:

 import itertools def grouper(iterable, n): def coroutine(n): yield # queue up coroutine for i in itertools.count(): for j in range(n): yield i groups = coroutine(n) next(groups) # queue up coroutine for c, objs in itertools.groupby(iterable, groups.send): yield c, list(objs) # or instead of materializing a list of objs, just: # return itertools.groupby(iterable, groups.send) list(grouper(range(10), 3)) 

huellas dactilares

 [(0, [0, 1, 2]), (1, [3, 4, 5]), (2, [6, 7, 8]), (3, [9])] 

Clasificación y groupby

 from itertools import groupby val = [{'name': 'satyajit', 'address': 'btm', 'pin': 560076}, {'name': 'Mukul', 'address': 'Silk board', 'pin': 560078}, {'name': 'Preetam', 'address': 'btm', 'pin': 560076}] for pin, list_data in groupby(sorted(val, key=lambda k: k['pin']),lambda x: x['pin']): ... print pin ... for rec in list_data: ... print rec ... o/p: 560076 {'name': 'satyajit', 'pin': 560076, 'address': 'btm'} {'name': 'Preetam', 'pin': 560076, 'address': 'btm'} 560078 {'name': 'Mukul', 'pin': 560078, 'address': 'Silk board'} 

Un ejemplo útil que encontré puede ser útil:

 from itertools import groupby #user input myinput = input() #creating empty list to store output myoutput = [] for k,g in groupby(myinput): myoutput.append((len(list(g)),int(k))) print(*myoutput) 

Entrada de muestra: 14445221

Salida de muestra: (1,1) (3,4) (1,5) (2,2) (1,1)

Puedes escribir tu propia función groupby:

  def groupby(data): kv = {} for k,v in data: if k not in kv: kv[k]=[v] else: kv[k].append(v) return kv Run on ipython: In [10]: data = [('a', 1), ('b',2),('a',2)] In [11]: groupby(data) Out[11]: {'a': [1, 2], 'b': [2]}