mitmario.dev

Tabellen als Listen von Dictionaries

Python Sandbox 4 Min Lesezeit 5 BeispieleLektion 4 von 7

Ein Haushaltsbuch ist eine Tabelle. Jede Zeile ist eine Ausgabe, und jede Ausgabe hat dieselben Spalten. An welchem Tag, wofür, wie viel. In einer Tabellenkalkulation stehen die Spalten nebeneinander und haben oben eine Überschrift. In Python gibt es zwei naheliegende Wege, so eine Zeile aufzuschreiben, und der zweite ist der übliche.

Eine Zeile mit Namen

Eine Zeile mit Namen
zeile = ("28.09.", "Lebensmittel", 31.80)
print(zeile[2])

zeile = {"datum": "28.09.", "kategorie": "Lebensmittel", "betrag": 31.80}
print(zeile["betrag"])
print(zeile)

Als Tupel geht es, und zeile[2] ist der Betrag. Nur musst du dafür wissen, dass der Betrag an Position 2 steht. Wer den Code in drei Monaten liest, weiß das nicht mehr.

Als Dictionary hat jede Spalte ihren Namen, und bei zeile["betrag"] ist klar, was gemeint ist. Die Schlüssel sind die Überschriften der Tabelle und die Werte der Inhalt einer Zeile.

Die Spaltennamen stehen in diesem Kurs klein und ohne Umlaute da, wie Variablennamen. Pflicht ist das nicht. Aber dann musst du nie überlegen, ob die Spalte nun Betrag oder betrag hieß. Wenn du danebenliegst, gibt es einen KeyError, hier KeyError: 'Betrag'.

Die Tabelle als Liste

Eine Woche als Liste
ausgaben = [
    {"datum": "28.09.", "kategorie": "Lebensmittel", "betrag": 31.80},
    {"datum": "29.09.", "kategorie": "Freizeit", "betrag": 18.00},
    {"datum": "30.09.", "kategorie": "Mobilität", "betrag": 2.90},
    {"datum": "02.10.", "kategorie": "Freizeit", "betrag": 7.50},
    {"datum": "03.10.", "kategorie": "Lebensmittel", "betrag": 18.60},
]
print(len(ausgaben))
print(ausgaben[1])
print(ausgaben[1]["kategorie"])
for ausgabe in ausgaben:
    print(f"{ausgabe['datum']}  {ausgabe['kategorie']:<14}{ausgabe['betrag']:>6.2f}")

Die ganze Tabelle ist eine Liste aus solchen Zeilen. Eine Woche Ausgaben ist also eine Liste aus fünf Dictionaries. Damit es lesbar bleibt, steht jedes in einer eigenen Zeile des Codes, wie die Listen über mehrere Zeilen in Lektion 5.1.

Gelesen wird von außen nach innen. ausgaben[1] ist die zweite Zeile der Tabelle, ein ganzes Dictionary, und ausgaben[1]["kategorie"] ist darin die Kategorie. Zwei Paar eckige Klammern hintereinander, und mit jedem Paar geht es eine Ebene tiefer. Erst kommt die Position in der Liste, dann der Schlüssel in der Zeile.

Die Schleife geht die Tabelle Zeile für Zeile durch. In jeder Runde steht in ausgabe ein Dictionary, und die Spalten liest du über ihre Namen. Im f-String stehen die Schlüssel dabei in einfachen Anführungszeichen, weil außen doppelte stehen. Das ist die Regel aus den f-Strings, innen andere als außen.

Filtern und zusammenzählen

Nur die Freizeit
ausgaben = [
    {"datum": "28.09.", "kategorie": "Lebensmittel", "betrag": 31.80},
    {"datum": "29.09.", "kategorie": "Freizeit", "betrag": 18.00},
    {"datum": "30.09.", "kategorie": "Mobilität", "betrag": 2.90},
    {"datum": "02.10.", "kategorie": "Freizeit", "betrag": 7.50},
    {"datum": "03.10.", "kategorie": "Lebensmittel", "betrag": 18.60},
]
summe = 0
anzahl = 0
for ausgabe in ausgaben:
    if ausgabe["kategorie"] == "Freizeit":
        summe += ausgabe["betrag"]
        anzahl += 1
print(f"Freizeit: {anzahl} Ausgaben, zusammen {summe:.2f} Euro")
print(sum([a["betrag"] for a in ausgaben if a["kategorie"] == "Freizeit"]))

Filtern und Summieren gehen wie bei einer Liste aus Zahlen. Nur schauen Frage und Rechnung jeweils auf eine andere Spalte der Zeile. Im if geht es um die Kategorie und bei der Summe um den Betrag. Nebenbei zählt ein zweiter Zähler mit, wie viele Ausgaben es waren. Das ist das Zählmuster.

