Seite 1 von 1

Verständnisfrage: itertools.groupby

Verfasst: Samstag 6. August 2016, 03:01
von Sophus
Hallo Leute,

ich hantiere gerade mit dem Modul itertool herum, und bin dabei auf die groupby()-Funktion gestoßen.

Im Laufe meiner Recherche habe ich mir folgenden Schnipsel zusammengebastelt:

Code: Alles auswählen

from itertools import groupby

strs="AaaBbbCcccDde"
groups = groupby(strs)

print "result", [(label, sum(1 for i in group)) for label, group in groups]
# output: [('A', 1), ('a', 2), ('B', 1), ('b', 2), ('C', 1), ('c', 3), ('D', 1), ('d', 1), ('e', 1)]
Natürlich möchte ich den Schnipsel in seinen Einzelheiten verstehen. Da mir dieser Ausdruck hinter der Print-Anweisung verdächtig nach einer List Comprehension aussieht, habe ich mir als "Muster" eine Formal angelegt:

Code: Alles auswählen

#       M = [x for x in S]
Also fing ich an, den oberen Ausdruck zu zerlegen:
Innerhalb der LC gibt es eine For-Schelife: for label, group in groups, dies entspricht aus dem Muster for x in S. Hier sehe ich, dass die groupby-Funktion zwei Werte zurückliefert, die einmal in die Variable label und einmal in die Variable group gespeichert werden.

Da die LC eine neue Liste erzeugt, wo dann die neuen Elemente gespeichert werden, braucht die LC vor der For-Schleife einen Ausdruck. Im Muster wäre hier das grüne x --> [x for x in S]. Im Schnipsel wäre die rote Markierung der Ausdruck in der LC --> [(label, sum(1 for i in group)) for label, group in groups]

Wenn ich einmal den Ausdruck extrahiere: (label, sum(1 for i in group))
Hier sehe ich, dass ein Tupel erzeugt wird. Dies erkenne ich an den äußeren runden Klammern. Und innerhalb des Tupels sehe ich, dass eine Generator Comprehension angewendet wird - und zwar hier --> sum(1 for i in group). Und in der Variable label innerhalb des Tupels werden die einzelnen Buchstaben gespeichert. Als Zwischenfazit sehe ich, dass in diesem Tupel als "Paare" an Daten gespeichert werden.

Nun habe ich diesen Schnipsel für mich zerlegt, und versuche das mal in Worten zusammezufassen:
In der LC wird gesagt: Iteriere mir über groups und speichere die Werte einmal in label und group. Erzeuge mir eine neue Liste, um Werte dort abzuspeichern, und greife daher auf den Ausdruck (label, sum(1 for i in group)) zu. In der neuen Liste sollen Tupels mit Daten-Paare gespeichert werden. Innerhalb des Tupel-Ausdruckes wird die Generator Comprehension angesprochen. Da der GC einen Iterator zurückgibt, und der Iterator somit eine sogenannte next-Methode besitzt, wird im GC immer eins hochgezählt (daher auch die Zahl 1) - solange bis der Iterator durchlaufen ist. Und das ganze wird dann summiert. Und in der Variable label werden die einzelnen Buchstaben gespeichert. Am Ende werden dann in der der neuen Liste Tupels gespeichert, die wiederum Daten-Paare enthalten. Hier werden also die Buchstaben gezählt, wie oft sie im String vorkamen.

Habe ich das richtig gedeutet? Bestimmt habe ich mal wieder daneben gegriffen.

Re: Verständnisfrage: itertools.groupby

Verfasst: Samstag 6. August 2016, 09:06
von Sirius3
@Sophus: stimmt soweit alles.

Re: Verständnisfrage: itertools.groupby

Verfasst: Samstag 6. August 2016, 10:51
von Sophus
@Sirius3: Danke. Wenn ich diesen Schnipsel runterbrechen würde, käme ich zum folgenden Ergebnis:

Code: Alles auswählen

strs = "AaaBbbCcccDde"
print "Your String: ", strs
for label, group in groupby(strs):
    for grouped_char in group:
        print "Grouped char",  grouped_char

        print "Total", 1+(sum(1 for i in group))

    print "------"
