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:
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:
- Ansehen, was nicht stimmt:
dtypes,describe,value_counts, Sortieren. - Korrigieren, mit einem Schritt für jede Art von Schmutz: Duplikate, Text, Zahlen, Datumsangaben, Ausreißer.
- 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:
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:
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.
Lösung
import pandas as pd
prices = pd.read_csv('supplier.csv')
prices['name'] = prices['name'].str.strip().str.lower()
prices = prices.drop_duplicates()
rows = len(prices)
print(rows)prices['name'].str.strip().str.lower() gleicht die Namen an, und drop_duplicates() entfernt dann die zwei wiederholten Zeilen: Von 16 bleiben rows = 14. Wenn du zuerst die Duplikate entfernst, bleiben 15: Dann sind die zwei Milchzeilen noch verschieden, und drop_duplicates entfernt nur den Käse.
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:
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.
Lösung
import pandas as pd
prices = pd.read_csv('supplier.csv')
text = prices['price'].str.replace('€', '').str.replace(',', '.').str.strip()
prices['price'] = pd.to_numeric(text, errors='coerce')
print(prices['price'].isna().sum())str.replace('€', ''), dann str.replace(',', '.') und str.strip() machen aus „0,95 €“ „0.95“, und pd.to_numeric(text, errors='coerce') macht daraus 0.95. Es bleiben 2 NaN: die Äpfel und das Wasser. Mit nur to_numeric(..., errors='coerce') werden 11 von 16 zu NaN, weil jeder Preis mit Komma stillschweigend verschwindet und der Code gar keinen Fehler meldet.
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:
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:
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.
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:
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:
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:
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.
Lösung
import pandas as pd
prices = pd.read_csv('supplier.csv')
prices['name'] = prices['name'].str.strip().str.lower()
prices = prices.drop_duplicates()
text = prices['price'].str.replace('€', '').str.replace(',', '.').str.strip()
prices['price'] = pd.to_numeric(text, errors='coerce')
dots = pd.to_datetime(prices['valid_from'], format='%d.%m.%Y', errors='coerce')
iso = pd.to_datetime(prices['valid_from'], format='%Y-%m-%d', errors='coerce')
prices['valid_from'] = dots.fillna(iso)
cents = prices['price'] > 100
prices.loc[cents, 'price'] = prices.loc[cents, 'price'] / 100
assert prices['code'].is_unique
assert prices['price'].isna().sum() == 2
avg_price = round(prices['price'].mean(), 2)
print(avg_price)Die vier Schritte in dieser Reihenfolge ergeben 14 Zeilen, 2 Preise NaN und 1 Datum NaT. round(prices['price'].mean(), 2) ist 4,53 €, und der Kaffee kostet 11,90 €. Ohne die Kaffee-Korrektur ist der Mittelwert 102,70 €. Mit fillna(0) „kosten“ das Wasser und die Äpfel 0 € und ziehen den Mittelwert herunter, und mit dropna() verlierst du auch die Bananen, die einen richtigen Preis haben.
Test
Teste dich
Verwandte Lektionen
Abschnitt „Verwandte Lektionen“- Baut auf: Auswählen: [], loc und iloc · Filtern und Sortieren · Neue Spalten · Typen und fehlende Werte
- Geht weiter in: Datenleck
- Aufgaben zum Thema: Daten aufbereiten