itertools.groupby() agrupa elementos consecutivos que produzem a mesma chave. Ele trabalha de forma preguiçosa, consome o iterável apenas conforme necessário e pode processar grandes fluxos sem carregar todos os dados na memória. Porém, seu comportamento é diferente de um GROUP BY de banco de dados: valores iguais que não estejam adjacentes formam grupos separados.
Neste guia, você aprenderá a ordenar dados antes do agrupamento, consumir os subiteradores corretamente, agregar registros em streaming, trabalhar com objetos e evitar os erros mais comuns.
Primeiro exemplo
from itertools import groupby
valores = [1, 1, 2, 2, 2, 3]
for chave, grupo in groupby(valores):
print(chave, list(grupo))
O resultado contém um grupo para cada sequência consecutiva de valores iguais.
A regra da adjacência
valores = [1, 2, 1]
print([(k, list(g)) for k, g in groupby(valores)])
O valor 1 aparece em dois grupos, porque os elementos iguais não estão juntos. Para reunir todas as ocorrências por chave, ordene primeiro quando isso fizer sentido.
Ordenar pela mesma chave
registros = [
{"categoria": "b", "valor": 4},
{"categoria": "a", "valor": 2},
{"categoria": "b", "valor": 7},
]
registros.sort(key=lambda item: item["categoria"])
for categoria, grupo in groupby(registros, key=lambda item: item["categoria"]):
itens = list(grupo)
print(categoria, itens)
A função usada na ordenação deve ser compatível com a função de chave do agrupamento. Caso contrário, uma mesma chave pode reaparecer em grupos separados.
Os grupos compartilham o iterador
Cada grupo é um subiterador ligado ao iterador principal. Quando o loop avança para a próxima chave, o grupo anterior deixa de estar disponível. Materialize-o imediatamente quando precisar conservar seus itens.
for chave, grupo in groupby(dados, key=obter_chave):
itens = list(grupo)
processar(chave, itens)
Agregação sem materializar tudo
for categoria, grupo in groupby(registros, key=lambda r: r.categoria):
total = sum(item.valor for item in grupo)
print(categoria, total)
Nesse caso, o grupo é consumido diretamente por sum. Isso reduz memória e combina bem com arquivos ordenados, logs e resultados de consultas.
Contagem por grupo
for chave, grupo in groupby(dados, key=obter_chave):
quantidade = sum(1 for _ in grupo)
print(chave, quantidade)
O padrão evita criar uma lista apenas para usar len.
Agrupando objetos
from operator import attrgetter
pedidos.sort(key=attrgetter("cliente_id"))
for cliente_id, grupo in groupby(pedidos, key=attrgetter("cliente_id")):
processar_pedidos(cliente_id, grupo)
operator.attrgetter pode deixar a intenção mais clara que uma lambda repetida.
Dados já ordenados
Quando uma consulta SQL usa ORDER BY, um arquivo é produzido em ordem ou um pipeline garante monotonicidade, groupby pode agregar em streaming. Esse é um dos seus melhores casos de uso.
Diferença para um dicionário
Um dicionário de listas agrupa independentemente da ordem, mas precisa manter todas as coleções em memória. groupby preserva o fluxo e usa pouca memória, desde que os dados estejam adjacentes por chave.
Diferença para pandas
pandas.DataFrame.groupby oferece operações vetorizadas, índices e agregações tabulares. itertools.groupby é mais leve e apropriado para iteradores Python, streams e transformações da biblioteca padrão.
Normalização da chave
nomes.sort(key=lambda nome: nome.casefold())
for chave, grupo in groupby(nomes, key=lambda nome: nome.casefold()):
print(chave, list(grupo))
Use a mesma normalização para ordenar e agrupar. Para texto humano, considere locale e regras de Unicode.
Grupos aninhados
dados.sort(key=lambda r: (r.pais, r.cidade))
for pais, grupo_pais in groupby(dados, key=lambda r: r.pais):
itens_pais = list(grupo_pais)
for cidade, grupo_cidade in groupby(itens_pais, key=lambda r: r.cidade):
processar(pais, cidade, grupo_cidade)
Materializar o grupo externo pode ser necessário porque o iterador principal é compartilhado.
Erros comuns
- Esperar que chaves iguais não adjacentes sejam reunidas.
- Ordenar com uma chave e agrupar com outra.
- Guardar o subiterador para consumir mais tarde.
- Converter todos os grupos em listas sem necessidade.
- Executar a função de chave com efeitos colaterais.
Boas práticas
Documente se a entrada precisa estar ordenada, mantenha a função de chave pura, consuma cada grupo antes de avançar e escolha entre streaming e materialização conscientemente. Veja também os guias internos sobre itertools no Python e itertools.pairwise.
Conclusão
itertools.groupby é uma ferramenta eficiente para segmentar sequências ordenadas e realizar agregações preguiçosas. Entender a adjacência e o compartilhamento do iterador evita resultados incorretos e permite pipelines de baixo consumo de memória.







