Fehlende Werte in Pandas 2026: Der praktische Leitfaden zur Missing-Data-Imputation

Praktischer Leitfaden zur Behandlung fehlender Werte in Pandas: von der Diagnose mit missingno über dropna/fillna bis zu KNNImputer und IterativeImputer (MICE) in scikit-learn Pipelines.

Fehlende Werte in Pandas: Guide 2026

Aktualisiert: 5. Juli 2026

Fehlende Werte in Pandas behandeln Sie in drei Schritten: (1) diagnostizieren Sie das Muster mit df.isna().sum() und missingno, (2) klassifizieren Sie den Mechanismus als MCAR, MAR oder MNAR nach Rubin (1976), und (3) wählen Sie eine passende Strategie (z. B. dropna, fillna, interpolate oder ein Imputer aus sklearn.impute). Der falsche Umgang damit führt zu verzerrten Modellen, selbst wenn die Metriken solide aussehen. Ich habe genau diesen Bug in einem Kreditscoring-Projekt gesehen: ein fillna(0) auf einer Einkommensspalte hat den AUC künstlich um 0,04 Punkte hochgeschoben, weil das Modell die Null als "kein Einkommen" gelernt hat. Der folgende Leitfaden zeigt Ihnen alle etablierten Techniken mit lauffähigem Code für pandas 2.2 und scikit-learn 1.5.

  • Pandas markiert fehlende Werte mit NaN für Floats, pd.NaT für Zeitstempel und pd.NA für die neuen Nullable-Dtypes (Int64, boolean, string).
  • Der Mechanismus (MCAR, MAR, MNAR) entscheidet, ob eine einfache oder eine modellbasierte Imputation angebracht ist – nicht die Menge der fehlenden Werte.
  • SimpleImputer ist der Baseline; KNNImputer nutzt Distanzen im Feature-Raum, IterativeImputer (MICE) modelliert jede Spalte durch die anderen.
  • Für Zeitreihen sind interpolate, ffill und bfill in fast allen Fällen den Mittelwert-Strategien überlegen.
  • Imputation gehört in eine Pipeline – niemals vor dem Train/Test-Split, sonst leaken Sie Testdaten in Ihr Modell.
  • Mit pandas 3.0 (April 2026) wird pd.NA zum einheitlichen Missing-Marker über alle Nullable-Dtypes hinweg.

Was bedeutet NaN in Pandas?

NaN (Not a Number) ist ein spezieller IEEE-754-Floating-Point-Wert, den Pandas als universellen Missing-Marker für numerische Spalten verwendet. Historisch bedeutete das: sobald eine Integer-Spalte auch nur einen fehlenden Wert enthielt, konvertierte Pandas sie automatisch zu float64. Seit Pandas 1.0 gibt es die Nullable-Dtypes (Int64, Float64, boolean, string), die pd.NA statt NaN nutzen und den Datentyp erhalten.

Wichtig ist die semantische Unterscheidung: NaN ist ein numerisches Fehlerzeichen, pd.NaT steht für "Not a Time" bei datetime64-Spalten, und pd.NA ist der neue, dtype-agnostische Marker. In der offiziellen pandas User Guide zu Missing Data wird das Verhalten für jeden Dtype im Detail beschrieben. Für ML-Workflows gilt: prüfen Sie mit df.dtypes, welchen Missing-Marker Sie verarbeiten, denn NaN != NaN (die IEEE-Semantik) führt sonst zu subtilen Bugs, insbesondere bei Joins und Vergleichen.

import pandas as pd
import numpy as np

df = pd.DataFrame({
    "alter": [34, np.nan, 41, 29],
    "einkommen": pd.array([52000, pd.NA, 71000, pd.NA], dtype="Int64"),
    "eintritt": pd.to_datetime(["2023-01-05", None, "2024-06-11", "2025-03-01"]),
})
print(df.dtypes)
# alter          float64   -> NaN
# einkommen        Int64   -> pd.NA
# eintritt  datetime64[ns] -> pd.NaT

Fehlende Werte erkennen und visualisieren

