¿Cómo uso itertools.groupby() de Python?
Pregunta
No he podido encontrar una explicación comprensible de cómo utilizar realmente Python. itertools.groupby()
función.Lo que estoy tratando de hacer es esto:
- Tome una lista: en este caso, los hijos de una persona objetivada.
lxml
elemento - Dividirlo en grupos según algunos criterios.
- Luego, repita cada uno de estos grupos por separado.
he revisado la documentación, y los ejemplos, pero he tenido problemas al intentar aplicarlos más allá de una simple lista de números.
Entonces, ¿cómo uso itertools.groupby()
?¿Hay otra técnica que debería utilizar?También se agradecerían sugerencias sobre una buena lectura como "requisito previo".
Solución
NOTA IMPORTANTE: Tienes que ordena tus datos primero.
La parte que no entendí es que en la construcción del 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 utilizar para iterar sobre el grupo definido por esa clave de agrupación.En otras palabras, el groupby
El propio iterador 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á el resultado:
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, things
es una lista de tuplas donde el primer elemento de cada tupla es el grupo al que pertenece el segundo elemento.
El groupby()
La función toma dos argumentos:(1) los datos a agrupar y (2) la función con la que agruparlos.
Aquí, lambda x: x[0]
dice groupby()
para utilizar el primer elemento de cada tupla como clave de agrupación.
en lo anterior for
declaración, groupby
devuelve tres pares (clave, iterador de grupo), una vez para cada clave única.Puede utilizar el iterador devuelto para iterar sobre cada elemento individual de ese grupo.
Aquí hay un ejemplo ligeramente diferente con los mismos datos, usando una lista por 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á el resultado:
animales:oso y pato.
plantas:cactus.
vehículos:lancha rápida y autobús escolar.
Otros consejos
¿Puedes mostrarnos tu código?
El ejemplo de 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, keyfunc es donde va la lógica de su función de criterios y luego groupby()
agrupa los datos.
Debes tener cuidado de ordenar los datos por los criterios antes de llamar groupby
o no funcionará. groupby
El método en realidad simplemente recorre una lista y cada vez que cambia la clave, crea un nuevo grupo.
Un buen truco con groupby es ejecutar la codificación 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.
Editar:Tenga en cuenta que esto es lo que separa itertools.groupby
del SQL GROUP BY
semántica:itertools no ordena (y en general no puede) 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, recogemos más a fondo lo que podría hacer:
# [k for k, g in groupby('AAAABBBCCDAABBB')] --> A B C D A B
# [list(g) for k, g in groupby('AAAABBBCCD')] --> AAAA BBB CC D
groupby
Los objetos producen pares clave-grupo donde el grupo es un generador.
Características
- A.Agrupar elementos consecutivos
- B.Agrupar todas las apariciones de un elemento, dado un iterable ordenado
- C.Especificar 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
- Anagramas (ver cuaderno)
- Binning
- Agrupar números pares e impares
- Agrupar una lista por valores
- Eliminar elementos duplicados
- Encuentra índices de elementos repetidos en una matriz.
- Dividir una matriz en fragmentos de tamaño n
- Encuentra elementos correspondientes entre dos listas.
- Algoritmo de compresión (ver cuaderno)/Codificación de longitud de ejecución
- Agrupar letras por longitud, función clave (ver cuaderno)
- Valores consecutivos por encima de un umbral (ver cuaderno)
- Encuentra 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 estos últimos ejemplos derivan del PyCon de Víctor Terrón. (hablar) (Español), "Kung Fu al amanecer con Itertools".Ver también el groupby
código fuente escrito en c.
Respuesta
# OP: Yes, you can use `groupby`, e.g.
[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 subiterador 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 agrupación: cuando las claves no están ordenadas.En el siguiente ejemplo, los elementos de xx se agrupan por valores en yy.En este caso, primero se genera un conjunto de ceros, seguido de un conjunto de unos y seguido nuevamente de 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 sintaxis list(groupby(...)) no funcionará de la forma deseada.Parece destruir los objetos iteradores internos, por lo que usar
for x in list(groupby(range(10))):
print(list(x[1]))
Producirá:
[]
[]
[]
[]
[]
[]
[]
[]
[]
[9]
En lugar de list(groupby(...)), pruebe [(k, list(g)) for k,g in groupby(...)], o si usa esa sintaxis con frecuencia,
def groupbylist(*args, **kwargs):
return [(k, list(g)) for k, g in groupby(*args, **kwargs)]
y obtenga acceso a la funcionalidad groupby evitando por completo esos molestos iteradores (para datos pequeños).
Me gustaría dar otro ejemplo en el que groupby sin clasificación no funciona.Adaptado del ejemplo de 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 se podría esperar un solo grupo.
@CaptSolo, probé tu ejemplo, pero no funcionó.
from itertools import groupby
[(c,len(list(cs))) for c,cs in groupby('Pedro Manoel')]
Producción:
[('P', 1), ('e', 1), ('d', 1), ('r', 1), ('o', 1), (' ', 1), ('M', 1), ('a', 1), ('n', 1), ('o', 1), ('e', 1), ('l', 1)]
Como puedes ver, hay dos o y dos e, pero se ubicaron en grupos separados.Fue entonces cuando me di cuenta de que necesitabas 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)]
Producción:
[(' ', 1), ('M', 1), ('P', 1), ('a', 1), ('d', 1), ('e', 2), ('l', 1), ('n', 1), ('o', 2), ('r', 1)]
Solo recordando, si la lista no está ordenada, la función groupby no trabajará!
¿Cómo uso itertools.groupby() de Python?
Puedes usar groupby para agrupar cosas sobre las que iterar.Le das a groupby un iterable y un opcional. llave función/invocable mediante la cual verificar los elementos a medida que salen del iterable, y devuelve un iterador que da una doble tupla del resultado de la clave invocable 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).
A continuación se muestra un ejemplo de groupby que utiliza una rutina para agrupar por un recuento; utiliza una clave invocable (en este caso, coroutine.send
) para simplemente escupir el recuento de cuantas iteraciones y un subiterador agrupado de elementos:
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])]
Ordenar y agrupar
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 resultar ú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]}