In eine List Comprehension passt das auch. Darin werden die Beträge der passenden Zeilen gesammelt, und sum() zählt sie zusammen. Für eine einzige Zahl ist das bequem. Brauchst du daneben noch die Anzahl oder sonst etwas, ist die Schleife übersichtlicher.

Die ganze Zeile merken

Die erste Ausgabe für Freizeit
ausgaben = [
    {"datum": "28.09.", "kategorie": "Lebensmittel", "betrag": 31.80},
    {"datum": "29.09.", "kategorie": "Freizeit", "betrag": 18.00},
    {"datum": "30.09.", "kategorie": "Mobilität", "betrag": 2.90},
    {"datum": "02.10.", "kategorie": "Freizeit", "betrag": 7.50},
    {"datum": "03.10.", "kategorie": "Lebensmittel", "betrag": 18.60},
]
print([a["betrag"] for a in ausgaben if a["kategorie"] == "Freizeit"])

erste = None
for ausgabe in ausgaben:
    if ausgabe["kategorie"] == "Freizeit":
        erste = ausgabe
        break
print(f"Zuerst Freizeit: {erste['betrag']:.2f} Euro am {erste['datum']}")
print(min([a["betrag"] for a in ausgaben]))

Mit einer List Comprehension hast du die Beträge für Freizeit schnell eingesammelt, 18,00 und 7,50 Euro. Nur weißt du danach nicht mehr, an welchem Tag sie anfielen, denn in der Liste stehen nur noch die Zahlen und nicht mehr die Zeilen, aus denen sie kamen.

Deshalb wird in der Schleife danach nicht der Betrag gemerkt, sondern die ganze Zeile. Die erste Zeile für Freizeit landet in erste, und danach ist mit break Schluss. Am Ende steht in erste ein ganzes Dictionary, und daraus kommen Betrag und Datum. Das None davor ist für eine Woche ohne Freizeit da. Dann steht in erste eben nichts, statt dass der Name ganz fehlt.

Dasselbe Problem hat min() in der letzten Zeile. Es findet 2,90 Euro als kleinste Ausgabe, aber nicht, wann das war und wofür. Wenn du die kleinste oder die größte samt Datum brauchst, merkst du dir auch dort die ganze Zeile, nur hört die Schleife dann nicht beim ersten Treffer auf. Die Idee ist dieselbe wie beim teuersten Tag in Lektion 4.3. Dort waren es zwei Dinge in zwei Namen, hier ist es eins, in dem alles drinsteht.

Liste oder Zeile

Liste oder Zeile
ausgaben = [
    {"datum": "28.09.", "kategorie": "Lebensmittel", "betrag": 31.80},
    {"datum": "29.09.", "kategorie": "Freizeit", "betrag": 18.00},
]
print(ausgaben[0]["betrag"])
print(ausgaben["betrag"])

Der häufigste Fehler mit solchen Tabellen ist, die Ebenen zu verwechseln. ausgaben["betrag"] fragt die ganze Liste nach einem Betrag, und eine Liste hat keine Schlüssel, nur Positionen. Heraus kommt TypeError: list indices must be integers or slices, not str, auf Deutsch „Positionen einer Liste müssen ganze Zahlen oder Ausschnitte sein, kein Text“. Gemeint war der Betrag einer Zeile, und an den kommst du über eine Position oder in der Schleife.

So sehen Daten übrigens aus, wenn Python sie aus einer Tabellendatei liest. In Abschnitt 12 kommen Tabellen aus CSV-Dateien, und heraus kommt genau so eine Liste von Dictionaries. Bei CSV sind die Werte darin erst einmal Text. Zu Zahlen werden sie wie in der Aufgabe dieser Lektion mit float().

Was die Aufgabe will

Eine Woche Haushaltsbuch steht in einer Datei, eine Ausgabe je Zeile, und der Startcode macht daraus schon eine Liste von Dictionaries. Gesucht sind zwei Antworten. Wie viel ist in dieser Woche für Lebensmittel draufgegangen? Und welches war die größte einzelne Ausgabe, mit Datum und Kategorie?

Zum Mitnehmen

In Python wird eine Tabelle zu einer Liste von Dictionaries. Die Zeilen sind Dictionaries, und die Spaltennamen sind ihre Schlüssel. Eine Schleife geht die Zeilen durch, und die Spalten liest du über ihren Namen statt über eine Position.

Jetzt du

Basis Konto, kostenlos

Zu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.

Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.

In diesem Kurs läuft dein Code auf einem Server. Dafür hat das Basis Konto 1 Stunde im Monat, mehr Zeit gibt es mit dem Premium Konto.

Was in dieser Lektion steckt

  • Artikel mit 5 Beispielen zum Ausprobieren

    Steht hier, ohne Konto lesbar.

  • Aufgabe, dein Code läuft auf einem Server

    Öffnet sich mit dem Basis Konto.