Bevor Sie imputieren, sollten Sie die Verteilung der fehlenden Werte quantifizieren und visualisieren. Die numerische Diagnose liefert df.isna().sum() pro Spalte oder df.isna().mean() * 100 für Prozentanteile. Für die grafische Analyse hat sich die Bibliothek missingno etabliert – die letzte offizielle Version 0.5.2 ist zwar aus 2023, funktioniert aber weiterhin einwandfrei mit pandas 2.2 und liefert vier komplementäre Plots.

import missingno as msno
import matplotlib.pyplot as plt

# Übersicht: welche Zeilen haben welche NaNs?
msno.matrix(df)
# Korrelation der Fehlmuster zwischen Spalten (deutet auf MAR/MNAR hin)
msno.heatmap(df)
# Dendrogramm: Cluster von gemeinsam fehlenden Spalten
msno.dendrogram(df)
plt.show()

Die msno.heatmap ist besonders wertvoll: eine Korrelation nahe +1 zwischen zwei Spalten bedeutet, dass sie fast immer gemeinsam fehlen – ein starkes Indiz für einen MAR- oder MNAR-Mechanismus. Bei zufälligem Fehlen (MCAR) sollten die Korrelationen um 0 pendeln. Ergänzen Sie diese Analyse mit einer Gruppierung, um zu prüfen, ob bestimmte Segmente überproportional betroffen sind, etwa df.groupby("region")["einkommen"].apply(lambda s: s.isna().mean()).

MCAR, MAR, MNAR: Der Rubin-Rahmen

Donald Rubin hat 1976 (Rubin, D.B., Inference and Missing Data, Biometrika 63(3)) drei Missing-Data-Mechanismen definiert, die noch heute die Basis jeder ernsthaften Imputationsstrategie bilden. Ohne dieses Vokabular können Sie eine Methode nicht rechtfertigen – und Reviewer erkennen sofort, wenn jemand blind fillna(mean) anwendet.

  • MCAR (Missing Completely At Random): Die Wahrscheinlichkeit, dass ein Wert fehlt, hängt weder von beobachteten noch von unbeobachteten Variablen ab. Beispiel: ein Sensor fällt zufällig aus. Bei MCAR ist dropna unverzerrt.
  • MAR (Missing At Random): Das Fehlen hängt von beobachteten Variablen ab. Beispiel: ältere Kunden geben ihr Einkommen seltener an. Modellbasierte Imputer (KNN, MICE) sind hier ideal.
  • MNAR (Missing Not At Random): Das Fehlen hängt vom fehlenden Wert selbst ab. Beispiel: Personen mit sehr hohem Einkommen verweigern die Angabe. MNAR erfordert domänenspezifische Modelle oder Sensitivitätsanalysen; einfache Imputation verzerrt systematisch.

In der Praxis testen wir MCAR mit dem Little-MCAR-Test (pyampute oder rpy2 + R-Paket naniar). Wenn der p-Wert < 0.05 ist, verwerfen wir die MCAR-Hypothese. Ein pragmatischer Ansatz: erstellen Sie eine Missing-Indicator-Spalte (df["einkommen_missing"] = df["einkommen"].isna().astype(int)) und prüfen Sie deren Feature-Importance in einem Baseline-Modell. Ist der Indikator prädiktiv, liegt kein MCAR vor – und Sie sollten dieses Signal in Ihr Modell einbauen.

Wann sollte man dropna oder fillna verwenden?

dropna ist die richtige Wahl, wenn (a) der Mechanismus MCAR ist, (b) die betroffene Zeilen- oder Spaltenmenge klein ist (< 5 % Verlust) und (c) Sie kein Interesse an genau diesen Beobachtungen haben. Andernfalls verlieren Sie Signal und riskieren Selection Bias. fillna mit einer Konstanten (0, Mittelwert, Median, Modus) ist als Baseline in Ordnung, verzerrt aber die Verteilung – die Varianz sinkt, Korrelationen dämpfen sich ab.

# dropna: nur wenn beide Spalten fehlen
df.dropna(subset=["alter", "einkommen"], how="all")

# Nur Zeilen mit mehr als 3 Nicht-NaN-Werten behalten
df.dropna(thresh=3)

# fillna mit Spalten-Median (robust gegen Ausreißer)
df["alter"] = df["alter"].fillna(df["alter"].median())

# Kategorische Spalte mit Modus füllen
mode = df["region"].mode().iloc[0]
df["region"] = df["region"].fillna(mode)

