Fehlende Werte in R

Missing Values

Im heutigen Post werde ich genauer auf fehlende Werte („missings“, „missing values“) eingehen. R hat einen eigenen Wert für fehlende Werte, nämlich NA (für „not available“). Missings können ein heikles Thema sein, aber wenn man damit umzugehen weiß, ist es alles nur noch halb so schlimm!

Die Grundlagen

Wir fangen mit den Grundlagen an. Wie schon erwähnt, werden fehlende Werte in R mit dem Wert NA dargestellt. NA ist hierbei keine Zeichenkette (d.h., kein character vector), sondern tatsächlich ein R-eigener Wert, der entsprechend farblich markiert wird. Wir können zum Beispiel einen Vektor mit einem Element erstellen, welches „missing“ ist: missingValue <- NA. Das Objekt missingValue beinhaltet nun einen Wert, der fehlend ist. Genauso können wir einen Vektor erstellen und ihn mit 100 missings füllen: vecMissings <- rep(NA, 100). Mit der Funktion rep ("replicate") ist das einfach getan. Mit missings kann man auch (mehr oder minder) Dinge berechnen. Zum Beispiel ergibt 1 + missingValue selbst wieder NA. Das ergibt Sinn, da wir (und R) ja nicht wissen, was missingValue überhaupt für einen Wert enthält. 1 + missingValue könnte also alles sein - wir wissen es aber nicht, und somit erhalten wir ein NA.

Auf Missings überprüfen

In einer explorativen Analyse eines Datensatzes ist es immer ratsam, eine "missing value analysis" durchzuführen. So können wir zum Beispiel gezielt überprüfen, ob ein Vektor fehlende Werte enthält oder nicht. Entgegen der Intuition können wir dies allerdings nicht mit dem Vergleichsoperator == machen. Denn tun wir dies, erhalten wir wieder selbst ein Missing: missingValue == NA. Stattdessen müssen wir die Funktion is.na benutzen: is.na(missingValue). Im Folgenden werde ich diese Funktion anhand eines simplen Beispiels veranschaulichen.

Beispiel

Angenommen wir haben eine dieser kleinen Garten-Wetterstationen auf einer Terrasse stehen und speichern jede Nacht um 23:59 Uhr die Maximaltemperatur des vergangenen Tages. Allerdings gibt es ein Problem: die Station ist schon etwas älter und es gibt hin und wieder Übertragungsfehler, sodass kein Wert für den jeweiligen Tag gespeichert wird. Für die letzte sonnige Woche hätten wir also zum Beispiel einen Vektor mit sieben Elementen: tempVec <- c(24.1, 28.3, 26.8, 23.5, NA, 25.6, NA). Wir sehen: Zwei Mal wurde der Wert nicht gespeichert. Da wir mittlerweile schon data frames kennen (wenn nicht, schau hier und hier), verschönern wir das Beispiel noch etwas und ordnen diese Temperaturen bestimmten Datumseinträgen zu. Den Datumsvektor erstellen wir wie folgt (heute noch etwas umständlicher per Hand): dateVec <- as.Date(c("2016-09-10", "2016-09-11", "2016-09-12", "2016-09-13", "2016-09-14", "2016-09-15", "2016-09-16")). Und beide Vektoren in ein data frame: dfTemp <- data.frame(Datum=dateVec, Temperatur=tempVec). Jetzt haben wir einen Minidatensatz mit Temperaturen je Datum.

Mit der is.na-Funktion können wir jetzt jedes Element im Temperaturvektor überprüfen, ob es missing ist oder nicht: is.na(dfTemp$Temperatur). Das ist schonmal ein guter Anfang, aber gerade für große Vektoren ist es lästig, jedes Element anzuzeigen. Stattdessen schauen wir uns einfach genau an, welche Elemente missing sind, und speichern die Positionen in missingCases: missingCases <- which(is.na(dfTemp$Temperatur)==TRUE). Mit which fragen wir hier also: Welche Elemente in dfTemp$Temperatur sind missings? Jetzt haben wir die Fälle (die Reihen), für die es missings in der Spalte "Temperatur" gibt. Entsprechend können wir uns die Tage anzeigen lassen, an denen es Probleme mit dem Speichern der Temperaturen gab: dfTemp$Datum[missingCases].

