`sorted` vs. `groupby`

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Ich vermute fast, das ist mal wieder eine der Fragen, die mich als totalen Noob enttarnt... :?

Wo liegt hier der Vorteil von `groupby` gegenüber `sorted`, das ich ja sowieso anwenden muss:

Code: Alles auswählen

In [46]: objects
Out[46]: 
[Bundle(section=0, name='section_0'),
 Bundle(section=1, name='section_1'),
 Bundle(section=2, name='section_2'),
 Bundle(section=0, name='section_0'),
 Bundle(section=1, name='section_1'),
 Bundle(section=2, name='section_2')]

In [47]: [list(g) for k, g in groupby(sorted(objects, key=lambda obj: obj.section), key=lambda obj: obj.section)]
Out[47]: 
[[Bundle(section=0, name='section_0'), Bundle(section=0, name='section_0')],
 [Bundle(section=1, name='section_1'), Bundle(section=1, name='section_1')],
 [Bundle(section=2, name='section_2'), Bundle(section=2, name='section_2')]]

In [48]: sorted(objects, key=lambda obj: obj.section)
Out[48]: 
[Bundle(section=0, name='section_0'),
 Bundle(section=0, name='section_0'),
 Bundle(section=1, name='section_1'),
 Bundle(section=1, name='section_1'),
 Bundle(section=2, name='section_2'),
 Bundle(section=2, name='section_2')]
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

sorted sortiert Sachen, groupby gruppiert sie. Es sind zwei unterschiedliche Funktionen die unterschiedliche Dinge machen. Wie soll da ein Vorteil exisieren können, wenn du die zwei beiden Funktionen noch nicht einmal sinnvoll vergleichen kannst?
karolus
User
Beiträge: 141
Registriert: Samstag 22. August 2009, 22:34

.... das ich ja sowieso anwenden muss:
Das muss ist nicht allgemeingültig, dann wird auch der Unterschied offensichtlich:

Code: Alles auswählen

In [1]: from itertools import groupby

In [2]: [k for k, g in groupby('AAAABBBCCDAABBB')]
Out[2]: ['A', 'B', 'C', 'D', 'A', 'B']
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Aus der Doku zu `groupby()`:
It generates a break or new group every time the value of the key function changes
Und das veranschaulichende Beispiel, in dem eine Gruppe für den selben Wert durchaus mehrfach im Iterable auftauchen kann, wurde hier ja bereits zitiert. Insofern schließe ich mich meinen Vorpostern an: Die beiden Funktionen tun unterschiedliche Dinge.

@mutetella:
Entweder du hast `groupby()` noch nicht ganz verstanden oder deine Frage ist ungenau formuliert. Jedenfalls bringt keine der beiden Funktionen für sich gesehen einen Vorteil gegenüber der anderen, da die Funktionen unterschiedliche Aufgaben haben. Der Vorteil ergäbe sich ja nur dann, wenn man die eine als Ersatz für die andere verwenden könnte.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

snafu hat geschrieben:Entweder du hast `groupby()` noch nicht ganz verstanden ...
Definitiv noch nicht verstanden! Für die Beispiele aus der Doku fallen mir keine sinnvollen Anwendungsbeispiele ein. Was für einen Nutzen habe ich, wenn ich aus 'AAAABBBCCD'

Code: Alles auswählen

A ['A', 'A', 'A', 'A']
B ['B', 'B', 'B']
C ['C', 'C']
D ['D']
bekomme? Und das noch nicht einmal aus einem 'ABBCAACDAB'? Wenn ich das sowieso vorher sortieren muss, dann ist der break doch kein Grund mehr, `groupby` zu bemühen?

Konkret: Ich bekomme aus einer Suche bunt gemischt Termine geliefert. Jedes dieser Objekte hat natürlich ein Datumsattribut. Die Termine möchte ich sinnigerweise nach Datum geordnet ausgeben. Jetzt könnte ich das Suchergebnis entweder nach Datum sortieren oder eben via `groupby` gruppieren, wobei ich dabei auch erstmal sortieren muss. Ist also `groupby` für sowas nicht gedacht?

@karolus
Ok, für sowas macht's Sinn, wobei das doch auch ohne `groupby` sehr einfach ginge... :|
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Es wäre auch so etwas möglich:

Code: Alles auswählen

def get_grouped(items):
    grouped = defaultdict(set)
    for key, group in groupby(items):
        for item in group:
            grouped[key].add(item)
    return grouped