# Sentinel-Wert für kategorische Spalten (informativ!)
df["kanal"] = df["kanal"].fillna("UNBEKANNT")

Der Sentinel-Ansatz ("UNBEKANNT" als eigene Kategorie) ist bei MNAR häufig die ehrlichste Lösung: das Fehlen ist die Information. Ein Baum-basiertes Modell (XGBoost, LightGBM, CatBoost) kann diese Kategorie direkt lernen; für lineare Modelle brauchen Sie zusätzlich eine One-Hot-Kodierung. Vergleichen Sie diese Strategien immer gegen den Baseline aus unserem praktischen scikit-learn Pipelines Leitfaden, um den tatsächlichen Effekt auf Ihre Metrik zu messen.

Interpolation für Zeitreihen

Für zeitlich geordnete Daten sind lineare oder spline-basierte Interpolationen fast immer besser als Mittelwert-Imputation, weil sie den lokalen Trend respektieren. Pandas bietet über DataFrame.interpolate eine breite Palette: linear, time, polynomial, spline, akima, pchip und cubicspline. Für ungleichmäßige Zeitstempel unbedingt method="time" statt "linear" verwenden – sonst gewichtet Pandas jede Zeile gleich, egal wie weit sie zeitlich auseinanderliegen.

ts = pd.Series(
    [10.0, np.nan, np.nan, 13.5, 14.2, np.nan, 16.0],
    index=pd.to_datetime([
        "2026-01-01", "2026-01-02", "2026-01-03",
        "2026-01-10", "2026-01-11", "2026-01-12", "2026-01-20",
    ]),
)

# Falsch bei ungleichmäßigen Abständen
ts.interpolate(method="linear")

# Richtig: berücksichtigt die tatsächliche Zeitdifferenz
ts.interpolate(method="time")

# Für glatte Kurven (z.B. Sensordaten)
ts.interpolate(method="pchip")

# Kombination: forward-fill nur bis maximal 2 Perioden
ts.ffill(limit=2)

Ein häufiger Fehler bei Finanzzeitreihen: Kurslücken über Wochenenden mit method="linear" zu schließen. Das erzeugt Handelstage, die es nie gab. Setzen Sie stattdessen ffill mit limit oder resamplen Sie explizit auf einen Business-Day-Index mit ts.asfreq("B").ffill(). Bei saisonalen Daten liefert eine STL-Zerlegung (statsmodels.tsa.seasonal_decompose) mit anschließender Imputation der Residuen und Rekomposition messbar bessere Ergebnisse als jede Standardmethode.

SimpleImputer, KNNImputer und IterativeImputer

Für ML-Workflows sind die Imputer aus sklearn.impute der Goldstandard, weil sie sich in Pipelines integrieren und den Train/Test-Split respektieren. Drei Klassen decken 95 % aller Fälle ab:

MerkmalSimpleImputerKNNImputerIterativeImputer (MICE)
Strategiemean / median / most_frequent / constantDistanzgewichteter Durchschnitt der k nächsten NachbarnRegressionsmodell pro Spalte, iterativ
RechenaufwandO(n)O(n²) — teuer bei > 100k ZeilenSehr hoch, mehrere Iterationen
Nutzt Feature-KorrelationenNeinJa (lokal)Ja (global)
Kategorische WerteJa (most_frequent)Nur numerischNur numerisch (Standard-Estimator)
ReproduzierbarkeitDeterministischDeterministischrandom_state erforderlich
Beste Wahl beiBaseline, MCAR, wenige NaNsMAR, mittlere Datensätze, glatte FeaturesMAR mit starken Feature-Abhängigkeiten
from sklearn.impute import SimpleImputer, KNNImputer
from sklearn.experimental import enable_iterative_imputer  # noqa: F401
from sklearn.impute import IterativeImputer
from sklearn.ensemble import GradientBoostingRegressor

X = df[["alter", "einkommen", "score"]]

# 1) Baseline
simple = SimpleImputer(strategy="median")
X_simple = simple.fit_transform(X)

# 2) KNN (k=5 ist ein guter Startwert)
knn = KNNImputer(n_neighbors=5, weights="distance")
X_knn = knn.fit_transform(X)

# 3) MICE mit Gradient Boosting als Regressor pro Spalte
mice = IterativeImputer(
    estimator=GradientBoostingRegressor(n_estimators=50, max_depth=3),
    max_iter=10,
    random_state=42,
    initial_strategy="median",
)
X_mice = mice.fit_transform(X)

