Como faço para usar itertools.groupby do Python ()?

votos
364

Eu não tenho sido capaz de encontrar uma explicação compreensível de como realmente usar do Python itertools.groupby()função. O que estou tentando fazer é esta:

  • Dê uma lista - neste caso, os filhos de um objetivada lxmlelemento
  • Dividi-lo em grupos com base em alguns critérios
  • Então, mais tarde iterar sobre cada um desses grupos separadamente.

Revi a documentação e os exemplos , mas eu tive problemas para tentar aplicá-los para além de uma simples lista de números.

Então, como eu uso de itertools.groupby()? Existe outra técnica que eu deveria estar usando? Ponteiros para uma boa leitura pré-requisito também seria apreciada.

Publicado 03/08/2008 em 19:27
fonte usuário
Em outras línguas...                            


13 respostas

votos
523

Como disse Sebastjan, primeiro você tem que classificar os dados. Isso é importante.

A parte que eu não entendo é que, no exemplo da construção

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 interagir sobre o grupo definido por essa chave de agrupamento. Em outras palavras, o groupbypróprio iterador retorna iteradores.

Aqui está um exemplo de que, usando nomes de variáveis ​​mais claras:

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 " "

Isto lhe dará a saída:

Um urso é um animal.
Um pato é um animal.

Um cacto é uma planta.

Um barco da velocidade é um veículo.
Um ônibus escolar é um veículo.

Neste exemplo, thingsé uma lista de tuplos onde o primeiro item em cada tuplo é o grupo o segundo item pertence.

A groupby()função leva dois argumentos: (1) os dados de grupo e (2) a função de grupo-a com.

Aqui, lambda x: x[0]diz groupby()a usar o primeiro item em cada tupla como a chave de agrupamento.

No exemplo acima fordeclaração, groupbyretorna três pares (chave, iteradoras grupo) - uma vez para cada chave única. Você pode usar o iterador retornado para iterar sobre cada item individual nesse grupo.

Aqui está um exemplo ligeiramente diferente com os mesmos dados, usando uma lista de compreensão:

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

Isto lhe dará a saída:

animais: Urso e pato.
plantas: cacto.
veículos: barco de velocidade e ônibus escolar.

Respondeu 10/08/2008 em 19:45
fonte usuário

votos
65

Pode mostrar-nos o seu código?

O exemplo nos docs Python é bastante simples:

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

Assim, no seu caso, os dados é uma lista de nós, keyfunc é onde a lógica da sua função de critérios de vai e, em seguida, groupby()grupos de dados.

Você deve ter cuidado para classificar os dados por critérios antes de chamar groupbyou não vai funcionar. groupbymétodo realmente apenas percorre uma lista e sempre que as principais mudanças ele cria um novo grupo.

Respondeu 03/08/2008 em 19:40
fonte usuário

votos
32

Um truque neato com groupby é para executar a codificação de comprimento de uma linha:

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

vai lhe dar uma lista de 2-tuplas em que o primeiro elemento é o caractere eo segundo é o número de repetições.

Edit: Note que este é o que separa itertools.groupbydos SQL GROUP BYsemântica: itertools não (e, em geral, não pode) classificar o iterador com antecedência, para grupos com a mesma "chave" não são mescladas.

Respondeu 01/09/2008 em 00:27
fonte usuário

votos
21

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]

Note-se que igroup é um iterador (uma sub-iterador como a documentação chama).

Isso é útil para chunking 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 teclas não são ordenados. No exemplo a seguir, os itens em xx são agrupadas por valores em aa. Neste caso, um conjunto de zeros é emitido em primeiro lugar, seguido por um conjunto de ones, 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]
Respondeu 21/01/2013 em 17:54
fonte usuário

votos
17

ATENÇÃO:

A lista sintaxe (groupby (...)) não irá funcionar da maneira que você pretende. Parece para destruir os objetos iteradores internos, portanto, usando

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

vai produzir:

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

Em vez disso, da lista (groupby (...)), tente [(k, lista (g)) para k, g em groupby (...)], ou se você usar essa sintaxe, muitas vezes,

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

e obter acesso à funcionalidade groupby evitando aqueles traquinas (para dados pequeno) iteradores todos juntos.

Respondeu 16/11/2013 em 01:39
fonte usuário

votos
11

itertools.groupby é uma ferramenta para agrupar itens.

Desde os docs , nós recolher ainda mais o que ele 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 objectos produzir pares de grupos de chave, onde o grupo é um gerador.

Características

  • A. Grupo itens consecutivos em conjunto (similar à unique_justseenreceita)
  • B. Grupo todas as ocorrências de um item, dado um iterable ordenada
  • C. especificar como grupo itens com uma função chave

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
>>> 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

Vários dos últimos exemplos derivam PyCon palestra de Víctor Terrón (Inglês) (Espanhol) , Kung Fu no alvorecer com itertools . Para quem estiver interessado, aqui está o código-fonte para groupbyescrito em C.

Respondeu 25/08/2017 em 02:26
fonte usuário

votos
10

Eu gostaria de dar um outro exemplo onde groupby sem sorte não está funcionando. Adaptado de exemplo 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 " "

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.

há dois grupos com veículo, enquanto que se poderia esperar apenas um grupo

Respondeu 07/05/2013 em 21:09
fonte usuário

votos
7

@CaptSolo, eu tentei o 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, há duas O e dois e de, mas eles entraram em grupos separados. Foi quando eu percebi que você precisa para classificar a lista passado para a função groupby. Assim, 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)]

Apenas lembrando, se a lista não é ordenada, a função groupby não vai funcionar !

Respondeu 15/10/2009 em 16:41
fonte usuário

votos
5

Como faço para usar itertools.groupby do Python ()?

Você pode usar groupby para agrupar as coisas para iterar. Você dá groupby um iterável, e uma opcional chave / função que pode ser chamado pelo qual para verificar os itens que eles saem do iterable, e ele retorna um iterador que dá um dois-tupla do resultado da chave exigível e os itens reais em outra iterable. Desde a 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 utilizando uma co-rotina com o grupo por uma contagem, ele usa uma chave que pode ser chamado (neste caso, coroutine.send) apenas para cuspir a contagem para no entanto muitas iterações e uma sub-iterador 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))

impressões

[(0, [0, 1, 2]), (1, [3, 4, 5]), (2, [6, 7, 8]), (3, [9])]
Respondeu 27/07/2015 em 18:06
fonte usuário

votos
3

Classificando e 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'}
Respondeu 01/08/2017 em 07:14
fonte usuário

votos
2

Um exemplo útil que me deparei podem ser úteis:

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 amostra: 14445221

Exemplo de saída: (1,1) (3,4) (1,5) (2,2) (1,1)

Respondeu 18/06/2017 em 17:16
fonte usuário

votos
1

Você pode escrever função própria 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]}
Respondeu 10/10/2018 em 17:53
fonte usuário

votos
-1

Faça um iterador que retorna chaves consecutivos e grupos do iterable. A chave é uma função de computação um valor de chave para cada elemento.

import itertools

for k,group in  itertools.groupby([['subject1','english'],['subject2','kannada']]):
for g in group:
    print(f'{k[0]} is {g[1]}')
# output : 
subject1 is english
subject2 is kannada
Respondeu 23/08/2018 em 06:44
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more