ML Atlas

Machine Learning von Grund auf · Lektion 4.2 · 21 von 33 fertigen Lektionen · 12 Min.

Fehlende Daten

Kurz gesagt

Lösche Zeilen mit Lücken, und von 344 Pinguinen bleiben 333. Fehlt der BMI bei den Schwersten, schwächt der Mittelwert den Zusammenhang mit der Krankheit.

Aufwärmen Zwei Fragen aus früheren Lektionen. Eine Minute, und sie blockiert die Lektion nicht.
Aus Lektion 4.1: Exploration: erst schauen, dann modellieren Richtig oder falsch: „Kennzahlen reichen, Daten zu zeichnen ist Zeitverschwendung“.

Antwort anzeigen

Richtige Antwort: Falsch, weil dieselben Zahlen einen Bogen, Gruppen oder einen fernen Punkt verbergen

Im Quartett verbergen dieselben Zahlen vier verschiedene Bilder, und bei 342 Pinguinen hat eine Korrelation das umgekehrte Vorzeichen wie in jeder Art. Ein Bild zeigt das sofort.

Aus Lektion 3.6: Frühes Stoppen Woran entscheidet frühes Stoppen, wann das Training abbricht?

Antwort anzeigen

Richtige Antwort: Am Validierungsverlust: Es stoppt, wenn er nicht mehr sinkt

Der Trainingsverlust sinkt fast bis zum Ende, selbst wenn das Netz Rauschen lernt. Der Validierungsverlust, berechnet auf Daten außerhalb des Trainings, steigt, wenn das Modell anfängt, sich zu überanpassen.

In Lektion 4.1 haben wir die Daten angeschaut, bevor ein Modell entstand. Heute geht es um das, was in den Daten fehlt. Fehlende Daten sind eine leere Zelle in der Tabelle: Jemand hat nicht gemessen, nichts notiert oder wollte es nicht sagen. Die meisten Modelle nehmen keine leere Zelle an, also musst du etwas damit tun. Es gibt zwei einfache Auswege: die Zeile löschen oder einen Wert in die Lücke schreiben, was man Imputation fehlender Werte nennt.

Erst vorhersagen, dann prüfen Die Pinguintabelle hat 344 Zeilen. Das Geschlecht fehlt bei 11 Pinguinen, jede der vier Messungen (zweimal Schnabel, Flosse, Masse) bei 2. Du löschst jede Zeile, in der irgendetwas fehlt. Wie viele Pinguine bleiben?
Wie sicher bist du?

Interaktives Labor

Dieses interaktive Labor läuft im Browser mit aktiviertem JavaScript. Der Text drumherum beschreibt, was es zeigt.

Antwort anzeigen

Richtige Antwort: 333, weil die 2 Pinguine ohne Messungen unter den 11 ohne Geschlecht sind

Es bleiben 333. Die leeren Messzellen liegen in denselben 2 Zeilen, und diesen Pinguinen fehlt auch das Geschlecht. Du löschst also 11 Zeilen, nicht 19 und nicht 13. Bevor du die Verluste zählst, prüfe, in welchen Zeilen die Lücken liegen.

Probier es aus

  • Fang mit den Pinguinen an. Wie vielen fehlt das Geschlecht, wie vielen die Messungen? Sind es dieselben Zeilen?
  • Das Modell soll die Körpermasse aus den drei anderen Messungen vorhersagen. Braucht es das Geschlecht? Wie viele Pinguine bleiben, wenn du nur die Zeilen ohne die Spalten des Modells löschst?
  • Wechsle zu Diabetes. Hier fehlt der BMI bei den schwersten Patienten. Setze den Mittelwert in die Lücke: Wird der Zusammenhang zwischen BMI und Krankheitsverlauf schwächer oder stärker?
  • Füge die Spalte „fehlt“ hinzu und vergleiche R² in der Kreuzvalidierung. Wechsle dann zu zufälligen Patienten. Was ändert sich?

Wenn du es im Lab geprüft hast, geben wir dem, was du siehst, einen Namen.

Die Idee in einfachen Worten

Eine Zeile mit Lücke zu löschen ist einfach, aber es kostet. Bei den Pinguinen sind die Zeilen mit irgendeiner Lücke 11 von 344, also bleiben 333. Ein Modell, das die Körpermasse aus Schnabel- und Flossenmaßen vorhersagt, liest das Geschlecht nie. Löschst du nur die Zeilen, denen Spalten des Modells fehlen, bleiben 342. Lösche Zeilen wegen der Spalten, die du wirklich nutzt.