Output:
Your String: AaaBbbCcccDde
Grouped char A
Total 1
------
Grouped char a
Total 2
------
Grouped char B
Total 1
------
Grouped char b
Total 2
------
Grouped char C
Total 1
------
Grouped char c
Total 3
------
Grouped char D
Total 1
------
Grouped char d
Total 1
------
Grouped char e
Total 1
------
Meine naive Frage an dieser Stelle. Wäre der runtergebrochene Quelltext nicht wesentlich Speicher schonender als im ersten Beispiel? Denn im ersten Beispiel wird ja eine neue Liste erzeugt. In meinem Beispiel wird keine neue Liste erzeugt (oder übersehe ich was?). Gut, mein Quelltext ist etwas "umständlicher" und somit mit mehr Lese- und SChreibaufwand verbunden, da hier mehr verschachtelt ist. Aber aus reiner technischer Perspektive: wäre mein zweiter Quelltext nicht eher "schonender"? (Die Ausführungsgeschwindigkeit vernachlässigen wir einmal - wobei ich nicht glaube, dass diese runter gebrochene Variante wesentlich langsamer sein sollte.)

Re: Verständnisfrage: itertools.groupby

Verfasst: Samstag 6. August 2016, 11:44
von BlackJack
@Sophus: Dein runtergebrochenes Beispiel ist nicht richtig weil die ``for``-Schleife in Zeile 4 immer genau einmal durchlaufen wird, und damit ist die Schleife sinnfrei, denn die wiederholt ja nie irgendetwas.

Was das speicherschonender angeht: Ja (wenn da entsprechende viele Elemente verarbeitet werden), aber im ersten Beispiel wird ja eine Liste erzeugt. Das scheint Absicht zu sein, sonst hätte man das ja dort nicht gemacht sondern Zeile 6 so geschrieben:

Code: Alles auswählen

for item in ((label, sum(1 for _ in group)) for label, group in groups):
    print item

Re: Verständnisfrage: itertools.groupby

Verfasst: Samstag 6. August 2016, 12:18
von Sirius3
@Sophus: was willst Du mit der zweiten for-Schleife bezwecken? Wozu ist label da?

Code: Alles auswählen

strs = "AaaBbbCcccDde"
print "Your String: ", strs
for label, group in groupby(strs):
    print "Grouped char",  label
    print "Total", sum(1 for _ in group)
    print "------"

Re: Verständnisfrage: itertools.groupby

Verfasst: Samstag 6. August 2016, 13:20
von kbr
@Sophus: In Zeile 6 Deines ersten Beispiels befindet sich eine List-Comprehension, da bei einem speicherschonenden Generator-Ausdruck das print Statement sonst nur die Adresse des Generators ausgeben würde und dieser nie anlaufen würde. BlackJacks Variante wäre die konsequente Umsetzung dieses Beispiels.

Um das Problem in Deinem zweiten Beispielcode zu verstehen, kann es hilfreich sein wenn Du Dich fragst, warum es erforderlich ist zur Summenbildung in Zeile 7 eine 1 zu addieren.

Re: Verständnisfrage: itertools.groupby

Verfasst: Samstag 6. August 2016, 22:21
von Sophus
@BlackJack, Sirius3 und kbr: Ihr habt Recht. Die zweite For-Schleife im zweiten Beispiel war wirklich unnötig. Sehe ich jetzt auch erst. Keine Ahnung, was mich da geritten hat. Manchmal sollte ich mir einen Quelltext zwei Mal mehr ansehen.

Allerdings habe ich eine Frage an Sirius3 (eigentlich an alle!).

Wieso steht in Sirius3' Beispiel ein Bodenstrich im GC? Hat es eine "besondere" Wirkung?

Code: Alles auswählen

sum(1 for _ in group)
          ^
          #Hiere

Re: Verständnisfrage: itertools.groupby

Verfasst: Samstag 6. August 2016, 23:23
von __deets__
Nein, hat es nicht. Es steht nur fuer "ein Bezeichner den ich nicht wirklich zu benutzen gedenke". Reine Konvention. Wird so zB von pylint etc. interpretiert & nicht angemeckert, wenn du den nicht benutzt.

Und in diesem Fall interessiert niemanden der Inhalt von group.