Python-Kurs / CSV-Daten mit pandas prüfen, gruppieren und speichern

Mit pandas verarbeiten Sie tabellarische Daten als DataFrame. Trennen Sie Einlesen, Prüfung, Aggregation und Speichern und zählen Sie abgelehnte Werte mit, damit erkennbar bleibt, welche Datensätze eine Berichtssumme tatsächlich umfasst.

Spaltentypen vor der Auswertung wählen

Ein DataFrame ist eine Tabelle mit Zeilen und Spalten; eine Series entspricht im folgenden Aufbau einer einzelnen Spalte. Beim CSV-Einlesen kann eine automatische Typannahme von Ihrer fachlichen Regel abweichen. Wenn etwa die Kennung 001 erhalten bleiben soll, lesen Sie sie von Beginn an als Zeichenfolge und wandeln Sie nur die Betragsspalte gezielt in Zahlen um.

pandas gehört nicht zur Standardbibliothek. Installieren Sie eine zur Python-Version und zum Betriebssystem passende Ausgabe in Ihrer Projektumgebung:

python -m pip install pandas

Verwenden Sie für Installation und Ausführung denselben Interpreter. Falls import pandas später scheitert, prüfen Sie die gewählte Umgebung, statt das Paket ungeprüft global zu installieren. Die Referenz zu read_csv beschreibt insbesondere Datentypen und die Behandlung fehlender Werte.

Das Beispiel liest zunächst alle Spalten als string und schaltet mit keep_default_na=False die üblichen Standardmarkierungen für fehlende Werte aus. So bleibt etwa ein Kennungstext wie NA zunächst Text. Das ist eine bewusste Einleseregel; sie ersetzt keine Prüfung, welche Kennungen und Kategorien Ihre Anwendung zulässt.

Eine vollständige Prüfung und Kategoriesumme ausführen

Speichern Sie die Datei pandas_demo.py mit UTF-8 in einem eigenen Übungsordner und starten Sie python pandas_demo.py in der Umgebung mit installiertem pandas. Unter macOS oder Ubuntu kann außerhalb einer virtuellen Umgebung der Interpreterbefehl python3 heißen.

from io import StringIO
import pandas as pd

raw = "id,category,amount\ne1,food,3500\ne2,food,8000\ne3,transport,2500\ne4,food,bad\n"
df = pd.read_csv(StringIO(raw), dtype="string", keep_default_na=False)
amounts = pd.to_numeric(df["amount"], errors="coerce")
valid = amounts.notna() & (amounts >= 0) & (amounts % 1 == 0)
clean = df.loc[valid].copy()
clean["amount"] = amounts.loc[valid].astype("int64")
report = clean.groupby("category", as_index=False, sort=True)["amount"].sum()
print("accepted:", len(clean), "rejected:", int((~valid).sum()))
for row in report.itertuples(index=False):
    print(row.category, row.amount)

Erwartete Ausgabe:

accepted: 3 rejected: 1
food 11500
transport 2500

Die Eingabedaten stehen hier in StringIO, also einer Datei-ähnlichen Textquelle im Speicher. to_numeric versucht anschließend, nur die Betragsspalte umzuwandeln. errors="coerce" macht aus nicht lesbaren Zahlen wie bad einen fehlenden Zahlenwert. Dieser Wert wird nicht automatisch null.

Die Maske valid verlangt einen vorhandenen, nichtnegativen und ganzzahligen Zahlenwert. Die mit & verbundenen Bedingungen gelten elementweise; ihre Klammern sind für die eindeutige Auswertung wichtig. df.loc[valid] wählt passende Zeilen. Das anschließende copy() liefert eine eigene ausgewählte Tabelle, bevor deren Betragsspalte neu zugewiesen wird.

groupby bildet hier Kategoriesummen über den angenommenen Bestand. as_index=False belässt die Kategorie als Spalte, und sort=True legt deren Reihenfolge fest. Das ist eine andere Gruppierungsoperation als itertools.groupby über unmittelbar benachbarte Elemente. itertuples(index=False) liefert für die Ausgabe Zeilen ohne zusätzlichen Indexwert.

Fehlende Werte und den begrenzten Prüfvertrag verstehen

