Weiter zum Inhalt

Fehlende Daten mit dem VIM-Paket visualisieren

Lerne, mit den Visualisierungstools des VIM-Pakets schnell Einblicke in Muster fehlender Daten zu gewinnen.
Aktualisiert 18. Sept. 2026  · 13 Min. lesen

Mit KI erkunden

ChatGPTClaudePerplexity

Fehlende Daten sind im Arbeitsalltag jeder Data Scientist ein Thema. Sollten wir sie imputieren? Falls ja, welche Methode passt? Oder können Beobachtungen mit fehlenden Werten einfach entfernt werden? Um das zu entscheiden, musst du verstehen, warum Daten fehlen. Statistische Tests sind dafür oft kompliziert und liefern mitunter nur vage Hinweise. Visualisierungstools sind dagegen leicht anzuwenden und helfen nicht nur dabei, Mechanismen fehlender Daten zu erkennen, sondern liefern auch Einblicke in andere Aspekte der Datenqualität. In diesem Tutorial zeigen wir eine Reihe von Plot-Methoden aus dem VIM-Paket und wie sie dir helfen, die Muster hinter fehlenden Werten zu verstehen.

Mechanismen fehlender Daten

Es kann viele Gründe geben, warum ein Datensatz unvollständig ist. Es ist entscheidend zu untersuchen, welche Ursachen in Frage kommen, denn davon hängt ab, wie du das Problem angehst. Bei arbeitsbezogenen Umfragen etwa könnte man erwarten, dass die Allerreichsten und Allerärmsten ihr Einkommen nicht angeben – fehlende Werte verteilen sich dann nicht gleichmäßig über den Datensatz. In so einem Fall würde das simple Entfernen unvollständiger Beobachtungen zu verzerrten Ergebnissen führen. Fehlen Daten hingegen wegen eines Defekts am Erfassungsgerät, könnten die Positionen der fehlenden Punkte im Datensatz rein zufällig sein.

Man unterscheidet drei typische Muster, nach denen Daten fehlen können. Diese werden als Mechanismen fehlender Daten bezeichnet.

  • Missing completely at random (MCAR)
    Bei MCAR gibt es kein systematisches Muster, wo im Datensatz Werte fehlen: Das Fehlen ist komplett zufällig. Formal heißt das: Die Wahrscheinlichkeit, dass eine Beobachtung fehlt, hängt weder von anderen Variablen noch vom eigenen (unbeobachteten) Wert ab. In diesem Fall führt das Entfernen unvollständiger Beobachtungen nicht zu Verzerrungen in der anschließenden Analyse.
    Beispiel: Ein Sensor misst kontinuierlich die Temperatur und sendet die Daten per Internet an eine Datenbank. Aus unbekannten Gründen bricht die Verbindung gelegentlich ab.

  • Missing at random (MAR)
    Bei MAR ist die Wahrscheinlichkeit, dass eine Beobachtung fehlt, weiterhin unabhängig vom eigenen Wert, sie hängt jedoch von den Werten anderer Variablen ab. Entfernst du unvollständige Beobachtungen, wird die Stichprobe weniger repräsentativ.
    Beispiel: Einige Daten aus den Nachtstunden fehlen wegen Wartungsarbeiten am Sensor, die immer nachts stattfinden.

  • Missing not at random (MNAR)
    Bei MNAR hängt die Wahrscheinlichkeit, dass eine Beobachtung fehlt, vom eigenen, nicht beobachteten Wert ab. Auch hier führt das Entfernen unvollständiger Daten zu einer verzerrten Analyse.
    Beispiel: Der Sensor friert bei -20 Grad Celsius ein und misst darunter keine Temperaturen.

In der Praxis ist es schwierig, eindeutig festzustellen, welcher der drei Mechanismen in einem konkreten Fall vorliegt. Um das zu veranschaulichen, plotten wir MNAR-Daten. Wir verwenden die Pakete ggplot2 und gridExtra, die wir zuvor laden. Außerdem laden wir zwei weitere Pakete für später: VIM für die visuelle Analyse fehlender Daten und dplyr für etwas Vorverarbeitung. Denk daran, install.packages() aufzurufen, falls sie in deiner Umgebung noch fehlen.