IterativeImputer ist die Python-Umsetzung des MICE-Algorithmus (Multiple Imputation by Chained Equations, van Buuren & Groothuis-Oudshoorn, 2011). Beachten Sie, dass die Klasse in scikit-learn 1.5 noch als "experimental" markiert ist – daher der explizite Import von enable_iterative_imputer. Für kleine bis mittlere Tabellen (< 50k Zeilen, < 50 Features) liefert MICE die statistisch fundiertesten Schätzungen; ab dieser Größe wird die Laufzeit zum Problem, und KNNImputer ist der praktikablere Kompromiss.

Imputation in einer scikit-learn Pipeline

Der kritischste Fehler in Produktions-ML-Pipelines ist die Imputation vor dem Train/Test-Split. Dabei fließt Information aus dem Testset (der Mittelwert, die Nachbarn) in Ihr Trainingsset – ein klassischer Data-Leak. Die einzige richtige Lösung: alle Imputationsschritte in eine sklearn.pipeline.Pipeline packen und die Pipeline nach dem Split fitten.

from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split

num_features = ["alter", "einkommen", "score"]
cat_features = ["region", "kanal"]

num_pipe = Pipeline([
    ("imputer", KNNImputer(n_neighbors=5)),
    ("scaler", StandardScaler()),
])

cat_pipe = Pipeline([
    ("imputer", SimpleImputer(strategy="constant", fill_value="UNBEKANNT")),
    ("onehot", OneHotEncoder(handle_unknown="ignore", sparse_output=False)),
])

preprocess = ColumnTransformer([
    ("num", num_pipe, num_features),
    ("cat", cat_pipe, cat_features),
])

model = Pipeline([("prep", preprocess), ("clf", LogisticRegression(max_iter=1000))])

X_train, X_test, y_train, y_test = train_test_split(
    df[num_features + cat_features], df["target"], test_size=0.2, random_state=42
)
model.fit(X_train, y_train)
print("Test-Accuracy:", model.score(X_test, y_test))

Diese Struktur folgt exakt den Empfehlungen aus unserem scikit-learn Pipelines Leitfaden. Für die Kreuzvalidierung verwenden Sie cross_val_score(model, X, y, cv=5) – die Pipeline garantiert, dass in jedem Fold die Imputer nur auf den Trainingsfaltendaten gefittet werden. Ohne diese Kapselung sind Ihre CV-Scores optimistisch verzerrt.

Nullable-Dtypes und pd.NA in pandas 2026

Seit pandas 1.0 gibt es Nullable-Dtypes, und mit der Migration zu pandas 3.0 im April 2026 werden sie zum Standard. Der Vorteil: eine Int64-Spalte bleibt integer, auch wenn sie fehlende Werte enthält – kein automatischer Cast zu float64 mehr. Das ist besonders wichtig für Zähler, IDs und kategorische Codierungen, die semantisch keine Fließkommazahlen sind.

# Alter Weg: NaN forciert float64
s_old = pd.Series([1, 2, None])
print(s_old.dtype)  # float64

# Neuer Weg: pd.NA bewahrt den Integer-Dtype
s_new = pd.Series([1, 2, None], dtype="Int64")
print(s_new.dtype)  # Int64
print(s_new.isna())  # 0 False, 1 False, 2 True

# Umwandlung bestehender DataFrames
df = df.convert_dtypes()
print(df.dtypes)

Zu beachten: pd.NA propagiert in booleschen Kontexten anders als NaN. Ein Ausdruck wie s_new > 0 liefert für pd.NA-Positionen kein False, sondern pd.NA – und ein if darauf wirft TypeError: boolean value of NA is ambiguous. Nutzen Sie .fillna(False) oder Masken mit .notna(). Für die Migrationsdetails siehe unseren Pandas 3.0 Migrationsleitfaden, insbesondere den Abschnitt zu Copy-on-Write und dem neuen String-Dtype.

Häufige Fallstricke und Anti-Patterns

