Como faço para usar itertools.groupby() do Python?
Pergunta
Não consegui encontrar uma explicação compreensível de como realmente usar o Python itertools.groupby()
função.O que estou tentando fazer é o seguinte:
- Faça uma lista - neste caso, os filhos de um objeto
lxml
elemento - Divida-o em grupos com base em alguns critérios
- Posteriormente, itere cada um desses grupos separadamente.
eu revisei a documentação, e os exemplos, mas tive problemas ao tentar aplicá-los além de uma simples lista de números.
Então, como faço para usar itertools.groupby()
?Existe outra técnica que eu deveria usar?Indicações para uma boa leitura de "pré-requisitos" também seriam apreciadas.
Solução
NOTA IMPORTANTE: Você tem que classifique seus dados primeiro.
A parte que não entendi é que na construção do exemplo
groups = []
uniquekeys = []
for k, g in groupby(data, keyfunc):
groups.append(list(g)) # Store group iterator as a list
uniquekeys.append(k)
k
é a chave de agrupamento atual e g
é um iterador que você pode usar para iterar no grupo definido por essa chave de agrupamento.Em outras palavras, o groupby
o próprio iterador retorna iteradores.
Aqui está um exemplo disso, usando nomes de variáveis mais 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 " "
Isso lhe dará a saída:
Um urso é um animal.
Um pato é um animal.Um cacto é uma planta.
Uma lancha é um veículo.
Um ônibus escolar é um veículo.
Neste exemplo, things
é uma lista de tuplas onde o primeiro item de cada tupla é o grupo ao qual o segundo item pertence.
O groupby()
função leva dois argumentos:(1) os dados para agrupar e (2) a função para agrupá-los.
Aqui, lambda x: x[0]
diz groupby()
para usar o primeiro item em cada tupla como chave de agrupamento.
No acima for
declaração, groupby
retorna três pares (chave, iterador de grupo) - uma vez para cada chave exclusiva.Você pode usar o iterador retornado para iterar cada item individual desse grupo.
Aqui está um exemplo ligeiramente diferente com os mesmos dados, usando uma compreensão de lista:
for key, group in groupby(things, lambda x: x[0]):
listOfThings = " and ".join([thing[1] for thing in group])
print key + "s: " + listOfThings + "."
Isso lhe dará a saída:
animais:urso e pato.
plantas:cacto.
veículos:lancha e ônibus escolar.
Outras dicas
Você pode nos mostrar seu código?
O exemplo na documentação do Python é bastante direto:
groups = []
uniquekeys = []
for k, g in groupby(data, keyfunc):
groups.append(list(g)) # Store group iterator as a list
uniquekeys.append(k)
Então, no seu caso, data é uma lista de nós, keyfunc é para onde vai a lógica da sua função de critérios e então groupby()
agrupa os dados.
Você deve ter cuidado para classificar os dados pelos critérios antes de ligar groupby
ou não funcionará. groupby
na verdade, apenas itera por uma lista e sempre que a chave muda, ele cria um novo grupo.
Um truque bacana com groupby é executar a codificação de comprimento em uma linha:
[(c,len(list(cgen))) for c,cgen in groupby(some_string)]
fornecerá uma lista de 2 tuplas onde o primeiro elemento é o char e o segundo é o número de repetições.
Editar:Observe que é isso que separa itertools.groupby
do SQL GROUP BY
semântica:itertools não classifica (e em geral não pode) classificar o iterador antecipadamente, portanto, grupos com a mesma "chave" não são mesclados.
itertools.groupby
é uma ferramenta para agrupar itens.
De os documentos, coletamos ainda mais o que isso pode fazer:
# [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
objetos produzem pares de grupos-chave onde o grupo é um gerador.
Características
- A.Agrupar itens consecutivos
- B.Agrupe todas as ocorrências de um item, dado um iterável classificado
- C.Especifique como agrupar itens com uma função principal
Comparações
# 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 (veja caderno)
- Armazenamento
- Agrupar números pares e ímpares
- Agrupar uma lista por valores
- Remover elementos duplicados
- Encontre índices de elementos repetidos em uma matriz
- Divida uma matriz em pedaços de tamanho n
- Encontre elementos correspondentes entre duas listas
- Algoritmo de compressão (veja caderno)/Codificação de comprimento de execução
- Agrupando letras por comprimento, função principal (veja caderno)
- Valores consecutivos acima de um limite (veja caderno)
- Encontre intervalos de números em uma lista ou itens contínuos (ver documentos)
- Encontre todas as sequências mais longas relacionadas
- Pegue sequências consecutivas que atendam a uma condição (veja postagem relacionada)
Observação:Vários dos últimos exemplos derivam do PyCon de Víctor Terrón (falar) (Espanhol), "Kung Fu ao amanhecer com Itertools".Veja também o groupby
Código fonte escrito em C.
Resposta
# OP: Yes, you can use `groupby`, e.g.
[do_something(list(g)) for _, g in groupby(lxml_elements, key=criteria_func)]
Outro exemplo:
for key, igroup in itertools.groupby(xrange(12), lambda x: x // 5):
print key, list(igroup)
resulta em
0 [0, 1, 2, 3, 4]
1 [5, 6, 7, 8, 9]
2 [10, 11]
Observe que igroup é um iterador (um subiterador, como a documentação o chama).
Isso é útil para fragmentar um gerador:
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)
Outro exemplo de groupby é quando as chaves não estão ordenadas.No exemplo a seguir, os itens em xx são agrupados por valores em yy.Neste caso, um conjunto de zeros é gerado primeiro, seguido por um conjunto de uns, seguido novamente por um conjunto de zeros.
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])
Produz:
0 [0, 1, 2]
1 [3, 4, 5]
0 [6, 7, 8, 9]
AVISO:
A sintaxe list(groupby(...)) não funcionará da maneira que você pretende.Parece destruir os objetos iteradores internos, então usar
for x in list(groupby(range(10))):
print(list(x[1]))
vai produzir:
[]
[]
[]
[]
[]
[]
[]
[]
[]
[9]
Em vez disso, list(groupby(...)), tente [(k, list(g)) for k,g in groupby(...)], ou se você usa essa sintaxe com frequência,
def groupbylist(*args, **kwargs):
return [(k, list(g)) for k, g in groupby(*args, **kwargs)]
e obtenha acesso à funcionalidade groupby, evitando todos aqueles iteradores incômodos (para pequenos dados).
Gostaria de dar outro exemplo em que groupby sem classificação não funciona.Adaptado do exemplo 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 " "
a saída é
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.
existem dois grupos com veículo, enquanto se poderia esperar apenas um grupo
@CaptSolo, tentei seu exemplo, mas não funcionou.
from itertools import groupby
[(c,len(list(cs))) for c,cs in groupby('Pedro Manoel')]
Saída:
[('P', 1), ('e', 1), ('d', 1), ('r', 1), ('o', 1), (' ', 1), ('M', 1), ('a', 1), ('n', 1), ('o', 1), ('e', 1), ('l', 1)]
Como você pode ver, existem dois O e dois E, mas eles estão em grupos separados.Foi quando percebi que você precisa ordenar a lista passada para a função groupby.Então, o uso correto seria:
name = list('Pedro Manoel')
name.sort()
[(c,len(list(cs))) for c,cs in groupby(name)]
Saída:
[(' ', 1), ('M', 1), ('P', 1), ('a', 1), ('d', 1), ('e', 2), ('l', 1), ('n', 1), ('o', 2), ('r', 1)]
Só lembrando, caso a lista não esteja ordenada, a função groupby não funciona!
Como faço para usar itertools.groupby() do Python?
Você pode usar groupby para agrupar coisas para iterar.Você dá ao groupby um iterável e um opcional chave função/chamável pela qual verificar os itens à medida que saem do iterável e retorna um iterador que fornece duas tuplas do resultado da chave que pode ser chamada e dos itens reais em outro iterável.Da ajuda:
groupby(iterable[, keyfunc]) -> create an iterator which returns
(key, sub-iterator) grouped by each value of key(value).
Aqui está um exemplo de groupby usando uma corrotina para agrupar por uma contagem, ele usa uma chave que pode ser chamada (neste caso, coroutine.send
) para apenas cuspir a contagem de quantas iterações e um 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))
estampas
[(0, [0, 1, 2]), (1, [3, 4, 5]), (2, [6, 7, 8]), (3, [9])]
Classificando e agrupando
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'}
Um exemplo útil que encontrei pode 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)
Exemplo de entrada:14445221
Exemplo de saída:(1,1) (3,4) (1,5) (2,2) (1,1)
Você pode escrever sua própria função 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]}