Die Maske prüft numerische Werte, aber nicht die vollständige Textschreibweise. 0 wird angenommen, -1 und 1.5 werden verworfen. Eine Schreibweise wie 1.0 oder 1e3 kann dagegen als ganzzahliger Zahlenwert bestehen. Wenn nur Zifferntexte zulässig sind, benötigen Sie vor der Umwandlung eine zusätzliche Textregel.

Ebenso fehlen hier Regeln für leere Kennungen, Kategorien und doppelte IDs. Ein zweimal gelieferter Datensatz kann deshalb zweimal in die Summe eingehen. Die Anzahl „accepted“ bedeutet im Programm lediglich, dass die numerische Maske wahr war. Ein Bericht mit richtigen Rechenergebnissen ist damit noch kein Nachweis vollständiger Datenqualität.

Das Beispiel setzt kleine Beträge voraus, die zuverlässig in int64 passen. Sowohl eine Umwandlung sehr großer Zahlen als auch eine spätere Summe brauchen eine eigene Bereichs- und Genauigkeitsregel. Ein formal ganzzahlig erscheinender Gleitkommawert beweist bei großen Zahlen keine ursprüngliche exakte Ganzzahl. Die Referenz zu to_numeric weist auf mögliche Genauigkeitsverluste bei großen Eingaben hin.

Wenn Sie eine echte Datei verwenden, ersetzen Sie die DataFrame-Erstellung beispielsweise durch pd.read_csv("expenses-input.csv", dtype={"id":"string", "category":"string", "amount":"string"}, keep_default_na=False), um die Einleseregel des Beispiels beizubehalten. Ohne keep_default_na=False können Standardtexte wie NA und leere Felder zu fehlenden Werten werden. Das kann auch die Kategorie-Gruppierung beeinflussen. Legen Sie deshalb die gewünschte Regel ausdrücklich fest.

Bewahren Sie abgelehnte Zeilen für eine Diagnose getrennt auf, etwa über rejected = df.loc[~valid].copy(). Füllen Sie fehlende Beträge nicht pauschal mit null auf: Dann wären „kein lesbarer Betrag“ und „tatsächlicher Nullbetrag“ nicht mehr unterscheidbar. Bei realen Daten ergänzen Sie die konkreten Ablehnungsgründe und passende Identitätsregeln.

Den Bericht speichern und mit der Eingabe abstimmen

Fügen Sie nach der Berichtserstellung am Ende derselben Programmdatei diese Zeile hinzu:

report.to_csv("category-report.csv", index=False, encoding="utf-8")

Es entsteht eine CSV mit den Spalten category,amount und den Zeilen food,11500 sowie transport,2500. index=False verhindert eine zusätzliche Spalte für den DataFrame-Index. Der angegebene Pfad ist relativ zum Arbeitsverzeichnis. Verwenden Sie einen neuen bewusst gewählten Ausgabepfad, weil der Standardmodus eine bereits vorhandene Datei ersetzt. Die Referenz zu to_csv beschreibt auch den Modus x, falls Sie eine vorhandene Datei ausdrücklich ablehnen möchten.

  1. Vergleichen Sie angenommene und abgelehnte Anzahl mit allen vier Eingabezeilen: Drei plus eins muss vier ergeben. Stimmen Sie zusätzlich die angenommenen Beträge und Berichtssummen auf 14.000 ab.
  2. Ändern Sie nur bad auf 0. Nun müssen vier Zeilen angenommen und keine verworfen werden; die Summe bleibt 14.000.
  3. Ändern Sie diesen Wert auf -1. Er muss wieder verworfen werden. Stellen Sie danach bad wieder her und prüfen Sie die ursprüngliche Ausgabe.
  4. Lesen Sie den gespeicherten Bericht erneut ein und prüfen Sie Spalten und Werte. Kontrollieren Sie nicht nur die Existenz der Datei.

Wenn Sie statt eines DataFrames eine ausdrückliche zeilenweise Prüfung wünschen, zeigt das CSV-Berichtsprojekt mit der Standardbibliothek eine Alternative mit Kennungsprüfung und Ablehnungsgründen. Wählen Sie den Ansatz danach, welche Regeln und Datenmengen Sie tatsächlich bearbeiten müssen.