700 Stimmen

Wie kann ich itertools.groupby() verwenden?

Ich habe keine verständliche Erklärung gefunden, wie man Pythons itertools.groupby() Funktion. Was ich zu tun versuche, ist dies:

  • Man nehme eine Liste - in diesem Fall die Kinder eines objektivierten lxml Element
  • Teilen Sie sie nach bestimmten Kriterien in Gruppen ein
  • Später wird dann über jede dieser Gruppen separat iteriert.

Ich habe Folgendes überprüft die Dokumentation aber ich hatte Schwierigkeiten, sie über eine einfache Liste von Zahlen hinaus anzuwenden.

Wie verwende ich also die itertools.groupby() ? Gibt es eine andere Technik, die ich anwenden sollte? Hinweise auf gute "voraussetzungsreiche" Lektüre wären ebenfalls willkommen.

3 Stimmen

Ein nützlicher Fall für die wäre leetcode.com/probleme/string-komprimierung

877voto

James Sulak Punkte 29697

WICHTIGER HINWEIS: Sie müssen Ihre Daten sortieren Erstens.


Was ich nicht verstanden habe, ist, dass in der Beispielkonstruktion

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

k ist der aktuelle Gruppierungsschlüssel, und g ist ein Iterator, mit dem Sie über die durch diesen Gruppierungsschlüssel definierte Gruppe iterieren können. Mit anderen Worten, die groupby Iterator selbst gibt Iteratoren zurück.

Hier ist ein Beispiel dafür, das klarere Variablennamen verwendet:

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

Sie erhalten dann die Ausgabe:

Ein Bär ist ein Tier.
Eine Ente ist ein Tier.

Ein Kaktus ist eine Pflanze.

Ein Schnellboot ist ein Fahrzeug.
Ein Schulbus ist ein Fahrzeug.

In diesem Beispiel, things ist eine Liste von Tupeln, wobei das erste Element in jedem Tupel die Gruppe ist, zu der das zweite Element gehört.

El groupby() nimmt zwei Argumente entgegen: (1) die zu gruppierenden Daten und (2) die Funktion, mit der sie gruppiert werden sollen.

Hier, lambda x: x[0] sagt groupby() um das erste Element in jedem Tupel als Gruppierungsschlüssel zu verwenden.

In dem oben genannten for Erklärung, groupby gibt drei Paare (Schlüssel, Gruppen-Iterator) zurück - eines für jeden eindeutigen Schlüssel. Sie können den zurückgegebenen Iterator verwenden, um über jedes einzelne Element in dieser Gruppe zu iterieren.

Hier ein leicht abgewandeltes Beispiel mit denselben Daten unter Verwendung eines Listenverständnisses:

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

So erhalten Sie die Ausgabe:

Tiere: Bär und Ente.
Pflanzen: Kaktus.
Fahrzeuge: Schnellboot und Schulbus.

3 Stimmen

Gibt es eine Möglichkeit, die Gruppen im Voraus festzulegen und dann keine Sortierung zu verlangen?

3 Stimmen

Itertools klappt bei mir normalerweise, aber ich hatte auch eine "Blockade" für dieses Projekt. Ich schätzte Ihre Beispiele - viel klarer als Docs. Ich denke, itertools neigen dazu, entweder klicken oder nicht, und sind viel einfacher zu begreifen, wenn Sie zufällig getroffen haben ähnliche Probleme. Ich habe diese noch nicht in freier Wildbahn benötigt.

4 Stimmen

@Julian Python Docs scheinen für die meisten Sachen groß, aber wenn es um Iteratoren, Generatoren und Cherrypy kommt die Docs meist mystifizieren mich. Django's docs sind doppelt verwirrend.

163voto

pylang Punkte 33775

itertools.groupby ist ein Werkzeug zur Gruppierung von Elementen.

Von die Dokumente erfahren wir mehr darüber, was sie bewirken könnte:

# [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 Objekte ergeben Schlüssel-Gruppen-Paare, wobei die Gruppe ein Generator ist.

Eigenschaften

  • A. Aufeinanderfolgende Artikel zusammenfassen
  • B. Alle Vorkommen eines Elements gruppieren, wenn eine sortierte Wiederholungsliste vorliegt
  • C. Legen Sie fest, wie die Elemente mit einem Schlüsselfunktion *

Vergleiche

# 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']

Verwendet

Anmerkung: Einige der letztgenannten Beispiele stammen von Víctor Terróns PyCon (Gespräch) (Spanisch) "Kung Fu im Morgengrauen mit Itertools". Siehe auch die groupby Quellcode in C geschrieben.