library(\"VIM\")
library(\"dplyr\")
library(\"ggplot2\")
library(\"gridExtra\")

Erzeugen wir nun einen Datensatz mit zwei unkorrelierten, normalverteilten Variablen x und y, bei dem in y im rechten Verteilungsschwanz (also bei hohen Werten) einige Werte fehlen. Wir behalten die tatsächlichen Werte bei und markieren sie in einer separaten Variable als fehlend.

set.seed(2)
mnar_data <- data.frame(x = rnorm(100), y = rnorm(100)) %>%
    mutate(y_miss = ifelse(y > 1, y, NA),
           y = ifelse(is.na(y_miss), y, NA),
           x_miss = ifelse(is.na(y), x, NA))

Als Nächstes zeichnen wir zwei Streudiagramme von x gegen y: eines mit den Punkten, deren y-Werte wir als fehlend annehmen, und eines nur mit beobachteten Daten.

grid.arrange(
  # Plot mit Hervorhebung fehlender Datenpunkte
  ggplot(mnar_data, aes(x, y)) +
    geom_point(size = 4, alpha = 0.6) +
    geom_point(aes(x, y_miss), col = \"red\", size = 4, alpha = 0.6) +
    geom_hline(aes(yintercept = y_miss), col = \"red\", alpha = 0.6,
               linetype = \"dashed\") +
    ylim(NA, max(mnar_data$y_miss, na.rm = TRUE)) +
    ggtitle(\"Where the data points are missing\"),
  # Was Data Scientists sehen können
  ggplot(mnar_data, aes(x, y)) +
    geom_point(size = 4, alpha = 0.6) +
    geom_vline(aes(xintercept = x_miss), col = \"red\", alpha = 0.6,
               linetype = \"dashed\") +
    ylim(NA, max(mnar_data$y_miss, na.rm = TRUE)) +
    ggtitle(\"What the data scientist can see\"),
  # Beide Plots nebeneinander anordnen
  ncol = 2
)
\"scatter

Im ersten Plot ist klar zu erkennen, dass die Daten wirklich MNAR sind. Sichtbar sind für die Data Scientist jedoch nur die x-Koordinaten der fehlenden Punkte. Welche Schlussfolgerung erlaubt der zweite Plot? Sind die Daten MCAR? Möglich, die Positionen der fehlenden Werte scheinen recht gleichmäßig entlang der x-Achse verteilt. Oder MAR? Bei genauerem Hinsehen gibt es bei negativen x-Werten etwas mehr fehlende y-Werte als bei positiven, das könnte also auch sein. MNAR lässt sich schließlich nie ausschließen, denn um MNAR sicher zu erkennen, müssten wir per Definition die unobservierten Werte kennen.

Zum Glück sind wir damit nicht völlig orientierungslos. Datenvisualisierung kann Hinweise darauf geben, welche Muster in den fehlenden Daten stecken. Im Folgenden analysieren wir nützliche Plots zur Mustererkennung anhand des biopics-Datensatzes aus dem Paket fivethirtyeight mit Informationen zu biografischen Filmen.

Datenaufbereitung

Bevor wir plotten, braucht der Datensatz etwas Vorverarbeitung. Der folgende Code übernimmt das. select am Anfang der dplyr-Pipeline wählt die interessanten Variablen aus, anschließend fasst mutate verwandte Kategorien bei den Rassen zusammen, um ausreichend Beobachtungen pro Gruppe zu haben. Dann passen wir die Typen einiger Variablen an: Strings werden zu Faktoren, die logische Variable zu Integern. Zum Schluss vergeben wir kürzere Spaltennamen für besser lesbare Plots.

data(biopics, package = \"fivethirtyeight\")

biopics <- biopics %>%
  select(country, year_release, box_office, number_of_subjects,
         type_of_subject, subject_race, person_of_color, subject_sex) %>%
  mutate(subject_race = ifelse(grepl(\"^Hispanic\", subject_race), \"Hispanic\",
                               subject_race),
         subject_race = ifelse(grepl(\"^African\", subject_race), \"African\",
                               subject_race),
         subject_race = ifelse(grepl(\"^Middle\", subject_race), \"Mid Eastern\",
                               subject_race),
         subject_race = ifelse(subject_race %in% c(\"White\", \"Asian\", \"African\",
                                                   \"Hispanic\", \"Mid Eastern\",
                                                   \"Multi racial\", NA),
                               subject_race, \"other\")) %>%
  mutate(country = as.factor(country),
         type_of_subject = as.factor(type_of_subject),
         subject_race = as.factor(subject_race),
         subject_sex = as.factor(subject_sex),
         person_of_color = as.integer(person_of_color)) %>%
      as.data.frame()

colnames(biopics) <- c(\"country\", \"year\", \"earnings\", \"sub_num\",
                       \"sub_type\", \"sub_race\", \"non_white\", \"sub_sex\")

Am Ende haben wir einen Datensatz mit acht Variablen:

  • country – Herkunftsland bzw. -länder des Films,
  • year – Erscheinungsjahr,
  • earnings – Bruttoeinnahmen an den US-Kinokassen,
  • sub_num – Anzahl der im Film dargestellten Personen,
  • sub_type – Tätigkeit der Person bzw. Grund der Bekanntheit,
  • sub_race – ethnische Zugehörigkeit der Person,
  • non_white – Dummyvariable, die eine Person of Color anzeigt,
  • sub_sex – Geschlecht der dargestellten Person.

Aggregationsplots

Die erste, grundlegende Frage lautet: In welchen Variablen fehlen Beobachtungen, und wie viele sind es? Aggregationsplots sind hier sehr hilfreich. Die folgende Einzeile reicht aus.

aggr(biopics, numbers = TRUE, prop = c(TRUE, FALSE))
\"aggregation

Mit numbers = TRUE werden die Ziffern oberhalb der Balken eingeblendet. prop ist ein logischer Parameter, der steuert, ob Anteile fehlender Werte bzw. Kombinationen statt absoluter Zahlen angezeigt werden. Wir setzen ihn für den ersten Plot auf TRUE und für den zweiten auf FALSE.

Klar ist: Fehlende Werte gibt es nur in zwei Variablen – über 40% bei earnings und rund 25% bei sub_race. Im Kombinationen-Plot rechts zeigt das Gitter alle in den Daten vorkommenden Kombinationen aus fehlenden (rot) und beobachteten (blau) Werten. Es gibt 317 vollständige Beobachtungen, und in 77 Zeilen fehlen beide Variablen.

Zwei Plots gleichzeitig zu betrachten, kann unübersichtlich sein. Packen wir daher dieselben Informationen in eine einzige Visualisierung:

aggr(biopics, combined = TRUE, numbers = TRUE)

Mit dem Argument combined werden beide Plots kombiniert. Rechts neben dem Gitter zeigen horizontale Balken die Häufigkeiten der jeweiligen Kombinationen, während die vertikalen Balken darüber die Anteile fehlender Werte je Variable darstellen. Addieren wir die Werte aller Kombinationen für earnings (hier 0.32 und 0.10), erhalten wir einen Gesamtanteil fehlender Werte von 42% – passend zur groben Schätzung aus dem ersten Balkendiagramm.

Spinogram und Spineplot

Bislang haben wir einen groben Überblick gewonnen. Jetzt schauen wir auf Wechselwirkungen zwischen konkreten Variablen. Spinogram und Spineplot zeigen, wie hoch der Anteil fehlender Werte in einer Variablen für unterschiedliche Ausprägungen einer anderen Variablen ist. Ist letztere numerisch, sprechen wir vom Spinogram; ist sie kategorial, handelt es sich um ein Spineplot.

Beide Visualisierungen erzeugst du mit spineMiss(), das einen Data Frame mit zwei Spalten erwartet. Die zuerst angegebene Variable dient zur Aufteilung der Daten und liegt auf der x-Achse. Die zweite ist die Variable, deren Fehlmuster uns interessiert. Erstellen wir ein Spineplot: Wir geben zuerst die kategoriale Variable sub_race an, gefolgt von earnings. Das liest sich als: Wie hoch ist der Anteil fehlender Werte in earnings je Kategorie von sub_race?

spineMiss(biopics[, c(\"sub_race\", \"earnings\")])
\"Spinogram

Die relative Breite der Balken für die sub_race-Kategorien spiegelt deren Häufigkeit im Datensatz wider: In der großen Mehrheit der Filme ist die Hauptperson weiß. Innerhalb jedes Balkens wird der fehlende Anteil von earnings gezeigt, während der schattierte Balken rechts den Anteil für den gesamten Datensatz darstellt. Wenn die Hauptperson afrikanisch ist, liegen die Einnahmen am häufigsten vollständig vor.

Vertauschen wir die Reihenfolge der Variablen wie unten, erhalten wir ein Spinogram zur umgekehrten Frage: Wie hoch ist der Anteil fehlender Werte in sub_race für verschiedene Werte von earnings?

spineMiss(biopics[, c(\"earnings\", \"sub_race\")])
\"Spinogram

Da earnings numerisch ist, werden die Werte in Bins aufgeteilt, deren Breiten der Verteilung der Variable entsprechen. Das Spinogram zeigt eine stark linksschiefe Verteilung von earnings: Nur wenige Filme erzielten die höchsten Gewinne. Interessant ist: Gerade bei diesen Blockbustern fehlt am ehesten die Rassenangabe der Hauptperson – sichtbar am höchsten roten Balken für die größten earnings-Werte.

Mosaikplot

Spinogram und Spineplot betrachten jeweils zwei Variablen. Das lässt sich auf mehr Variablen verallgemeinern: den Mosaikplot. Er besteht aus Kacheln, die jeweils eine bestimmte Kombination von Kategorien (bei kategorialen Variablen) oder Bins (bei numerischen Variablen) aus zwei oder mehr Variablen darstellen. In jeder Kachel wird der Prozentsatz fehlender Werte einer weiteren Variablen angezeigt. Grundsätzlich kannst du Mosaikplots für beliebig viele Variablen erstellen, doch zu viele Dimensionen machen ihn unübersichtlich. Am besten funktionieren Faktoren mit nicht allzu vielen Stufen.

Betrachten wir den fehlenden Anteil in earnings, aufgeteilt nach sub_sex und US_movie. Letztere Variable erzeugen wir im folgenden Code: ein logischer Indikator, ob die USA an der Produktion beteiligt waren. Wir übergeben den dreispaltigen Data Frame als erstes Argument an mosaicMiss(). Mit plotvars = 1:2 teilen wir nach den ersten beiden Spalten auf. highlight = 3 bedeutet, dass der fehlende Anteil der dritten Spalte in den Kacheln angezeigt wird.

biopics <- biopics %>%
    mutate(US_movie = ifelse(grepl(\"US\", country), TRUE, FALSE))
mosaicMiss(biopics[, c(\"sub_sex\", \"US_movie\", \"earnings\")], highlight = 3,
           plotvars = 1:2, miss.labels = FALSE)
\"Mosaic

Auch hier entsprechen die Kachelgrößen den Auftretenshäufigkeiten der Kombinationen. Die größte Kachel unten rechts bedeutet: Die meisten Filme handeln von einer männlichen Person und wurden zumindest teilweise in den USA produziert.

Bei Filmen mit männlicher Hauptfigur fehlen die Einnahmen eher bei nicht-amerikanischen Produktionen. Ist die Hauptfigur weiblich, gibt es in US-Filmen leicht mehr fehlende Einnahmen. Die Unterschiede wirken jedoch zu klein, um als deutlich relevant zu gelten.

Paralleler Boxplot

Ein weiterer Visualisierungstyp ist der parallele Boxplot. Die Idee: Teile den Datensatz in zwei Teilmengen – eine mit beobachteten Werten einer unvollständigen Variablen und eine mit fehlenden Werten dieser Variablen. Für beide Teilmengen wird ein Boxplot einer gewählten numerischen Variablen gezeichnet. So siehst du, ob deren Verteilung durch fehlende Werte in der trennenden Variablen beeinflusst wird.

Den parallelen Boxplot erzeugst du mit pbox(). Übergeben wird ein Data Frame mit zwei Spalten. Für die erste Spalte wird der Boxplot gezeichnet, die zweite dient zur Aufteilung. Im Beispiel nehmen wir den Logarithmus von earnings, da die Verteilung stark schief ist – ohne Transformation würde der Boxplot fast wie eine Linie wirken.

biopics <- biopics %>%
  mutate(log_earnings = log(earnings))
pbox(biopics[, c(\"log_earnings\", \"sub_race\")])
\"Parallel

Die weiße Box links zeigt die Gesamtverteilung von log_earnings, die blaue und die rote Box die Verteilungen für Beobachtungen mit vorhandenen bzw. fehlenden Werten in sub_race. Die relative Boxbreite spiegelt die Größe der Teilmengen wider: Die breitere blaue Box bedeutet mehr beobachtete als fehlende Werte in sub_race. Abgesehen davon sehen beide Boxen ähnlich aus – auch im Vergleich zur Gesamtbox. Das deutet darauf hin, dass fehlende Rassenangaben die Verteilung der Einnahmen nicht beeinflussen.

Paralleler Koordinatenplot

Bisher haben wir einzelne Variablen und deren Interaktionen betrachtet. Jetzt analysieren wir alle Variablen gleichzeitig. Das geeignete Werkzeug ist der parallele Koordinatenplot. Dabei wird jede Variable auf dieselbe Skala transformiert und durch eine parallele Achse dargestellt. Kategoriale Variablen werden als gleichabständige Punkte auf ihrer Achse abgebildet. Fehlende Werte liegen oberhalb der Achsen, außerhalb des Plotbereichs. Jede Linie repräsentiert eine Beobachtung, die Linienfarbe markiert fehlende Werte in einer gewählten Variablen.

Den Plot erzeugst du mit parcoordMiss(). Standardmäßig werden alle Variablen des übergebenen Data Frames verwendet. Mit highlight = earnings werden Beobachtungen mit fehlenden earnings farblich hervorgehoben. Eine Alpha-Transparenz von 0.6 erhöht die Lesbarkeit.

# Für vorherige Plots erzeugte Variablen entfernen
biopics <- biopics %>%
  select(- US_movie, - log_earnings)

parcoordMiss(biopics, highlight = 'earnings', alpha = 0.6)
\"Parallel

Im Plot markieren dunkelrote Linien Beobachtungen mit fehlenden earnings. Diese heben sich vom Rest ab. Besonders auffällig: Nur wenige davon fallen auf die zweithöchste Ausprägung von country, obwohl dort sonst viele Beobachtungen liegen! Dahinter verbergen sich US/UK-Filme (prüfe das mit levels(biopics$country) und suche den vorletzten Wert!).

Außerdem zeigt die year-Achse einen Abschnitt etwa auf zwei Dritteln ihrer Höhe, in dem weniger dunkelrote Linien verlaufen. Offenbar gibt es eine jüngere Phase, in der die Einnahmendaten kompletter sind. Das legt nahe, dass sowohl country als auch year die Verteilung fehlender Werte in earnings mit erklären können.

Matrixplot

Zum Schluss betrachten wir den Matrixplot. Er stellt alle Zellen der Datenmatrix als Rechtecke dar. Beobachtete Daten erscheinen in einer kontinuierlichen Grau-Schwarz-Skala (je dunkler, desto größer der Wert), fehlende Werte werden rot hervorgehoben. Es bewährt sich, die Daten nach einer unvollständigen Variablen zu sortieren – das erleichtert die Interpretation. Unten sortieren wir nach earnings.

matrixplot(biopics, sortby = c('earnings'))
\"Matrix

Der Plot bestätigt einige der bisherigen Beobachtungen: Zeilen mit fehlenden earnings stammen tendenziell aus früheren Jahren (niedriges year) und weisen ein niedriges country-Niveau auf – erkennbar an den helleren Farbtönen dieser beiden Variablen in den Zeilen mit roten Einnahmen. Außerdem scheint es bei fehlenden earnings weniger Filme mit nichtweißen Hauptpersonen zu geben.

Fazit

Zusammengefasst scheinen die fehlenden Daten im biopics-Datensatz nicht MCAR zu sein. Die Positionen fehlender Werte in earnings und sub_race erklären sich gegenseitig, wie Spineplot und Spinogram gezeigt haben. Zudem deuten Matrixplot und paralleler Koordinatenplot darauf hin, dass country, year und non_white die Verteilung fehlender Werte in earnings ebenfalls mit beeinflussen. Unvollständige Beobachtungen zu verwerfen, würde daher sehr wahrscheinlich Verzerrungen in die Schlussfolgerungen bringen.

Abschließende Gedanken

Starke Leistung! Du kennst jetzt die Mechanismen fehlender Daten, ihre Unterschiede und vor allem, wie du sie mit verschiedenen Visualisierungstools erkundest. Gut gemacht! Damit bist du bestens gerüstet, deine eigenen unvollständigen Datensätze zu analysieren.

Wenn du mehr über R lernen möchtest, schau dir den DataCamp-Kurs Data Visualization with ggplot2 (Part 1) an und lies unser R Formula Tutorial.

Themen
Datenvisualisierung
Datenwissenschaft

Mehr über R und Datenvisualisierung lernen

Kurs

Einführung in die Datenvisualisierung mit ggplot2

4 Std.
188.2K
Lerne die Grundlagen professioneller Diagramme und erstelle aussagekräftige und ansprechende Datenvisualisierungen mit ggplot2.
Details anzeigenRight Arrow
Kurs Starten
Mehr anzeigenRight Arrow
Verwandt

Tutorial

Python Datenstrukturen Tutorial

Mach dich mit Python-Datenstrukturen vertraut: Lerne mehr über Datentypen und primitive sowie nicht-primitive Datenstrukturen wie Strings, Listen, Stapel usw.
Sejal Jaiswal's photo

Sejal Jaiswal

24 Min.

Tutorial

Python-Tutorial zum Verknüpfen von Zeichenfolgen

Lerne verschiedene Methoden zum Verknüpfen von Zeichenfolgen in Python kennen, mit Beispielen, die jede Technik zeigen.
DataCamp Team's photo

DataCamp Team

5 Min.

Tutorial

Python NaN: 4 Möglichkeiten, um in Python nach fehlenden Werten zu suchen

Schau dir 4 Möglichkeiten an, wie du NaN-Werte in Python mit NumPy und Pandas erkennen kannst. Lerne die wichtigsten Unterschiede zwischen NaN und None kennen, um Daten effizient zu bereinigen und zu analysieren.
Adel Nehme's photo

Adel Nehme

5 Min.

Tutorial

Fibonacci-Folge in Python: Lerne und entdecke Programmiertechniken

Finde raus, wie die Fibonacci-Folge funktioniert. Schau dir die mathematischen Eigenschaften und die Anwendungen in der echten Welt an.
Laiba Siddiqui's photo

Laiba Siddiqui

6 Min.

Tutorial

Python-Arrays

Python-Arrays mit Code-Beispielen. Lerne noch heute, wie du mit Python NumPy Arrays erstellen und ausdrucken kannst!
DataCamp Team's photo

DataCamp Team

3 Min.

Tutorial

Python Switch Case Statement: Ein Leitfaden für Anfänger

Erforsche Pythons match-case: eine Anleitung zu seiner Syntax, Anwendungen in Data Science und ML sowie eine vergleichende Analyse mit dem traditionellen switch-case.
Matt Crabtree's photo

Matt Crabtree

5 Min.

Mehr AnzeigenMehr Anzeigen