Teil 4 · Statistische Inferenz und medizinische Statistik
Lösungen
14 · Fehlende Werte und Imputation
Alle Zahlen stammen aus code/python.py (Seed 42). Das
R-Skript code/r.R liefert dieselben Werte; nur die multiple
Imputation weicht leicht ab, weil sie stochastisch ist und mice predictive
mean matching statt Regressions-Imputation nutzt.
Aufgabe 1 – Missingness-Tabelle
.py schreiben und mit dem ▶-Knopf in VS Code ausführen – oder Zeile für Zeile in die Python-Konsole. Setzt die in Modul 02 eingerichtete Umgebung voraus.import numpy as np import pandas as pd import statsmodels.api as sm import statsmodels.formula.api as smf from statsmodels.imputation import mice df = pd.read_csv("https://schradern.github.io/data-science-coach/data/kohorte.csv").merge( pd.read_csv("https://schradern.github.io/data-science-coach/data/labor.csv"), on="patient_id", how="left") print(df.isna().mean().loc[lambda s: s > 0].sort_values(ascending=False))
| Variable | fehlend |
|---|---|
bga_ph |
19,8 % |
laktat_mmol_l |
17,2 % |
bmi, gewicht_kg |
~6 % |
Nennenswert fehlen bga_ph und laktat_mmol_l. Die ~6 % beim bmi sind MCAR
und kosten nur Power.
Aufgabe 2 – Wer fehlt?
.py schreiben und mit dem ▶-Knopf in VS Code ausführen – oder Zeile für Zeile in die Python-Konsole. Setzt die in Modul 02 eingerichtete Umgebung voraus.work = df.assign(laktat_fehlt=df["laktat_mmol_l"].isna().astype(int), bga_fehlt=df["bga_ph"].isna().astype(int)) for col in ["laktat_fehlt", "bga_fehlt"]: print(smf.logit(f"{col} ~ sofa_score + verstorben_30d", data=work).fit(disp=0).summary())
| fehlende Variable | β(sofa_score) | β(verstorben_30d) |
|---|---|---|
laktat_mmol_l |
−0,268 (p = 0,0001) | −0,065 (p = 0,87) |
bga_ph |
−0,071 (p = 0,22) | +1,455 (p < 0,0001) |
Laktat fehlt nach Schweregrad: je höher der SOFA-Score, desto seltener fehlt es — Schwerkranke bekommen häufiger eine BGA. Das Outcome erklärt nichts.
Der pH fehlt nach Outcome: Verstorbene haben ein exp(1,455) ≈ 4,3-fach
höheres Odds, keinen pH-Wert zu haben. Konkret fehlt der pH bei 41,0 % der
Verstorbenen und bei 15,9 % der Überlebenden.
Aufgabe 3 – Die Entscheidung
- Das Fehlen von
laktat_mmol_lhängt vomsofa_scoreab. - Ja,
sofa_scoresteht im Modell. - Unverzerrt. Hängt die Missingness nur von Variablen ab, auf die das
Modell ohnehin konditioniert, dann ist
P(Y | X)unter den vollständigen Fällen dieselbe Verteilung wie in der Gesamtkohorte. Complete Case schätzt hier alle Koeffizienten korrekt und kostet nur Präzision:
complete-case β(laktat) = +0,084 (95 % KI −0,113 bis +0,281)
- Verzerrt. Der Mittelwert ist eine marginale Kennzahl, kein bedingter Schätzer. Weil Schwerkranke (hohes Laktat) überrepräsentiert sind, liegt der beobachtete Mittelwert von 2,16 mmol/l über dem wahren Kohortenmittel. Eine Table 1 aus vollständigen Fällen beschreibt die falsche Kohorte.
Dieselbe Spalte, dasselbe „MAR“ — und trotzdem ist der Regressionskoeffizient in Ordnung, während die Table 1 falsch ist.
Aufgabe 4 – Was Complete Case beim pH kostet
| Intercept | OR je −0,1 pH | Risiko (64 J., SOFA 4, pH 7,38) | |
|---|---|---|---|
| volle Daten (Wahrheit) | 68,05 | 2,76 | 0,106 |
| Complete Case | 65,72 | 2,67 | 0,076 |
- Das Odds Ratio überlebt (2,67 vs. 2,76 — der Unterschied ist Rauschen).
- Intercept und absolutes Risiko brechen: 0,076 statt 0,106, also 28 % zu niedrig.
Warum: Die Selektion erfolgt nach dem Outcome Y, nicht nach den
Prädiktoren. In einem logistischen Modell verschiebt eine Selektion nach Y
ausschließlich den Intercept — die Steigungen bleiben unverzerrt. Das ist exakt
derselbe Grund, aus dem man aus einer Fall-Kontroll-Studie Odds Ratios schätzen
darf, aber keine Prävalenz (Prentice & Pyke 1979).
Klinisch: Das Modell erkennt korrekt, dass Azidose das Risiko erhöht, und sagt jeder Patient:in trotzdem eine zu gute Prognose voraus. Wer nur Odds Ratios berichtet, bemerkt den Fehler nie.
Sichtbar schon an der Kohorte selbst:
volle Kohorte N=500 30-Tage-Mortalität 15,6 % vollständige Fälle N=401 30-Tage-Mortalität 11,5 %
Aufgabe 5 – Median- gegen multiple Imputation
| β(bga_ph) | SE | OR je −0,1 pH | Risiko | |
|---|---|---|---|---|
| volle Daten (Wahrheit) | −10,14 | 3,00 | 2,76 | 0,106 |
| Complete Case | −9,82 | 3,77 | 2,67 | 0,076 |
| Median-Imputation | −5,76 | 3,21 | 1,78 | 0,115 |
| Multiple Imputation (m = 20) | −9,50 | 3,64 | 2,59 | 0,107 |
Der Widerspruch: Die Median-Imputation setzt 99 Patient:innen auf exakt denselben pH-Wert. Damit
- schrumpft der Effekt. Die künstliche Punktwolke am Median trägt keinen Zusammenhang mit dem Outcome bei; das verdünnt die Steigung gegen Null (regression dilution): OR 2,76 → 1,78.
- schrumpft der Standardfehler. Das Modell sieht 500 statt 401 Beobachtungen und behandelt die 99 erfundenen Werte wie gemessene.
Der SE ist also nicht kleiner, weil mehr Information vorläge, sondern weil die Methode so tut. Rubin's rules addieren die Between-Imputation-Varianz wieder hinzu: der MI-Standardfehler (3,64) ist ehrlich größer als der der Median-Imputation und kleiner als der der Complete-Case-Analyse (3,77), die ganze Fälle verwirft.
Aufgabe 6 – Das Imputationsmodell sabotieren
.py schreiben und mit dem ▶-Knopf in VS Code ausführen – oder Zeile für Zeile in die Python-Konsole. Setzt die in Modul 02 eingerichtete Umgebung voraus.imputer.set_imputer("bga_ph", "alter + sofa_score") # ohne verstorben_30d
| OR je −0,1 pH | Risiko (Referenz) | |
|---|---|---|
| volle Daten (Wahrheit) | 2,76 | 0,106 |
| MI, Outcome im Imputationsmodell | 2,59 | 0,107 |
| MI, Outcome weggelassen | 1,80 | 0,114 |
Es bricht das Odds Ratio, nicht das absolute Risiko — das Spiegelbild von Aufgabe 4.
Mechanismus: Ohne verstorben_30d werden die fehlenden pH-Werte allein aus
alter und sofa_score vorhergesagt, also unabhängig vom Outcome. Die 99
imputierten Patient:innen tragen per Konstruktion keinen pH-Outcome-Zusammenhang
bei, und die gepoolte Steigung wird gegen Null verdünnt — genau wie bei der
Median-Imputation. Die Missingness war MAR gegeben das Outcome; lässt man das
Outcome weg, ist sie es nicht mehr.
Das Imputationsmodell muss jede Variable enthalten, von der die Missingness abhängt — auch dann, wenn sie im Analysemodell die abhängige Variable ist. Ein Imputationsmodell ist kein Analysemodell.
Aufgabe 7 – Rubin's rules von Hand
Vollständig in code/rubin.py / code/rubin.R.
1. Aus den m = 20 Fits (Python, statsmodels):
Q̄ = -9,9576 Ū = 9,1857 B = 1,6821 T = Ū + (1 + 1/20)·B = 9,1857 + 1,7662 = 10,9519 SE = √T = 3,3094
MICE.combine() auf denselben vervollständigten Datensätzen liefert exakt
Q̄ = -9,957600, SE = 3,309358 — bis auf Gleitkommarauschen identisch. Rubin's
rules sind keine Blackbox, sondern vier Zeilen Arithmetik.
2.
r = (1 + 1/20)·B / Ū = 0,1923 λ = (1 + 1/20)·B / T = 0,1613 RE = 1 / (1 + λ/m) = 0,9920
λ = 0,161 heißt: 16,1 % der Unsicherheit über den pH-Koeffizienten
entstehen daraus, dass 99 Werte fehlen; die übrigen 83,9 % hätte man auch bei
vollständigen Daten. RE = 0,992 heißt: m = 20 schöpft 99,2 % der Effizienz
von m = ∞ aus — für den Punktschätzer. Für den Standardfehler gilt das
nicht (siehe Aufgabe 8).
3.
ν_alt = (m − 1)/λ² = 19 / 0,0260 = 730,6 n − k = 496
ν_alt ist größer als die Freiheitsgrade bei vollständigen Daten. Das ist
offensichtlich absurd: fehlende Werte können keine Information hinzufügen. Rubins
Formel von 1987 nimmt implizit n → ∞ an. Barnard & Rubin (1999) korrigieren:
ν_com = 496 ν_obs = ((496+1)/(496+3)) · 496 · (1 − 0,1613) = 414,3 ν_BR = (730,6 · 414,3)/(730,6 + 414,3) = 264,4
Ergebnis: t = −3,009, p = 0,0029, 95-%-KI [−16,47; −3,44].
statsmodels rechnet für MI gar keine Freiheitsgrade, sondern
einen Wald-z-Test (df = ∞) — und meldet für dieselbe Analyse p = 0,0047
statt 0,0029, weil es ein anderes Verfahren ist, nicht weil eines rechnet.
mice::pool() implementiert Barnard–Rubin korrekt.
4. Mit B = 0:
T = Ū = 9,1857 SE = 3,0308 (statt 3,3094)
Das ist genau das, was eine Einfachimputation berichtet: sie behandelt die
99 geratenen Werte als gemessen und lässt die Between-Varianz weg. Der
Standardfehler fällt um 8,4 %, ohne dass ein einziges Datum hinzugekommen wäre.
B ist die Unsicherheit der Imputation. Sie wegzulassen ist der gesamte
Fehler der Einfachimputation.
Aufgabe 8 – Wie viele Imputationen?
1. m ≳ 100 · λ = 100 · 0,1613 = 16,1. Mit m = 20 ist die Faustregel
(White, Royston & Wood 2011) gerade erfüllt — kein Spielraum.
2./3. code/rubin.py wiederholt die gesamte Pipeline über 10 Seeds je m:
| m | mittlerer SE | sd(SE) | cv(SE) |
|---|---|---|---|
| 2 | 3,44 | 0,687 | 20,0 % |
| 5 | 3,62 | 0,267 | 7,4 % |
| 10 | 3,68 | 0,282 | 7,7 % |
| 20 | 3,50 | 0,145 | 4,1 % |
| 50 | 3,67 | 0,115 | 3,1 % |
Bei m = 2 schwankt der berichtete Standardfehler um 20 % — allein je nach
Zufallsseed. Bei m = 5 sind es noch ~7 %.
Die alte Regel „m = 5“ zielt auf die Effizienz des
Punktschätzers (RE > 0,96 schon bei m = 5). Der Standardfehler — und
damit dein p-Wert und dein Konfidenzintervall — ist dann noch deutlich
seed-abhängig. Rechenzeit ist heute kein Argument mehr: nimm m = 20–50.
Aufgabe 9 – Eine Methode überführen
1. Der Widerspruch (RMSE gegen Überdeckung).
Die deterministische Regressions-Imputation setzt jeden fehlenden pH auf seinen
bedingten Erwartungswert E[pH | alter, sofa, outcome]. Das ist per
Definition der Wert, der den mittleren quadratischen Fehler minimiert — deshalb
der beste RMSE (0,0501).
Genau dadurch entfernt sie aber die Streuung aus den imputierten Werten. Die 99 imputierten Patient:innen liegen alle exakt auf der Regressionsfläche, ohne Residualvarianz. Das Analysemodell hält diese künstliche Enge für echte Information und meldet einen viel zu kleinen Standardfehler: SE-Ratio 0,66, d. h. es berichtet nur 66 % der Streuung, die es tatsächlich hat. Überdeckung: 73,2 % statt 95 %.
Imputation ist keine Vorhersageaufgabe. Wer die imputierten Werte nach RMSE optimiert, optimiert die falsche Größe. Die stochastische Variante derselben Methode hat den schlechtesten RMSE (0,0709 — sie addiert Rauschen) und zugleich zehn Prozentpunkte bessere Überdeckung.
2. Mittelwert-Imputation: ehrlicher SE, trotzdem kaputt. Ihre SE-Ratio ist 1,06 — der Standardfehler stimmt also. Sie scheitert nicht an der Varianz, sondern am Bias: +3,08 auf einem wahren Wert von −9,52, also knapp ein Drittel des Effekts weg. Ein korrekt breites Intervall um einen falschen Punkt trifft die Wahrheit trotzdem nicht: 83,8 %.
Ein Konfidenzintervall kann auf zwei Weisen versagen: es liegt an der falschen Stelle (Bias) oder es hat die falsche Breite (Varianz). Die Tabelle enthält je ein Lehrbuchbeispiel.
3. Complete Case: Steigung 93,4 %, absolutes Risiko 57,0 %. In einem Prognosemodell ist das absolute Risiko die Zahl, die man einer Patientin nennt. Ein nominal 95-prozentiges Intervall, das die Wahrheit in 43 % der Fälle verfehlt, ist unbrauchbar — unabhängig davon, wie korrekt das Odds Ratio ist.
Daraus folgt: die Methodenwahl hängt vom Estimand ab. Willst du nur den Effekt (OR) berichten, ist Complete Case hier vertretbar. Willst du Risiken vorhersagen oder kalibrieren, brauchst du MI (93,6 % Überdeckung auf dem Risiko). Der Missing-Indikator ist für die Vorhersage attraktiv, weil „nicht gemessen" zur Anwendungszeit bekannt und selbst prädiktiv ist — für die kausale Effektschätzung ist er ungeeignet, weil der Indikator zum Kollider wird, sobald das Fehlen vom Outcome abhängt.
Aufgabe 10 – MNAR beziffern
Vollständig in code/mnar_delta.py.
1./2. Nur die imputierten Werte werden verschoben, die beobachteten nie (das Skript prüft das per Assertion):
| δ | OR je −0,1 pH | 95-%-KI | Schluss hält? |
|---|---|---|---|
| −0,02 | 3,33 | (1,55; 7,13) | ja |
| 0,00 (MAR) | 2,38 | (1,11; 5,10) | ja |
| +0,01 | 2,22 | (1,03; 4,76) | ja, knapp |
| +0,02 | 1,83 | (0,92; 3,63) | nein — Kipppunkt |
| +0,05 | 1,32 | (0,69; 2,51) | nein |
Der Kipppunkt liegt bei δ = +0,02.
3. Die bedingte Streuung des pH (Residual-SD nach Adjustierung für Alter,
SOFA und Outcome) beträgt 0,050. Der Kipppunkt entspricht also
0,02 / 0,050 = 0,40 bedingten Standardabweichungen.
Der Vergleich mit der Messungenauigkeit des Blutgasanalysators wäre ein
Kategorienfehler: Analysenrauschen ist zufällig und mittelt sich über 99
Patient:innen praktisch weg. δ ist ein systematischer Unterschied im wahren
Mittelwert zwischen gemessenen und nicht gemessenen Patient:innen — er mittelt
sich nicht weg. Das richtige Maß ist die Streuung, die das Imputationsmodell
ohnehin nicht erklären kann.
4. Antwort an die Gutachterin:
Die Schlussfolgerung hält, solange die nicht gemessenen Patient:innen im wahren pH um weniger als 0,02 Einheiten — 0,4 bedingte Standardabweichungen — von dem abweichen, was das Imputationsmodell für sie annimmt. Sie müsste zudem in die Richtung abweichen, die ihrer höheren Sterblichkeit widerspricht: die unbeobachteten Patient:innen müssten trotz ihrer Übersterblichkeit unauffälligere pH-Werte gehabt haben. Wir halten das für unplausibel, geben die Annahme aber offen an und berichten den Kipppunkt, statt MAR unbelegt zu behaupten.
Aufgabe 11 – Bericht
Umgang mit fehlenden Werten. Der arterielle pH fehlte bei 99 von 500 Patient:innen (19,8 %), Laktat bei 86 (17,2 %), der BMI bei 30 (6,0 %). Das Fehlen des pH-Werts war mit der 30-Tage-Mortalität assoziiert (41,0 % der Verstorbenen gegenüber 15,9 % der Überlebenden), nicht jedoch mit Alter oder SOFA-Score. Das Fehlen von Laktat war umgekehrt mit dem SOFA-Score assoziiert (β = −0,27; p < 0,001) und unabhängig vom Outcome.
Die Hauptanalyse verwendet daher multiple Imputation (m = 20, Seed 42, Rubin's rules mit Barnard–Rubin-Freiheitsgraden); das Imputationsmodell enthielt Alter, SOFA-Score und das Outcome. Die Fraktion fehlender Information für den pH-Koeffizienten betrug λ = 0,16, womit m = 20 die Empfehlung m ≳ 100·λ erfüllt. Eine Complete-Case-Analyse als Sensitivitätsanalyse lieferte vergleichbare Odds Ratios (2,67 vs. 2,59 je 0,1 pH-Abfall), aber systematisch zu niedrige absolute Risiken (−28 % am Referenzpatienten), da vollständige Fälle die Verstorbenen unterrepräsentieren. Eine Delta-Adjustment-Analyse zeigte, dass die Schlussfolgerung bis zu einer angenommenen systematischen pH-Differenz von +0,02 Einheiten (0,4 bedingte Standardabweichungen) zwischen nicht gemessenen und gemessenen Patient:innen bestehen bleibt. Auf Complete-Case-basierte absolute Risiken wird verzichtet.
Was der Absatz leistet: Fehlquoten pro Variable; der Mechanismus mit Zahlen
statt Vermutungen belegt; Imputationsmodell, m, Seed und λ explizit benannt;
die Sensitivitätsanalyse beziffert statt behauptet — und die Aussage, welche
Schlussfolgerung stabil ist und welche Größe deshalb nicht berichtet wird.