Das wäre mit `sorted()` in dieser Form nicht machbar.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@snafu
Was verstehe ich nicht?

Code: Alles auswählen

In [64]: get_grouped('AAABBBCCDDD')
Out[64]: defaultdict(<type 'set'>, {'A': set(['A']), 'C': set(['C']), 'B': set(['B']), 'D': set(['D'])})

In [65]: grouped = defaultdict(set)

In [66]: for char in 'AAABBBCCDDD':
   ....:     grouped[char].add(char)
   ....:     

In [67]: grouped
Out[67]: defaultdict(<type 'set'>, {'A': set(['A']), 'C': set(['C']), 'B': set(['B']), 'D': set(['D'])})
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Für einfache `groupby()`-Aufrufe ohne die Angabe eines `key`-Parameters war mein Beispiel schlecht gewählt. Das müsste man noch hinzufügen, wenn das Ablegen in ein Set oder einen anderen Container sinnvoll sein soll. Natürlich könnte man auch dies via `sorted()` erreichen, aber `groupby()` nimmt die Gruppierung in einem Durchlauf vor. Das kann ein Sortier-Algorithmus nicht leisten, weil seine Aufgabe eben eine andere ist. Mit anderen Worten: Der muss sich die Elemente mehrfach angucken. `groupby()` muss nur erkennen, ob das aktuelle Element noch zur bisherigen Gruppe gehört oder ob es in eine neue Gruppe abgelegt werden muss. Zudem muss `sorted()` aus dem zuvor genannten Grund alle Elemente im Speicher halten, was bei großen Datenmengen ein spürbarer Nachteil sein kann. `groupby()` muss immer nur die aktuelle Gruppe und das aktuelle Element kennen.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@snafu
Vor dem Hintergrund, was Du über `sorted` sagst, wäre für meine Gruppierung der `appointment` Exemplare also folgende Abwandlung ganz sinnvoll:

Code: Alles auswählen

def get_grouped(items):
    grouped = defaultdict(list)
    key = lambda item: item.date
    for key, group in groupby(items, key=key):
        for item in group:
            grouped[key].append(item)
    return grouped
Somit hätte ich den Vorteil, die Kosten von `sorted` zu vermeiden. Andererseits: `group` ist in dem Beispiel bei unsortierten `items` ja oftmals auch nur ein Element, also könnte ich doch einfach über `items` iterieren und mit `defaultdict(list)` zu selbigem Ergebnis kommen?
Um nicht falsch verstanden zu werden: Ich will hier nicht krampfhaft auf `groupby` verzichten. Ich verstehe nur immer noch nicht, welchen Vorteil ich daraus habe?
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
BlackJack

@mutetella: Der Vorteil ist das `groupby()` *gruppiert*. Was `sorted()` nicht tut und nicht kann. Und `groupby()` liefert Iteratoren, das heisst es wird nur so viel an Elementen aus dem iterierbaren Objekt im Speicher gehalten wie der Code der das verarbeitet im Speicher hält. Beispielsweise nützlich wenn man grosse Textdateien verarbeitet die beispielsweise durch eine spezielle Blockmarkierungszeile in Blöcke aufgeteilt sind und man diese Blöcke einzeln verarbeiten möchte, dann kann man die Zeilen mit einer Generatorfunktion vorbehandeln die die einzelnen Zeilen zum Beispiel um die Blocknummer ergänzt und `groupby()` dann danach gruppieren lassen. Man hat dann einen Iterator der Iteratoren über die Zeilen der einzelnen Blöcke liefert. Das heisst sogar einzelne Blöcke können grösser sein als in den Speicher passen wenn man die als Zeilenstrom verarbeiten kann ohne das alle Zeilen im Speicher sein müssen. Das geht mit `sorted()` überhaupt nicht, weil diese Funktion eben etwas völlig anderes macht.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

mutetella hat geschrieben:Andererseits: `group` ist in dem Beispiel bei unsortierten `items` ja oftmals auch nur ein Element, also könnte ich doch einfach über `items` iterieren und mit `defaultdict(list)` zu selbigem Ergebnis kommen?
So wie es aussieht, benötigst du `groupby()` tatsächlich nicht, da das Dictionary im Grunde diesen Job für dich erledigt. Ein Dictionary ist natürlich nicht äquivalent zu `groupby()`, aber bei dem Effekt, den du erreichen willst, wäre das Dictionary auch meine Wahl.
Antworten