Wichtiger ist die Frage, bei wem die Daten fehlen. Im Diabetes-Datensatz hat jeder der 442 Patienten einen BMI, also versteckt das Lab ihn absichtlich bei 110 von ihnen. Einmal zufällig, einmal bei den 110 schwersten, also bei allen mit einem BMI von mindestens 29,4. Eine solche Lücke ist nicht zufällig. Die Patienten ohne BMI haben einen mittleren Krankheitsverlauf von 209, die übrigen 133. Schon die Tatsache, dass der BMI fehlt, sagt etwas über den Patienten.

Wenn du den Mittelwert der anderen Patienten (24,3) in die Lücke schreibst, landen die 110 Menschen mit dem stärksten Verlauf bei einem durchschnittlichen BMI. Die Korrelation von BMI und Krankheitsverlauf fällt von 0,586 auf 0,352: Der Zusammenhang wird schwächer. Die Gerade des Modells wird dabei nicht flacher, denn sie wird von den Patienten mit BMI bestimmt; schlechter wird die Anpassung, weil die 110 eingesetzten Punkte weit von der Geraden liegen. Das R² einer linearen Regression in der Kreuzvalidierung fällt von 0,328 auf 0,106. Eine zusätzliche 0/1-Spalte „BMI fehlte“ hilft. Das Modell gibt dann der Lücke selbst ein Gewicht von 76 Punkten Verlauf, und R² steigt wieder auf 0,284. Bei zufälligen Lücken bringt diese Spalte nichts: 0,264 gegen 0,267 mit dem Mittelwert allein.

Jak to policzono
  • Pinguine: Palmer Penguins (Gorman, Williams und Fraser, 2014; Paket palmerpenguins: Horst, Hill und Gorman, 2020), Lizenz CC0. 344 Zeilen, echte Lücken: Geschlecht bei 11 (Adelie 6, Gentoo 5), die vier Messungen bei denselben 2 Pinguinen.
  • Diabetes: Diabetes (Efron, Hastie, Johnstone und Tibshirani, 2004), mit scikit-learn unter der Lizenz BSD-3-Clause verteilt. 442 Patienten, Krankheitsverlauf nach einem Jahr. Der BMI ist in den Daten bei allen vorhanden; die Lücken bei 110 Patienten haben wir selbst gemacht: zufällig oder bei den 110 mit dem höchsten BMI.
  • Modell: lineare Regression des Krankheitsverlaufs auf den BMI (mit der Lückenspalte auf den BMI und diese Spalte), Kreuzvalidierung mit 5 Teilen, 10-mal wiederholt. Der einzusetzende Mittelwert wird aus den Patienten mit BMI berechnet, in der Kreuzvalidierung in jeder Aufteilung nur aus dem Trainingsteil, wie SimpleImputer in einer Pipeline.

Warum das funktioniert

Der eingesetzte Mittelwert liegt in der Mitte der BMI-Achse, gehört aber zu Patienten von ihrem rechten Ende. Ihr Krankheitsverlauf ist stark, also entsteht beim Einsetzen ein senkrechter Stapel hoher Punkte über einem durchschnittlichen BMI. Ein solcher Stapel trägt nichts zur gemeinsamen Streuung von BMI und Verlauf bei. Verglichen mit den Patienten mit BMI (Korrelation 0,459) fügt er nur dem Verlauf Streuung hinzu, also fällt die Korrelation auf 0,352. Verglichen mit den vollständigen Daten (0,586) verlieren diese 110 Patienten ihren Anteil an der gemeinsamen Streuung: Sie hatten einen hohen BMI und einen starken Verlauf und stehen jetzt in der Mitte der Achse. Die Standardabweichung des BMI schrumpft von 4,4 auf 2,3, weil 110 Werte in einem Punkt sitzen.

Diese Patienten zu löschen, rettet die Sache auch nicht. Übrig bleibt eine Wolke ohne ihr rechtes Ende, und die Korrelation bei den restlichen 332 beträgt 0,459 statt 0,586. Außerdem gibt das Modell für 110 Patienten gar keine Antwort. Die Lückenspalte wirkt, weil sie dem Modell ein eigenes Gewicht für die Gruppe gibt, deren BMI wir nicht kennen. Diese Gruppe unterscheidet sich im Krankheitsverlauf, also trägt das Gewicht den Unterschied in die Vorhersagen. Ist die Lücke zufällig, unterscheidet sich die Gruppe ohne BMI nicht vom Rest (156 gegen 151), und das Gewicht hat nichts zu tragen.

Ein häufiger Fehler

Wahr oder falsch: „Fülle die Lücken mit dem Mittelwert, und fertig.“ Falsch. Der Mittelwert verkleinert die Streuung der Spalte und tut so, als hätte es die Lücke nie gegeben. Hängt die Lücke vom Wert selbst ab, wie hier bei den Schwersten, verbiegt der Mittelwert den Zusammenhang: Die Korrelation fällt von 0,586 auf 0,352 und R² von 0,328 auf 0,106. Die Spalte „fehlt“ bringt R² zurück auf 0,284. Im Code unten schlägt sie den Mittelwert allein in 48 von 50 Validierungsteilen.