Möchten wir einfach nur wissen, wie viele Missings es gibt, so können wir folgendes tun: sum(is.na(dfTemp$Temperatur)). Warum funktioniert das? Wir erinnern uns (oder schauen oben nochmal hin): is.na(dfTemp$Temperatur) gibt uns einen Vektor mit TRUE/FALSE - Werten zurück (ein logical vector in R-Sprache). Da TRUE-Werte der 1 und FALSE-Werte der 0 entsprechen (und das von R automatisch umgewandelt wird), können wir den logical-Vektor einfach mit sum aufsummieren und kommen so zu unserem Ergebnis. Für eine generelle Übersicht können wir auch immer die summary-Funktion benutzen: summary(dfTemp$Temperatur); wir sehen, dass es hier auch eine Spalte gibt, die die Anzahl der NA's anzeigt.

Funktionen und Missings

Wir müssen immer auf NA's gefasst sein, da die meisten Funktionen fehlende Werte berücksichtigen und ihr Ergebnis entsprechend anpassen. Beispiel: max(dfTemp$Temperatur). Hier wollten wir schnell schauen, an welchem Tag es am wärmsten war. Allerdings haben wir nicht beachtet, dass es Missings geben könnte und bekommen in unserem Fall auch gleich ein NA zurück. Wie könnte man auch das Maximum herausfinden, wenn sie nicht weiß, wie die Temperatur an zwei der sieben Tage war? Wir müssen der Funktion also sagen: Gib uns den Maximalwert, aber nehme NA's aus deiner Berechnung heraus. Wir müssen also das Funktionsargument na.rm ("NA remove") mit übergeben: max(dfTemp$Temperatur, na.rm=TRUE). Und schon klappt es. Im Übrigen gilt das auch für andere Funktionen, z.B. mean, median, sum, usw.

Den Datensatz in Hinsicht auf Missings anpassen

Manchmal wollen wir alle weiteren Berechnungen nur mit einem vollständigen Datensatz durchführen. In unserem Fall schmeißen wir also alle Fälle raus, für die es Missings gab. Das lässt sich schnell erledigen: dfValidTemp <- dfTemp[!is.na(dfTemp$Temperatur),]. Wir definieren ein neues data frame dfValidTemp, welches im Prinzip dfTemp ist, aber nur die Fälle, für die es keine Missings gibt. Das Ausrufezeichen bedeutet hier "nicht", wörtlich also "dfTemp, für das gilt: nicht missing(dfTemp$Temperatur)".

Missings beim Lesen und Schreiben von Dateien

Zuletzt möchte ich noch kurz auf Missings beim Lesen und Schreiben von Dateien eingehen. Missings werden gelegentlich als bestimmte numerische Werte angegeben, welche per se unmöglich sind. Ein klassisches Beispiel sind hier Werte wie -999 oder -9999. Es wäre doch hilfreich, diese Werte sofort als Missings in R zu haben. Kein Problem: Wir können das gleich beim Einlesen einer Datei angeben: df <- read.csv("example.csv", na.strings="-999"). Hier haben wir einfach beim Funktionsargument na.strings den jeweiligen Wert angegeben. Gibt es mehrere Möglichkeiten, übergeben wir einfach einen Vektor im typischen R-Stil: df <- read.csv("example.csv", na.strings=c("-999", "-9999")). Alles, was vorher in der CSV-Datei als -999 oder -9999 stand, müsste jetzt in R ein NA sein. Beim Schreiben gibt es auch ein bestimmtes Argument, das uns bestimmen lässt, wie wir NA's in eine Datei schreiben möchten: write.csv(df, "example.csv", row.names=F, na=""). In diesem Fall möchten wir einfach gar nichts schreiben, dementsprechend setzen wir für na einen leeren character.

Hast du noch mehr Fragen zu Missings oder ein bestimmtes Problem in einem anderen Bereich? Schreib mir einfach eine Mail: mail@r-coding.de.
Bleib außerdem auf dem Laufenden mit dem r-coding Newsletter. Du erhältst Infos zu neuen Blogeinträgen, sowie kleine Tipps und Tricks zu R. Melde dich jetzt an: http://r-coding.de/newsletter.

Cheers!

Foto von Caleb Roenigk (siehe hier auf flickr), lizensiert unter CC2.0, modifiziert mit Schwarz-Weiß-Filter.

Ähnliche Beiträge

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.