Typen und fehlende Werte
Kurz gesagt: Jede Spalte in pandas hat einen Typ (dtype), der entscheidet, was du damit tun kannst, und fehlende Werte (NaN) findest du mit isna und füllst sie mit fillna oder entfernst sie mit dropna.
Problem
„An welchem Tag wird am meisten bestellt?“
Der Chef will wissen, wann mehr Leute im Lager sein sollen. Die Datei year_orders.csv hat 11.116 Bestellungen eines Jahres, jede mit Datum und Uhrzeit. Es sieht aus wie ein einziger Befehl: den Tag aus dem Datum nehmen und zählen.
Ein Fehler. dtypes sagt, warum: ordered_at ist str, Text. Für uns ist „2025-11-01 07:20“ ein Datum, aber read_csv sieht Ziffern, Striche und einen Doppelpunkt und rät nicht.
Der Typ einer Spalte entscheidet, was du damit tun kannst. In dieser Lektion siehst du, wie man ihn ändert, welche Typen Speicher sparen und eine Reihenfolge geben und was passiert, wenn ein Wert fehlt.
Die Idee
Jede Spalte hat einen Typ: astype
Jede Spalte eines DataFrames hat einen Typ (dtype). Du hast int64 und float64 für Zahlen und str für Text gesehen. Es gibt auch bool für True und False, datetime64 für Datumsangaben und category für wenige Werte, die sich wiederholen.
read_csv rät den Typ aus dem Text in der Datei. Wenn es falsch rät oder du einen anderen brauchst, macht astype eine Spalte eines neuen Typs. In products ist die Kategorie float64, weil die Geschenkkarte keine Kategorie hat und NaN eine Dezimalzahl ist. Ganze Zahlen mit Lücken hält der Typ Int64 (mit großem I):
Die Zahlen sind wieder ganz, und die Lücke ist <NA>: das Zeichen für „nichts“ in den neueren pandas-Typen. Mit astype(int) bekämst du den Fehler IntCastingNaNError, weil ein gewöhnliches int keinen Platz für eine Lücke hat.
Sieh es dir an
Wenige Werte, viele Zeilen: category
Die Spalte status hat 11.116 Zeilen, aber nur fünf verschiedene Wörter. Als Text wird jedes Wort in jeder Zeile gespeichert. Der Typ category hält die fünf Wörter einmal und für jede Zeile nur die Nummer ihres Wortes:
377.057 Byte werden zu 11.345, etwa 33-mal weniger. Im Jahr des Shops ist das wenig, aber in einer Tabelle mit Millionen Zeilen ist es der Unterschied zwischen „passt in den Speicher“ und „passt nicht“.
Eine Kategorie hat noch einen zweiten Nutzen: eine Reihenfolge. Die Status sind kein Alphabet, sondern ein Weg: new, paid, shipped, delivered. CategoricalDtype mit ordered=True legt diese Reihenfolge fest:
value_counts(sort=False) zählt in der Reihenfolge des Wegs, nicht nach Häufigkeit. Der Vergleich < 'delivered' heißt „vor der Lieferung“: 205 Bestellungen sind noch unterwegs. Die stornierten stehen am Ende, abseits des Wegs.
Probier es aus
Die Status entlang des Wegs
Mach die Spalte status zu einer Kategorie mit der Reihenfolge new, paid, shipped, delivered, cancelled und lege in status_counts ab, wie viele Bestellungen es auf jeder Stufe gibt, in dieser Reihenfolge.
Lösung
import pandas as pd
orders = pd.read_csv('year_orders.csv')
steps = pd.CategoricalDtype(['new', 'paid', 'shipped', 'delivered', 'cancelled'], ordered=True)
orders['status'] = orders['status'].astype(steps)
status_counts = orders['status'].value_counts(sort=False)
status_countspd.CategoricalDtype(['new', 'paid', 'shipped', 'delivered', 'cancelled'], ordered=True) legt die Reihenfolge fest, astype(steps) setzt sie auf die Spalte, und value_counts(sort=False) zählt entlang davon: 90 neue, 63 bezahlte, 52 versendete, 10.254 gelieferte und 657 stornierte. Mit einem gewöhnlichen value_counts() ist die Reihenfolge nach Häufigkeit, mit delivered oben. Mit astype('category') und ohne Liste ist die Reihenfolge alphabetisch: cancelled, delivered, new, paid, shipped.
Die Idee
Datumsangaben: pd.to_datetime und .dt
pd.to_datetime macht aus dem Text Datumsangaben. Die Spalte bekommt den Typ datetime64 und kennt jetzt den Kalender. Die Teile eines Datums kommen aus .dt: .dt.year, .dt.month, .dt.day_name() und viele mehr:
Das Jahr läuft vom 1. November 2025, 7:20 Uhr, bis zum 31. Oktober 2026, 23:01 Uhr. Die erste Bestellung ist an einem Samstag (Saturday: day_name() liefert die englischen Namen der Wochentage). .dt funktioniert nur bei Datumsangaben: Beim Text am Anfang hat es den Fehler geliefert.
Mehr zu Datumsangaben (nach Woche, nach Monat, ein gleitender Durchschnitt) steht in der Lektion über Zeit.
Die Idee
Text: .str
Was .dt für Datumsangaben ist, ist .str für Text: Es wendet eine String-Methode auf jede Zeile auf einmal an. str.contains hast du bei den Filtern gesehen. Es gibt auch str.lower() und str.upper(), str.strip() (entfernt die Leerzeichen an den Enden), str.len(), str.split() und das Schneiden wie bei einem String: str[:13].
Die Regaletiketten fassen 13 Zeichen. Welche Namen passen nicht, und wie sehen sie abgeschnitten aus?
4 Namen sind länger als 13 Zeichen. str[:13] schneidet sie ab, und str.upper() setzt sie für das Etikett in Großbuchstaben. Das Ergebnis ist wieder eine Spalte, also setzt assign sie neben den Namen.
Die Idee
Fehlende Werte: isna und notna
Fast jede echte Tabelle hat etwas, das fehlt: Ein Kunde hat seine Stadt nicht angegeben, die Kasse hat keinen Bericht geschickt. pandas schreibt die Lücke als NaN (NaT bei Datumsangaben, <NA> bei den neueren Typen). isna() liefert eine Maske, die True ist, wo ein Wert fehlt, und .sum() darauf zählt die Lücken in jeder Spalte:
Ein Kunde hat keine Stadt. notna() ist die umgekehrte Maske: True, wo ein Wert da ist. Mit beiden Masken filterst du wie mit jeder Bedingung.
Sieh es dir an
Warum 0 nicht „fehlend“ ist
Die Banitsa und die Gurken haben den Bestand 0. Ist das eine Lücke?
isna() findet in stock nichts: Die Nullen sind echte Werte. 0 heißt „wir wissen, dass keines da ist“, und NaN heißt „wir wissen es nicht“.
Jetzt die Einnahmen pro Tag aus der Lektion über Zusammenfassungen, aber als pandas-Series. Ein wichtiger Unterschied: NumPy lieferte für den Mittelwert nan, und pandas überspringt die Lücken:
count() zählt 6 bekannte Tage von 7. Ihr Mittelwert ist 494,70 €, wie bei np.nanmean. Die Summe, 2.968,20 €, lässt aber still den Mittwoch aus: pandas warnt dich nicht. Mit fillna(0) sinkt der Mittelwert auf 424,03 €, weil die Null sagt „am Mittwoch haben wir nichts verkauft“, und das stimmt nicht.
Genauer, für Neugierige
Das Überspringen steuert der Parameter skipna: revenue.mean(skipna=False) liefert nan, wie NumPy. Das ist nützlich, wenn du lieber hättest, dass das Ergebnis eine Lücke anzeigt, statt still eine kleinere Zahl zu liefern.
Die Idee
fillna, dropna oder nichts
Es gibt drei Wege, mit einer Lücke umzugehen, und die Wahl hängt von der Frage ab, nicht vom Code:
fillna(wert), wenn du weißt, was die Lücke bedeutet. In einem Bericht „Kunden nach Stadt“ ist eine fehlende Stadt „unbekannt“: Das ist eine Antwort, keine Erfindung.dropna(), wenn die Zeile die Frage nicht beantworten kann. In einem Bericht nach Kategorie weiß die Geschenkkarte nicht, wohin.subset=[...]schaut nur auf die angegebenen Spalten.- Nichts, wenn du es nicht weißt. Die Einnahmen vom Mittwoch sind nicht 0, und der Mittelwert der bekannten Tage ist ehrlicher.
dropna hat die Geschenkkarte entfernt: Von 25 Produkten bleiben 24, so viele, wie notna() bei der Kategorie zählt. In einem Bericht „wie viele Produkte wir verkaufen“ wäre das ein Fehler. fillna('unbekannt') macht die fehlende Stadt in value_counts zu einer eigenen Zeile.
Probier es aus
Kunden nach Stadt
Das Marketing will wissen, woher die Kunden kommen. Lege in missing_city ab, wie viele Kunden keine Stadt haben, und in by_city, wie viele Kunden es in jeder Stadt gibt, wobei die fehlende Stadt als „unbekannt“ gezählt wird.
Lösung
import pandas as pd
customers = pd.read_csv('customers.csv')
missing_city = customers['city'].isna().sum()
by_city = customers['city'].fillna('unbekannt').value_counts()
print(missing_city)
by_citycustomers['city'].isna().sum() liefert 1. customers['city'].fillna('unbekannt').value_counts() zählt alle 20 Kunden: 6 in Sofia, 4 in Plovdiv und so weiter, und 1 „unbekannt“. Ohne fillna überspringt value_counts NaN und zählt 19, und count() liefert 19, die Zahl der Kunden mit Stadt, nicht ohne.
Übung
Der Tag mit den meisten Bestellungen
Beantworte die Frage des Chefs. Mach ordered_at zu einem Datum, lege den Namen des Wochentags in eine neue Spalte day, lege in by_day ab, wie viele Bestellungen es an jedem Tag gibt, und in best_day den Tag mit den meisten Bestellungen.
Lösung
import pandas as pd
orders = pd.read_csv('year_orders.csv')
orders['ordered_at'] = pd.to_datetime(orders['ordered_at'])
orders['day'] = orders['ordered_at'].dt.day_name()
by_day = orders['day'].value_counts()
best_day = by_day.index[0]
print(best_day)
by_daypd.to_datetime macht die Datumsangaben, und .dt.day_name() liefert den Tag. value_counts() sortiert von den meisten zu den wenigsten, also ist by_day.index[0] Friday (Freitag): 1.890 Bestellungen. Am schwächsten ist der Sonntag (Sunday) mit 1.280. Gezählt sind alle Bestellungen, auch die stornierten: Die Frage ist, wann sie kommen, nicht was sie wert sind. Mit .dt.day bekämst du 31 Zeilen, eine für jeden Tag des Monats.
Test
Teste dich
Verwandte Lektionen
Abschnitt „Verwandte Lektionen“- Baut auf: Series und DataFrame · Auswählen: [], loc und iloc · Filtern und Sortieren · Neue Spalten
- Geht weiter in: Gruppen: groupby · Verbinden: merge · Breit und lang: pivot_table und melt · Datumswerte und Zeitreihen
- Aufgaben zum Thema: Tabellen mit pandas