Zum Inhalt
wannacode

Daten bereinigen

Kurz gesagt: Beim Bereinigen entfernst du Duplikate, bringst Preise, Datumsangaben und Namen in ein einheitliches Format und prüfst im Code, dass die Daten danach stimmen.

Problem

Die neue Preisliste

Ein neuer Lieferant hat seine Preisliste als CSV geschickt. Der Chef fragt zwei Dinge: Wie viel zahlen wir im Durchschnitt pro Produkt, und was kostet uns der Kaffee? Die Datei supplier.csv liegt neben dem Code. Hier ist sie:

PythonDaten: die Tabellen des Shops

Alle vier Spalten sind str. Die Preise sind „0,95 €“ (der Lieferant schreibt Preise kontinental, mit Dezimalkomma), „1.45“, leer und „auf Anfrage“. Die Datumsangaben haben zwei Formate. Der Weißkäse steht zweimal drin. Die Namen haben Großbuchstaben, Kleinbuchstaben und Leerzeichen. Der Kaffee kostet 1190.

prices['price'].mean() startet gar nicht erst: Die Preise sind Text. Solche Daten heißen unsauber, und das ist der Normalfall, keine Ausnahme. Die Software des Lieferanten schreibt, was sie schreibt, und Menschen tragen Dinge von Hand ein.

Die Idee

Bereinigen ist Code, keine Handarbeit

Die Versuchung ist, die Datei zu öffnen und die Zellen von Hand zu korrigieren. Aber in einem Monat schickt der Lieferant eine neue Liste mit denselben Fehlern. Also ist Bereinigen Code: Er läuft bei jeder neuen Datei wieder.

Der Weg ist immer derselbe:

  1. Ansehen, was nicht stimmt: dtypes, describe, value_counts, Sortieren.
  2. Korrigieren, mit einem Schritt für jede Art von Schmutz: Duplikate, Text, Zahlen, Datumsangaben, Ausreißer.
  3. Im Code prüfen, dass es geklappt hat: die Anzahl der Zeilen vorher und nachher, NaN nur dort, wo es sein soll.

Jeder Schritt dieser Lektion gehört zu einer Art von Schmutz in der Liste.

Die Idee

Duplikate: duplicated und drop_duplicates

duplicated() liefert eine Maske: True für jede Zeile, die eine Zeile weiter oben wiederholt. .sum() zählt sie. Mit subset= vergleicht es nur die angegebenen Spalten, und keep=False markiert alle Kopien, nicht nur die zweite:

PythonDaten: die Tabellen des Shops

Es gibt eine ganz gleiche Zeile: den Weißkäse, P07. Nach dem Code sind es aber zwei Wiederholungen. Frischmilch, P05, steht zweimal drin, einmal als „FRISCHMILCH“ und einmal als „ Frischmilch“, mit einem Leerzeichen davor. Für pandas sind das verschiedene Texte, also fängt duplicated() es nicht.

drop_duplicates() entfernt die Wiederholungen und behält die erste Zeile. Wenn du es aber jetzt ausführst, bleibt die Milch zweimal.

Die Idee

Namen: str.strip und str.lower

Text bereinigst du mit .str aus der Lektion über Typen. str.strip() entfernt die Leerzeichen an den Enden, und str.lower() macht alle Buchstaben klein. So werden „FRISCHMILCH“ und „ Frischmilch“ zum selben Text:

PythonDaten: die Tabellen des Shops

Von 16 Zeilen bleiben 14: Der Käse und die Milch sind entfernt. Die Reihenfolge der Schritte zählt: erst den Text bereinigen, dann nach Duplikaten suchen.

Genauer, für Neugierige

str.lower() ist zum Vergleichen da. Für eine Beschriftung oder einen Bericht macht str.capitalize() den ersten Buchstaben groß: „frischmilch“ wird zu „Frischmilch“. Wenn sich Namen auch in der Mitte unterscheiden (zwei Leerzeichen zwischen Wörtern), gleichen str.replace(' ', ' ') oder str.split().str.join(' ') das aus.

Probier es aus

Ohne Wiederholungen

Bereinige die Namen in der Liste (keine Leerzeichen an den Enden, Kleinbuchstaben), entferne die wiederholten Zeilen und speichere in rows, wie viele Zeilen bleiben.

PythonDaten: die Tabellen des Shops

Die Idee

Preise aus Text: str.replace und pd.to_numeric

„0,95 €“ ist aus drei Gründen Text: das €-Zeichen, das Komma statt des Punkts und das Leerzeichen. str.replace entfernt jedes davon. Dann macht pd.to_numeric aus dem Text eine Zahl. Mit errors='coerce' wird alles, was immer noch keine Zahl ist, zu NaN, statt den Code zu stoppen:

PythonDaten: die Tabellen des Shops

Der Preis ist float64, und es gibt zwei NaN: die Äpfel (ein leeres Feld) und das Mineralwasser („auf Anfrage“). Das ist richtig: Der Lieferant hat dafür keinen Preis genannt. Ohne errors='coerce' stoppt to_numeric mit ValueError: Unable to parse string "auf Anfrage".

