Jak korzystać z itertools.groupby()?

Nie byłem w stanie znaleźć zrozumiałego wyjaśnienia, jak właściwie używać funkcji Pythona itertools.groupby(). Próbuję to zrobić:

  • weź listę-w tym przypadku dzieci elementu lxml
  • Podziel je na grupy na podstawie pewnych kryteriów
  • potem iterować po każdej z tych grup oddzielnie.

Przejrzałem dokumentację, ale miałem problem z zastosowaniem ich poza prostą listą liczb.

Więc, jak korzystać z itertools.groupby()? Czy jest inna technika, której powinienem użyć? Mile widziane będą również wskazówki do dobrej lektury "warunek wstępny".

Author: Seanny123, 2008-08-03

13 answers

WAŻNA UWAGA: najpierw musisz posortować swoje dane .


Część, której nie dostałem, to to, że w przykładowej konstrukcji

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

k jest bieżącym kluczem grupującym, a {[4] } jest iteratorem, którego można użyć do iteracji nad grupą zdefiniowaną przez ten klucz grupujący. Innymi słowy, iterator groupby sam zwraca Iteratory.

Oto przykład, używając jaśniejszych nazw zmiennych:

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

To da ci wyjście:

Niedźwiedź to zwierzę.
Kaczka to zwierzę.

Kaktus jest rośliną.

Motorówka to pojazd.
Autobus szkolny to pojazd.

W tym przykładzie {[6] } jest listą krotek, gdzie pierwsza pozycja w każdej krotce jest grupą, do której należy druga pozycja.

Funkcja groupby() pobiera dwa argumenty: (1) Dane do grupy i (2) funkcja do grupy.

Tutaj lambda x: x[0] mówi groupby() aby użyć pierwszego elementu w każdym krotka jako klucz grupujący.

W powyższej instrukcji for, groupby zwraca trzy pary (klucz, iterator Grupowy) - raz dla każdego unikalnego klucza. Zwracany iterator można wykorzystać do iteracji każdego elementu z tej grupy.

Oto nieco inny przykład z tymi samymi danymi, używając zrozumienia listy:

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

To da ci wyjście:

Zwierzęta: Niedźwiedź i kaczka.
rośliny: Kaktusy.
Pojazdy: motorówka i autobus szkolny.

 710
Author: James Sulak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-07-29 20:57:41

itertools.groupby jest narzędziem do grupowania elementów.

Z docs , możemy dowiedzieć się dalej, co to może zrobić:

# [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 obiekty dają pary klucz-Grupa, gdzie grupa jest generatorem.

Funkcje

  • A. Grupuj kolejne pozycje razem
  • B. grupuje wszystkie wystąpienia elementu, biorąc pod uwagę posortowane iterowalne
  • C. Określ, jak grupować elementy za pomocą funkcji klucza *

Porównania

# Define a printer for comparing outputs
>>> def print_groupby(iterable, keyfunc=None):
...    for k, g in it.groupby(iterable, keyfunc):
...        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
>>> # islower = lambda s: s.islower()                      # equivalent
>>> def islower(s):
...     """Return True if a string is lowercase, else False."""   
...     return s.islower()
>>> print_groupby(sorted("bCAaCacAADBbB"), keyfunc=islower)
key: 'False'--> group: ['A', 'A', 'A', 'B', 'B', 'C', 'C', 'D']
key: 'True'--> group: ['a', 'a', 'b', 'b', 'c']

Zastosowania

Uwaga: Niektóre z ostatnich przykładów pochodzą z PyCon Victor Terro (talk) (Hiszpański) , "Kung Fu o świcie z Itertools". Zobacz też:groupby kod źródłowy napisany w C.

* funkcja, w której wszystkie elementy są przekazywane i porównywane, wpływając na wynik. Inne obiekty o kluczowych funkcjach include sorted(), max() i min().


Odpowiedź

# OP: Yes, you can use `groupby`, e.g. 
[do_something(list(g)) for _, g in groupby(lxml_elements, criteria_func)]
 99
Author: pylang,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-09-10 01:40:04

Przykład w Python docs jest dość prosty:

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

Więc w Twoim przypadku, dane są listą węzłów, {[1] } jest gdzie logika funkcji kryteriów idzie, a następnie groupby() grupuje dane.

Musisz być ostrożny, aby posortować dane według kryteriów przed wywołaniem groupby lub nie będzie działać. groupby Metoda Po prostu iteruje przez Listę i za każdym razem, gdy klucz się zmieni, tworzy nową grupę.

 70
Author: Seb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-01-24 13:41:47

Neato trick z groupby polega na uruchomieniu kodowania długości w jednej linii:

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

Wyświetli listę 2 krotek, gdzie pierwszym elementem jest znak, a drugim liczba powtórzeń.

Edit: zauważ, że to jest to, co odróżnia itertools.groupby od semantyki SQL GROUP BY: itertools nie sortuje (i ogólnie nie może) z góry sortować iteratora, więc grupy z tym samym "kluczem" nie są scalane.

 42