* Eine Funktion, bei der alle Elemente durchlaufen und verglichen werden, was sich auf das Ergebnis auswirkt. Andere Objekte mit Schlüsselfunktionen sind sorted() , max() y min() .


Antwort

# OP: Yes, you can use `groupby`, e.g. 
[do_something(list(g)) for _, g in groupby(lxml_elements, criteria_func)]

2 Stimmen

Technisch gesehen sollten die Dokumente wahrscheinlich sagen [''.join(g) for k, g in groupby('AAAABBBCCD')] --> AAAA BBB CC D .

4 Stimmen

Ja. Die meisten der itertools-Dokumente sind auf diese Weise "gekürzt". Da es sich bei allen itertools um Iteratoren handelt, müssen sie in ein builtin ( list() , tuple() ) oder in einer Schleife/Verständigung konsumiert werden, um den Inhalt anzuzeigen. Dies sind Redundanzen, die der Autor wahrscheinlich aus Platzgründen weggelassen hat.

74voto

Seb Punkte 15918

Das Beispiel in den Python-Dokumenten ist recht einfach:

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

In Ihrem Fall sind die Daten also eine Liste von Knoten, keyfunc ist der Ort, an dem die Logik Ihrer Kriterienfunktion stattfindet, und dann groupby() gruppiert die Daten.

Sie müssen darauf achten, dass die Daten zu sortieren nach den Kriterien, bevor Sie die groupby oder es wird nicht funktionieren. groupby Methode durchläuft eigentlich nur eine Liste und erstellt bei jeder Änderung des Schlüssels eine neue Gruppe.

77 Stimmen

Sie lesen also keyfunc und sagten: "Ja, ich weiß genau, was das ist, denn diese Dokumentation ist ziemlich eindeutig"? Unglaublich!

17 Stimmen

Ich glaube, die meisten Leute wissen bereits über dieses "einfache", aber nutzlose Beispiel Bescheid, da nicht gesagt wird, welche Art von "data" und "keyfunc" zu verwenden sind!!! Aber ich schätze, Sie wissen es auch nicht, sonst würden Sie den Leuten helfen, indem Sie es klarstellen und nicht einfach kopieren. Oder wissen Sie es?

4 Stimmen

Ich möchte anmerken, dass das bloße Einfügen der Dokumente, auf die sich die Frage bezieht, keineswegs eine hilfreiche Antwort ist, aber die zusätzliche Aussage darunter ist eine gute Erinnerung. Die Daten müssen zunächst nach der keyfunc sortiert werden. Wenn der Benutzer also eine Liste von Klassen hat und sie nach obj.attr_a gruppieren möchte, grouping_target = sorted(obj_list, key=lambda o: o.attr_a) und dann eine groups = itertools.groupby(grouping_target, key=lambda o: o.attr_a) . Andernfalls wird es, wie bereits erwähnt, nicht funktionieren und Sie werden eine Verdoppelung Ihrer Groupby-Schlüssel sehen.

52voto

nimish Punkte 4440

Ein netter Trick mit groupby ist, die Längenkodierung in einer Zeile auszuführen:

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

ergibt eine Liste von 2 Tupeln, wobei das erste Element das Zeichen und das zweite die Anzahl der Wiederholungen ist.

Edit: Beachten Sie, dass dies der Unterschied zwischen itertools.groupby aus der SQL-Datei GROUP BY Semantik: itertools sortiert den Iterator nicht im Voraus (und kann dies im Allgemeinen auch nicht), so dass Gruppen mit demselben "Schlüssel" nicht zusammengeführt werden.

35voto

user650654 Punkte 4800

Ein weiteres Beispiel:

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

führt zu

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

Beachten Sie, dass igroup ist ein Iterator (ein Subiterator, wie es in der Dokumentation heißt).

Dies ist nützlich für das Chunking eines Generators:

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)

Ein weiteres Beispiel für groupby - wenn die Schlüssel nicht sortiert sind. Im folgenden Beispiel werden die Elemente in xx sind gruppiert nach Werten in yy . In diesem Fall wird zuerst ein Satz Nullen ausgegeben, gefolgt von einem Satz Einsen, gefolgt von einem weiteren Satz Nullen.

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

Produce:

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

0 Stimmen

Das ist interessant, aber wäre itertools.islice nicht besser für das Chunking einer Iterable? Es gibt ein Objekt zurück, das wie ein Generator iteriert, aber es verwendet C-Code.

0 Stimmen

@trojjer islice wäre besser, WENN die Gruppen eine einheitliche Größe haben.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X