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.

Foi útil?

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

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 groupbyCó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]}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top