In produktiven Systemen sehe ich immer wieder dieselben Fehler. Vier davon kosten am meisten Modellqualität:

  1. Imputation vor dem Split: df.fillna(df.mean()) auf dem gesamten DataFrame, danach train_test_split. Der Testset-Mittelwert ist in die Trainingsdaten gelaufen. Fix: immer eine Pipeline nach dem Split.
  2. Mittelwert-Imputation bei schiefen Verteilungen: Einkommen, Preise, Warenkorbgrößen sind rechtsschief. Der Mittelwert wird von Ausreißern nach oben gezogen. Nehmen Sie den median als Baseline und den Mittelwert nur bei annähernd symmetrischen Verteilungen.
  3. Verlust der Missing-Information: Wenn das Fehlen selbst prädiktiv ist (MNAR), löschen Sie diese Information durch Imputation. Fügen Sie mit MissingIndicator(features="missing-only") eine Zusatzspalte hinzu, die "war ursprünglich NaN" kodiert.
  4. KNN ohne Skalierung: siehe oben – ohne StandardScaler dominiert die Spalte mit der größten Wertespanne die Distanz und die imputierten Werte sind praktisch der Spaltenmittelwert.

Ein oft übersehener Zwischenschritt: nutzen Sie DuckDB, um vor der Imputation mit SQL schnell zu prüfen, wie fehlende Werte über Segmente verteilt sind. Beispielsweise mit SELECT region, AVG(einkommen IS NULL) FROM df GROUP BY region. Wie das direkt auf einem Pandas-DataFrame funktioniert, zeigt unser Artikel DuckDB mit Python: SQL-Analytik auf Pandas-DataFrames. Diese Kombination – SQL für Diagnose, sklearn für Imputation – ist in meiner Praxis der schnellste Weg zu belastbaren Preprocessing-Entscheidungen.

Häufig gestellte Fragen

Wie erkenne ich fehlende Werte in einem Pandas DataFrame?

Mit df.isna().sum() erhalten Sie die Anzahl fehlender Werte pro Spalte, mit df.isna().mean() * 100 den prozentualen Anteil. Für die visuelle Analyse nutzen Sie missingno.matrix(df) und missingno.heatmap(df), um Muster zu erkennen.

Was ist der Unterschied zwischen NaN, None und pd.NA?

NaN ist ein Float-Wert (IEEE 754) und der klassische Missing-Marker für numerische Spalten. None ist das Python-Nullobjekt und erscheint in Object-Spalten. pd.NA ist Pandas' neuer, dtype-agnostischer Missing-Marker für die Nullable-Dtypes (Int64, boolean, string) und verhält sich in Vergleichen konsistent.

Sollte ich fehlende Werte löschen oder imputieren?

Löschen (dropna) nur wenn der Datenverlust unter 5 % liegt und der Mechanismus MCAR ist. In allen anderen Fällen sollten Sie imputieren, weil Sie sonst Selection Bias einführen. Ein MissingIndicator zusätzlich zur Imputation bewahrt die Information, dass ein Wert ursprünglich fehlte.

Wann sollte ich KNNImputer statt SimpleImputer verwenden?

Wenn Ihre Features korreliert sind und der Mechanismus MAR ist – dann liefert KNN messbar bessere Imputationen als der Spaltenmittelwert. Für Datensätze über 100.000 Zeilen wird KNN aber zu langsam; nutzen Sie dann IterativeImputer mit einem schnellen Regressor oder bleiben Sie beim Median.

Wie funktioniert IterativeImputer intern?

IterativeImputer implementiert MICE: für jede Spalte mit fehlenden Werten wird ein Regressionsmodell (Standard: BayesianRidge) trainiert, das die Spalte anhand aller anderen vorhersagt. Der Prozess wird über mehrere Iterationen wiederholt, bis die Imputationen konvergieren. Das Verfahren ist statistisch fundiert, aber deutlich langsamer als SimpleImputer.

Wie vermeide ich Data Leakage bei der Imputation?

Führen Sie train_test_split immer vor jeder Imputation aus und kapseln Sie den Imputer in einer sklearn.pipeline.Pipeline. So wird der Imputer nur auf den Trainingsdaten gefittet, und die gelernten Statistiken (Mittelwerte, Nachbarn) werden dann auf das Testset angewendet – ohne dass Testinformation ins Training leakt.

Dr. Elena Vasquez
Über den Autor Dr. Elena Vasquez

Data scientist with a PhD in computational statistics. Translates papers into pandas one notebook at a time.