fbpx

Python ERWEITERTE Datentypen – Was ist Counter?

Inhalt
    Add a header to begin generating the table of contents

    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!
    Los geht es mit Listen und der Klasse Counter.

    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 herauszuholen. 
    Bleib also unbedingt bis zum Ende dran, um nichts zu verpassen!

    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 weißt 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 durchzugehen, 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.
    Dafür kommt eine List-Comprehension zum Einsatz:

    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
    Bild – Inner Join

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

    Das Problem mit dem Set…

    Jetzt gebe 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 zweimal 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 liefert: 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: Counter

    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 vorkommt 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, 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, wie viele 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 voneinander abziehen!
    Sieh mal hier, die Menge b.
    Und die ziehst du jetzt von deinem Ergebnis 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 zweimal vor und die 3 einmal.
    Davon ziehst du jetzt die Menge b ab.
    Hier kommt die 2 zweimal 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 dieselben 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.

    Picture of Ingo Janßen

    Ingo Janßen

    Lerne nicht einfach programmieren. Löse Probleme und automatisiere Aufgaben!

    Das könnte dich auch interessieren

    Nach oben scrollen
    Newsletter Popup Form

    Keine Inhalte mehr verpassen?

    Melde dich direkt für den "Code-Kompass" an und erhalte nützliche Tipps und Informationen direkt in deinen Posteingang.