Author: nimish,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-08 10:01:05

Inny przykład:

for key, igroup in itertools.groupby(xrange(12), lambda x: x // 5):
    print key, list(igroup)

Wyniki w

0 [0, 1, 2, 3, 4]
1 [5, 6, 7, 8, 9]
2 [10, 11]

Zauważ, że igroup jest iteratorem(pod-iteratorem, jak nazywa go dokumentacja).

Jest to przydatne do łączenia generatora:

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)

Kolejny przykład groupby-gdy klucze nie są sortowane. W poniższym przykładzie pozycje w xx są pogrupowane według wartości w yy. W tym przypadku najpierw wyprowadzany jest jeden zestaw zer, następnie zestaw jedynek, a następnie ponownie zestaw zer.

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

Produkuje:

0 [0, 1, 2]
1 [3, 4, 5]
0 [6, 7, 8, 9]
 27
Author: user650654,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-21 16:54:08

Ostrzeżenie:

Lista składni (groupby (...)) nie będzie działać tak, jak zamierzacie. Wydaje się, że niszczy wewnętrzne obiekty iteratora, więc używając

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

Będzie produkować:

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

Zamiast listy (groupby(...)), try [(k, list (g)) for k,g in groupby (...)], lub jeśli często używasz tej składni,

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

I uzyskaj dostęp do funkcji groupby, unikając jednocześnie tych nieznośnych (dla małych danych) iteratorów.

 22
Author: RussellStewart,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-28 19:26:52

Chciałbym podać inny przykład, w którym groupby bez sortowania nie działa. Adaptacja na przykładzie Jakuba Sulaka

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

Wyjście To

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.

Istnieją dwie grupy z pojazdami, podczas gdy można oczekiwać tylko jednej grupy

 10
Author: kiriloff,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-19 04:09:53

@CaptSolo, próbowałem twojego przykładu, ale nie zadziałało.

from itertools import groupby 
[(c,len(list(cs))) for c,cs in groupby('Pedro Manoel')]

Wyjście:

[('P', 1), ('e', 1), ('d', 1), ('r', 1), ('o', 1), (' ', 1), ('M', 1), ('a', 1), ('n', 1), ('o', 1), ('e', 1), ('l', 1)]

Jak widzisz, są dwa o I dwa e, ale zostały podzielone na oddzielne grupy. Wtedy zdałem sobie sprawę, że trzeba posortować listę przekazaną do funkcji groupby. Tak więc poprawne użycie byłoby:

name = list('Pedro Manoel')
name.sort()
[(c,len(list(cs))) for c,cs in groupby(name)]

Wyjście:

[(' ', 1), ('M', 1), ('P', 1), ('a', 1), ('d', 1), ('e', 2), ('l', 1), ('n', 1), ('o', 2), ('r', 1)]

Po prostu pamiętając, jeśli lista nie jest posortowana, funkcja groupby nie będzie działać !

 7
Author: pedromanoel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-11 22:55:48

Sortowanie i grupowanie

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'}
 7
Author: Satyajit Das,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-20 11:51:28

Jak używać Itertools Pythona.groupby()?

Możesz użyć groupby do grupowania rzeczy do iteracji. Podajesz groupby iterowalny i opcjonalny klucz funkcja/callable, za pomocą której sprawdzasz elementy, gdy wychodzą z iterowalnego, i zwraca iterator, który daje dwie krotki wyniku wywołania klucza i rzeczywistych elementów w innej iterowalnej. Z pomocy:

groupby(iterable[, keyfunc]) -> create an iterator which returns
(key, sub-iterator) grouped by each value of key(value).

Oto przykład groupby za pomocą coroutine do grupy przez count, używa klucza wywołującego (w tym przypadku coroutine.send), aby po prostu wypluć count dla wielu iteracji i zgrupowanego pod-iteratora elementów:

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

Druki

[(0, [0, 1, 2]), (1, [3, 4, 5]), (2, [6, 7, 8]), (3, [9])]
 5
Author: Aaron Hall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-27 18:37:13

Ta podstawowa implementacja pomogła mi zrozumieć tę funkcję. Mam nadzieję, że pomoże również innym:

arr = [(1, "A"), (1, "B"), (1, "C"), (2, "D"), (2, "E"), (3, "F")]

for k,g in groupby(arr, lambda x: x[0]):
    print("--", k, "--")
    for tup in g:
        print(tup[1])  # tup[0] == k
-- 1 --
A
B
C
-- 2 --
D
E
-- 3 --
F
 3
Author: Tiago,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-04-05 19:54:11

Jeden użyteczny przykład, na który się natknąłem może być pomocny:

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)

Przykładowe wejście: 14445221

Przykładowe wyjście: (1,1) (3,4) (1,5) (2,2) (1,1)

 1
Author: Arko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-18 17:16:54

Możesz napisać własną funkcję 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]}
 -2
Author: Sky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-10 18:06:35