Python ERWEITERTE Datentypen - Was ist Counter?

Python hat mit Listen, Sets, Tupel und Dictionaries eine Reihe nützlicher Datenstrukturen direkt mit dabei.
Aber wusstest du, dass du auch noch Hilfe dabei kommst die Typen so effizient wie möglich zu nutzen?
In dieser kleinen Serie möchte ich dir zeigen welche das sind!

Heute geht es um Listen.
Listen kommen praktisch in jedem Programm irgendwo vor.
Aber was ist, wenn du wissen willst wie häufig ein Element vorkommt, oder Mengen miteinander vergleichen willst?

Wie das funktioniert wirst du in diesem Beitrag erfahren!

Ich zeige dir zuerst die naheliegendste Herangehensweise, damit du siehst woher wir kommen und am Ende wie du das collections Modul einsetzen kannst um das Optimum heraus zu holen. 
Bleib also umbedingt bis zum Ende dran, um nichts zu verpassen!

Inhalt:
    Add a header to begin generating the table of contents
    Python Logo

    Häufigkeiten mit Listen

    Fangen wir erstmal mit einem einfachen Beispiel an.
    Stell dir vor, du hast die folgenden drei Listen:

    a = [1, 2, 3]
    b = [5, 3, 2]
    c = [7, 3, 2] 

    Deine Aufgabe ist es jetzt die Elemente zu finden, die in allen 3 Listen vorkommen.
    Im nächsten Schritt sollst du diese gemeinsamen Elemente dann aufsummieren.

    Bei der größe lässt sich das noch einfach mit dem Auge ermitteln:
    die 2 und 3 kommen in allen drei Listen vor.
    Aufsummiert ist das Ergebnis 5.

    Aber wie erledigst du das am besten in einer Funktion?

    Der naheliegendste Weg ist durch alle drei Listen zu gehen und die Elemente zu vergleichen.
    Jetzt weisst du natürlich schon, dass du mit in einfach prüfen kannst, ob ein Element in einer anderen Liste enthalten ist.

    Also: Eine neue Liste anlegen, um die Ergebnisse zu sammeln, eine Liste durchgehen und für jedes Element mit in prüfen, ob es in den anderen beiden Listen enthalten ist. 

    Es reicht eine Liste durch zu gehen, weil du ja nur Elemente wissen willst, die in allen Listen vorkommen.

    def gemeinsam(a, b, c):
      ergebnisse = []
      for element in a:
        if element in b and element in c:
          ergebnisse.append(element)
     
      return sum(ergebnisse)
     
    gemeinsam(a, b, c) 

    Sehr schön. Aufgabe gelöst.
    Aber hier hören wir natürlich nicht auf!

    List-Comprehension

    Als nächstes schreibst du das ein wenig schöner.
    Nutz dafür eine List-Comprehension:

    a = [1, 2, 3]
    b = [5, 3, 2]
    c = [7, 3, 2]
    # gemeinsam: 2, 3 -> 5
     
    def gemeinsam(a, b, c):
      ergebnisse = [element for element in a if element in b and element in c]
     
      return sum(ergebnisse)
     
    print(gemeinsam(a, b, c)) 

    Das ist doch schön kurz und übersichtlich.

    Sets und Mengenoperationen

    Du arbeitest hier mit Mengen.
    Und für Mengen gibt es in Python extra Operationen.
    Zumindest bei einem Datentyp – dem Set.

    Das versuchen wir doch direkt mal:

    a = [1, 2, 3]
    b = [5, 3, 2]
    c = [7, 3, 2]
    # gemeinsam: 2, 3 -> 5
     
    def gemeinsam_intersect(a, b, c):
      ergebnisse = set(a) & set(b) & set(c)  # intersection / gemeinsame Werte
     
      return sum(ergebnisse)
     
    print(gemeinsam_intersect(a, b, c)) 

    Mit einem Set kannst du also mit dem & Operator eine sogenannte Intersection machen.
    Das ist wie ein InnerJoin im SQL.
    Ich blende dir dazu einmal kurz das Bild ein:

    Bild - inner join

    Es werden dir also genau die Werte heraus gezogen, die alle Mengen gemeinsam haben.
    Genau was du willst! Ohne if else und Prüfungen!

    Das Problem mit dem Set...

    Jetzt geb ich dir mal einen neuen Datensatz:

    a = [1, 2, 2, 3]
    b = [5, 3, 2, 2]
    c = [7, 3, 2, 2]
    # gemeinsam: 2, 2, 3 -> 7 

    Hier kommt die 2 jetzt zwei mal in allen Mengen vor und die 3 einmal.
    Das heißt du brauchst eine Summe von 7.

    Führ das einmal aus und sieh was deine Funktion lierfert: 5
    Hmm… nicht das, was du wolltest.
    Was passiert?

    Ein Set hat die tolle Eigenschaft, dass es automatisch alle Duplikate entfernt.
    Das hat zwar seine Vorteile, aber eben auch Nachteile.
    In dem Fall wird bei der Umwandlung in ein Set die 2 entfernt – bzw das Duplikat.

    Die Intersection findet also nur noch 2 und 3 als gemeinsame Elemente.
    Und die Summe ist dann die 5 die du hier siehst.

    Blöd… Dabei ist die Intersection doch so schön zu schreiben.

    Und die For-Schleife? Führ die mal mit den neuen Daten aus.
    Da kommt das richtige Ergebnis.
    Also doch bei der For-Schleife bleiben?

    Die Lösung

    Nein, kommen wir endlich zum collections Modul.
    In dem Fall bietet sich die Klasse Counter an.
    Lass mich dir zeigen wie das funktioniert:

    from collections import Counter
     
    def gemeinsam_counter(a, b, c):
      ergebnisse = Counter(a) & Counter(b) & Counter(c)
      print(sum(ergebnisse))
     
    gemeinsam_counter(a, b, c) 

    Immer noch die 5? Sollte Counter nicht die Lösung bringen?
    Sehen wir uns das einmal genauer an:

    from collections import Counter
     
    def gemeinsam_counter(a, b, c):
      ergebnisse = Counter(a) & Counter(b) & Counter(c)
      print(ergebnisse)
     
    gemeinsam_counter(a, b, c) 

    Aha!
    Counter legt sich interne ein Dictionary an.
    Dafür nutzt es den Wert als Schlüssel und wie oft der Wert vor kommt als Wert.

    Das sum(ergebnisse) hat sich dann so verhalten wie bei einem Dictionary.
    Es hat einfach die Schlüssel genommen und aufsummiert.

    Und das ist ja korrekt.
    Die Schlüssel sind 2 und 3 weil die beiden Elemente in allen Mengen vorkommen.
    Und die aufsummiert sind 5.

    Jetzt willst du aber die Häufigkeiten aufsummieren.
    Wenn du an der Klasse Counter ein .elements() aufrufst bekommst, stellt dir Counter wieder ein Objekt mit deinen Elementen zur Verfügung.
    Mit allen Häufigkeiten:

    from collections import Counter
     
    def gemeinsam_counter(a, b, c):
      ergebnisse = Counter(a) & Counter(b) & Counter(c)
      print('Elemente', list(ergebnisse.elements()))
     
    gemeinsam_counter(a, b, c) 

    Das list() brauchst du, weil Counter eben keine List sondern ein Listen-ähnliches Objekt liefert.
    Aber du siehst: [2, 2, 3]. Deine Elemente sind da.
    Jetzt kannst du sie aufsummieren:

    from collections import Counter
     
    def gemeinsam_counter(a, b, c):
      ergebnisse = Counter(a) & Counter(b) & Counter(c)
      print('Summe der Häufigkeiten', sum(ergebnisse.elements()))
     
    gemeinsam_counter(a, b, c) 

    Und du siehst, es kommt wieder die 7 raus. 
    Mit Counter kannst du also weiterhin Mengenoperationen benutzen und verlierst trotzdem keine doppelten Einträge!
    Klasse!
    Was kann Counter noch?

    Weiteres zu Counter

    Du willst wissen welches Element am häufigsten vorkommen und wie häufig?
    Nimm .most_common().

    from collections import Counter
     
    def gemeinsam_counter(a, b, c):
      ergebnisse = Counter(a) & Counter(b) & Counter(c)
      print('Häufigste Zahl', ergebnisse.most_common())
     
    gemeinsam_counter(a, b, c) 

    Dazu kannst du der Funktion auch noch übergeben wieviele der häufigsten Elemente du haben möchtest.
    Hier haben wir jetzt nur zwei, aber einfach damit du es mal gesehen hast:

    from collections import Counter
     
    def gemeinsam_counter(a, b, c):
      ergebnisse = Counter(a) & Counter(b) & Counter(c)
      print('Häufigste Zahl', ergebnisse.most_common(2))
     
    gemeinsam_counter(a, b, c) 

    Du kannst sogar zwei Mengen von einander abziehen!
    Sieh mal hier, die Menge b.
    Und die ziehst du jetzt von deinen Ergebnisse ab.
    Dafür kannst du die Funktion .subtract() benutzen:

    from collections import Counter
     
    def gemeinsam_counter(a, b, c):
      ergebnisse = Counter(a) & Counter(b) & Counter(c)
      print(ergebnisse)
      print('Counter b', Counter(b))
      ergebnisse.subtract(Counter(b))
      print('Ergebnisse - b', ergebnisse)
     
    gemeinsam_counter(a, b, c) 

    In deinen Ergebnissen kommt die 2 zwei mal vor und die 3 einmal.
    Davon ziehst du jetzt die Menge b ab.
    Hier kommt die 2 zwei mal vor, die 3 einmal und die 5 einmal.
    Also bleibt dir: die 2 fällt weg, die 3 fällt weg, die 5 fällt auf -1

    Dazu passend gibt es noch den einfachen Operator.
    Hier werden nur die positiven Reste behalten.
    Ziehst du also den Counter b vom Counter a ab sieht das so aus:

    from collections import Counter
     
    def gemeinsam_counter(a, b, c):
      print('a - b', Counter(a) - Counter(b))
     
    gemeinsam_counter(a, b, c) 

    Dann kannst du noch Mengen Addieren mit dem + Operator.
    Eine Intersection hast du ja bereits gesehen.
    Und als letztes gibt es noch Union, also die Vereinigung von zwei Mengen.
    Nicht das Gleiche wie eine Addition!

    from collections import Counter
     
    def gemeinsam_counter(a, b, c):
      print('a + b', Counter(a) + Counter(b))
      print('a & b', Counter(a) & Counter(b))
      print('a | b', Counter(a) | Counter(b))
     
    gemeinsam_counter(a, b, c) 

    Zusammenfassung

    Mengenoperationen sind eine tolle Sache und helfen dir nicht nur einfach, sondern auch performant verschiedene Mengen, bzw Listen miteinander zu vergleichen. 
    Mit einem Set bist du eingeschränkt, dir werden alle doppelten Einträge entfernt.
    Aber dank der Klasse Counter kannst du die selben Operationen genauso nutzen, ohne doppelte Einträge einzubüßen.
    Darüber hinaus bekommst du sehr schnell und einfach einen Überblick über Häufigkeiten

    Kanntest du die Counter Klasse schon?
    Schreib mir mal deine Meinung dazu unten in die Kommentare! Ich freu mich schon darauf.

    Beitragsbild - Collections - NamedTuple
    mehr lesen »
    Beitragsbild - Anaconda installieren
    mehr lesen »
    Beitragsbild - Collections - DefaultDict
    mehr lesen »

    Kommentar verfassen

    Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

    Scroll to Top