Mittelwert und Löschen von Zeilen sind in Ordnung, wenn Lücken selten und zufällig sind. Prüfe aber zuerst, ob sich die Zeilen mit Lücke vom Rest unterscheiden.

Quiz

Drei Fragen zur Selbstkontrolle. Wähle eine Antwort oder klappe die Lösung unter der Frage auf. Mit der Tastatur: Pfeiltasten wählen, Enter oder Leertaste bestätigt.

1. In der Pinguintabelle fehlt das Geschlecht bei 11, die Messungen bei 2 derselben Pinguine. Wie viele bleiben nach dem Löschen jeder Zeile mit Lücke?
Antwort anzeigen

Richtige Antwort: 333, weil die 2 Pinguine ohne Messungen unter den 11 Pinguinen ohne Geschlecht sind

Die fehlenden Messungen liegen in 2 Zeilen, denen auch das Geschlecht fehlt. Du löschst 11 Zeilen, und von 344 bleiben 333.

2. Bei den 110 schwersten Patienten fehlt der BMI. Was bewirkt das Einsetzen des Mittelwerts der anderen?
Antwort anzeigen

Richtige Antwort: Es schwächt den Zusammenhang, weil die Kränksten bei einem mittleren BMI landen

Die Korrelation fällt von 0,586 auf 0,352. 110 Patienten mit starkem Verlauf sitzen in einem Punkt: beim Mittelwert 24,3.

3. Wahr oder falsch: „Fülle die Lücken mit dem Mittelwert, und fertig.“
Antwort anzeigen

Richtige Antwort: Falsch, weil der Mittelwert die Streuung verkleinert und eine Lücke versteckt, die etwas bedeuten kann

Bei den Schwersten fällt R² von 0,328 auf 0,106. Die Spalte „fehlt“ bringt 0,284 zurück, weil die Lücke selbst etwas über den Patienten sagt.

Das Wichtigste

  • Lösche Zeilen wegen der Spalten, die du nutzt: Es bleiben 342 Pinguine statt 333.
  • Bevor du etwas mit Lücken tust, prüfe, ob sich die Zeilen mit Lücke vom Rest unterscheiden.
  • Der Mittelwert in den Lücken verkleinert die Streuung und schwächt bei nicht zufälligen Lücken den Zusammenhang: Eine Korrelation von 0,586 fällt auf 0,352.
  • Eine 0/1-Spalte „fehlt“ lässt das Modell die Information nutzen, die die Lücke selbst trägt: R² 0,284 statt 0,106.

Tiefer: Formel, Code, Praxis

Die Mathematik

Einsetzen des Mittelwerts: x̃ᵢ = x̄_obs für die Patienten mit Lücke, x̃ᵢ = xᵢ für den Rest. Die eingesetzten Punkte haben x̃ᵢ − x̄ = 0, tragen also nichts zur Kovarianz Σ (x̃ᵢ − x̄)(yᵢ − ȳ) oder zur Varianz Σ (x̃ᵢ − x̄)² bei, fügen aber (yᵢ − ȳ)² zur Varianz von y hinzu. Verglichen mit den Patienten mit BMI allein ändert sich die Summe Σ (x̃ᵢ − x̄)(yᵢ − ȳ) nicht, während die Varianz von y wächst, also fällt die Korrelation r = cov(x̃, y) / (s_x̃ · s_y) (0,459 → 0,352). Verglichen mit den vollständigen Daten ist die Varianz von y gleich; die Kovarianz fällt, weil die eingesetzten Punkte ihre großen positiven Beiträge nicht mehr liefern (0,586 → 0,352).

Die Lückenspalte: ŷ = a + b · x̃ + c · m, wobei m = 1, wenn der BMI fehlte, und sonst m = 0. Für einen Patienten mit Lücke sagt das Modell a + b · x̄_obs + c vorher, also verschiebt c die ganze Gruppe ohne BMI. Hier ist c = 76 bei den Lücken unter den Schwersten.

Im Code (Python)

Dasselbe Experiment in pandas und scikit-learn: die Lücken in der Pinguintabelle, dann der BMI bei 110 Patienten zufällig und bei den 110 schwersten versteckt, der Mittelwert in den Lücken und der Mittelwert mit Lückenspalte in einer Kreuzvalidierung 10 × 5.

import numpy as np
import pandas as pd
from sklearn.datasets import load_diabetes
from sklearn.model_selection import cross_val_score, RepeatedKFold
from sklearn.linear_model import LinearRegression
from sklearn.impute import SimpleImputer
from sklearn.pipeline import make_pipeline