errors='coerce' ist praktisch und gefährlich: Es sagt nicht, was es weggeworfen hat. Deshalb zählst du NaN vorher und nachher. Wenn es nach der Umwandlung mehr sind als leere Felder, ist etwas aus Versehen zu NaN geworden.

Probier es aus

Preise als Zahlen

Mach aus der Spalte price eine Zahl. NaN soll nur dort bleiben, wo der Lieferant keinen Preis genannt hat.

PythonDaten: die Tabellen des Shops

Sieh es dir an

Datumsangaben in zwei Formaten

Die Hälfte der Datumsangaben ist „01.11.2026“ (Tag, Monat, Jahr), die andere Hälfte „2026-11-01“. Wenn du pandas raten lässt:

PythonDaten: die Tabellen des Shops

pandas rät das Format am ersten Datum und liest es als %m.%d.%Y: zuerst der Monat, wie in den USA. Beim ISO-Datum bricht es mit einem Fehler ab. Wenn alle Datumsangaben Punkte hätten, gäbe es keinen Fehler: Aus 01.11. würde stillschweigend der 11. Januar.

Also gibst du das Format selbst an, mit format=, einmal für jedes Format. Mit errors='coerce' werden die Datumsangaben im anderen Format zu NaT, und fillna führt die beiden Lesedurchgänge zusammen:

PythonDaten: die Tabellen des Shops

13 Zeilen gelten ab dem 1. November und 2 ab dem 15. November. Ein Datum lässt sich mit keinem der beiden Formate lesen: der 31.11.2026 bei den Bananen. Diesen Tag gibt es nicht, der November hat 30 Tage.

1. Nach den zwei Lesedurchgängen für die Datumsangaben bleibt 31.11.2026 NaT. Was machst du mit der Zeile der Bananen?

Sieh es dir an

Ausreißer: describe und Sortieren

Die Preise sind jetzt Zahlen. describe() vom ersten Blick sagt dir, ob sie plausibel aussehen, und das Sortieren zeigt, welche die größten sind:

PythonDaten: die Tabellen des Shops

Der Mittelwert ist 102,70 €, der Median 1,62 €. Ein solcher Abstand bedeutet, dass ein Wert weit von den anderen entfernt ist. Das nennt man einen Ausreißer. Das Sortieren findet ihn sofort: die Kaffeebohnen für 1190 €. Wir verkaufen den Kaffee für 14,90 €, also ist der Preis des Lieferanten 11,90 €: Seine Software hat das Komma verloren.

Die Korrektur ist eine Regel für alle Preise über 100 €. Du schreibst sie in den Code, nicht in eine Zelle, damit sie denselben Fehler auch in der nächsten Liste fängt:

PythonDaten: die Tabellen des Shops

Der Kaffee kostet 11,90 €, der Mittelwert ist auf 4,53 € gefallen, und der Median blieb 1,62 €. Der Median spürt eine einzelne riesige Zahl nicht, der Mittelwert spürt sie ganz.

Genauer, für Neugierige

Die Regel „über 100 € heißt verlorenes Komma“ stimmt für diese Liste, weil wir die Produkte kennen: Nichts darin kostet mehr als 100 €. In einer anderen Liste (Fernseher, Möbel) würde dieselbe Regel richtige Preise kaputt machen. Wenn du nicht weißt, warum ein Wert seltsam ist, frag nach seiner Herkunft, statt ihn zu korrigieren.

Die Idee

Prüfungen als Code: assert

Die Bereinigung ist fertig, wenn du sie beweisen kannst. assert Bedingung, 'Meldung' tut nichts, wenn die Bedingung wahr ist, und stoppt den Code mit einem AssertionError und der Meldung, wenn nicht. Hier ist die ganze Bereinigung mit den Prüfungen am Ende:

PythonDaten: die Tabellen des Shops

Von 16 Zeilen bleiben 14. Die Prüfungen sagen laut, was du erwartest:

  • jeder Code kommt einmal vor;
  • ein Preis fehlt nur bei den zwei Produkten ohne Preis;
  • jeder Preis liegt zwischen 0,10 € und 100 €;
  • nur ein Datum bleibt ungelesen.

Wenn die nächste Liste mit einem neuen Fehler kommt, stoppt die Prüfung den Code mit einer klaren Meldung, statt dass der Bericht stillschweigend falsch herauskommt.

Übung

Der mittlere Preis des Lieferanten

Beantworte die Frage des Chefs. Bereinige die ganze Liste in prices: die Namen und die Duplikate, die Preise als Zahlen, die Datumsangaben in beiden Formaten und den Kaffee ohne Komma. Die Produkte ohne Preis bleiben, mit NaN. Speichere in avg_price den mittleren Preis, auf 2 Dezimalstellen gerundet.

PythonDaten: die Tabellen des Shops

Test

Teste dich

1. Frischmilch steht zweimal in der Liste: FRISCHMILCH und Frischmilch. Wie viele Duplikate findet drop_duplicates(), bevor du die Namen bereinigst?
2. Warum ist pd.to_numeric(prices['price'], errors='coerce') allein gefährlich?
3. Der mittlere Preis ist 102,70 €, der Median 1,62 €. Was sagt dir das?
4. Was ist nach dem Bereinigen besser?

Erstellt von Ivo Gergov im Rahmen der Kampagne von Evo Tech für zugängliche Programmierausbildung ·