df = pd.read_csv("data/raw/penguins.csv")
gaps = df.isna().sum()
print(f"{len(df)} penguins; missing: " + ", ".join(f"{c} {n}" for c, n in gaps[gaps > 0].items()))
measured = df.dropna(subset=["bill_length_mm", "bill_depth_mm", "flipper_length_mm", "body_mass_g"])  # nur Zeilen ohne die Spalten des Modells löschen
print(f"dropna() leaves {len(df.dropna())}; dropping only rows without measurements leaves {len(measured)}")
X, y = load_diabetes(return_X_y=True, as_frame=True, scaled=False)
bmi, y = X.bmi.to_numpy(), y.to_numpy()
heavy = bmi >= np.sort(bmi)[-110]  # die 110 schwersten: BMI von mindestens 29,4
rnd = np.zeros(len(bmi), bool)
rnd[np.random.default_rng(0).choice(len(bmi), 110, replace=False)] = True  # 110 zufällige
cv = RepeatedKFold(n_splits=5, n_repeats=10, random_state=0)
r2 = lambda model, Z: cross_val_score(model, Z, y, cv=cv, scoring="r2")
print(f"{len(bmi)} patients, BMI vs progression: r {np.corrcoef(bmi, y)[0, 1]:.3f}, CV R2 {r2(LinearRegression(), bmi[:, None]).mean():.3f}")
for name, miss in [("random", rnd), ("heaviest", heavy)]:
    hidden = np.where(miss, np.nan, bmi)[:, None]
    filled = np.where(miss, bmi[~miss].mean(), bmi)  # der Mittelwert nur der Patienten mit BMI
    mean_r2 = r2(make_pipeline(SimpleImputer(), LinearRegression()), hidden)  # der Mittelwert wird in jeder Aufteilung nur aus ihrem Trainingsteil gelernt
    flag_r2 = r2(make_pipeline(SimpleImputer(add_indicator=True), LinearRegression()), hidden)  # zweite Spalte: 1 = BMI fehlte
    print(f"BMI hidden for 110 {name}: progression {y[miss].mean():.0f} vs {y[~miss].mean():.0f}; mean fill: r {np.corrcoef(filled, y)[0, 1]:.3f}, CV R2 {mean_r2.mean():.3f}; mean + flag: CV R2 {flag_r2.mean():.3f}")
print(f"heaviest: mean + flag beats the mean alone in {(flag_r2 > mean_r2).sum()} of 50 folds")

Ausgabe

344 penguins; missing: bill_length_mm 2, bill_depth_mm 2, flipper_length_mm 2, body_mass_g 2, sex 11
dropna() leaves 333; dropping only rows without measurements leaves 342
442 patients, BMI vs progression: r 0.586, CV R2 0.332
BMI hidden for 110 random: progression 159 vs 150; mean fill: r 0.503, CV R2 0.241; mean + flag: CV R2 0.239
BMI hidden for 110 heaviest: progression 209 vs 133; mean fill: r 0.352, CV R2 0.112; mean + flag: CV R2 0.289
heaviest: mean + flag beats the mean alone in 48 of 50 folds

Die zufälligen Patienten und die Aufteilungen sind andere als im Lab, also sind auch die Zahlen etwas anders. Das Bild ist dasselbe: Es bleiben 333 oder 342 Pinguine, der Mittelwert bei den Schwersten drückt die Korrelation auf 0,352, und die Lückenspalte hebt R² von 0,112 auf 0,289. Bei zufälligen Lücken hilft die Spalte nicht: 0,239 gegen 0,241.

Für die Praxis
  • In scikit-learn: SimpleImputer(strategy="mean") oder "median", und mit add_indicator=True bekommst du die Lückenspalte gleich dazu. Stecke ihn in make_pipeline, damit der Mittelwert nur auf dem Trainingsteil jeder Aufteilung berechnet wird.
  • df.isna().sum() und df[df.x.isna()].describe() zeigen, wie viele Lücken es gibt und ob sich die Zeilen mit Lücke vom Rest unterscheiden. Das ist das Erste, was du prüfst, bevor du eine Methode wählst.
  • IterativeImputer und KNNImputer sagen den fehlenden Wert aus anderen Spalten vorher. Sie helfen, wenn Lücken von anderen Spalten abhängen, aber eine Lücke, die vom versteckten Wert selbst abhängt, stellen sie nicht wieder her; dann zählt die Lückenspalte mehr.
  • Die Bäume in HistGradientBoostingClassifier behandeln Lücken selbst: Bei jeder Teilung lernen sie, in welche Richtung die Zeilen mit Lücke gehen.

Mehr im Atlas

Einträge der Enzyklopädie zu den Ideen dieser Lektion: