Imputieren von fehlenden Werten

Methoden der Zeitreihenanalyse

Moderator: schubbiaschwilli

Antworten
Effigy
Beiträge: 35
Registriert: Di Nov 26, 2019 11:44 am

Imputieren von fehlenden Werten

Beitrag von Effigy »

Hallo zusammen,

ich habe einen Datensatz mit Temperaturwerten von sechs verschiedenen Wetterstationen und möchte alle Werte imputieren, bei denen pro Tag und Monat mehr als 20 % NA-Werte, also fehlende Werte vorliegen. Die Daten werden alle 30 Minuten aufgezeichnet. Ich möchte die Daten erstmal nach Tagen gruppieren und dann alle Tage rausfiltern, an denen weniger als 20 % NA sind. Diese Tage möchte ich behalten. Die restlichen Tage möchte ich dann imputieren. Dasselbe möchte ich später für Monate machen, weil es manchmal in einem Monat mehrere Tage gibt, an denen alle Werte fehlen.
Mein Problem ist, dass ich nicht weiß, ob der Filter funktioniert bzw. ob es die beste Vorgehensweise ist. Außerdem bin ich mir nicht sicher, was danach die beste Methode ist, um die fehlenden Werte zu imputieren. Ich habe mich mit dem imputeTS package auseinandergesetzt, aber weiß nicht, ob na_seadec,na_seasplit o.ä am besten für mein Problem geeignet ist.

Meine Daten (Probe, erstellt mit slice_sample, n=20 aus dem dplyr package)

Code: Alles auswählen

df <- structure(list(td = structure(c(1591601400, 1586611800, 1574420400, 1583326800, 1568898000, 1561969800, 1577010600, 1598238000, 1593968400, 1567800000, 1590967800, 1584981000, 1563597000, 1589117400, 1599796800, 1563467400, 1569819600, 1571014800, 1573320600, 1577154600), tzone = "UTC", class = c("POSIXct", "POSIXt")), Temp_Dede = c(13.7, NA, NA, 6.4, 14.9, 19.1, 1.3, 14.2, 21.1, 15.1, 10, 5, 14.1, 24.2, 8.8, 25.3, 14.9, 19.7, NA, 6.2), Temp_188 = c(13.1, 12.6, 8.9, 6.3, 14.5, 18.8, 1.4, 14.2, 20.9, 13.1, 10.4, 5.1, 12.2, 24.2, 9.4, 25.9, 14.8, 18.9, NA, 6.1), Temp_275 = c(13.9, 12.6, 8.8, 6, 14.3, 18.9, 1.4, 13.5, 20.4, 12.2, 11.1, 4.6, 12.5, 23.3, 9.9, 24, 14.8, 19.2, 6.9, 5.9), Temp_807 = c(13.9, 13.1, 8.8, 6.2, 14.3, 19.1, 1.4, 14.7, 20.5, 13.3, 10.6, 4.9, 12.8, 23.1, 10.3, 24.8, 14.7, 19.1, 6.9, 6.1), Temp_1189 = c(13.7, 12.3, 8.8, 5.6, 14.1, 18.4, 1.4, 13.3, 19.9, 13.3, 10.7, 4.4, 13.6, 24, 9.8, 24.9, 14.7, 19.1, 6.9, 5.7), Temp_1599 = c(13.2, 12.7, 8.8, 5.1, 14.3, 18.3, 1.8, 14.2, 20.3, 13.2, 10.6, 4.4, 12.1, 22.9, 9.8, 25.8, 14.8, 19.2, 6.9, 5.9)), row.names = c(NA, -20L), class = "data.frame")
Der Code, den ich benutze.

Code: Alles auswählen

df %>% group_by(Datum) %>% filter_at(vars(Temp_Dede, Temp_188, Temp_275, Temp_807, Temp_1189, Temp_1599),~mean(is.na(.) <0.2))
Ich weiß nicht, wie ich jetzt weiter vorgehen soll und bin für jede Hilfe sehr dankbar! :)

Einen schönen Tag noch,
Effigy
Zuletzt geändert von jogo am Mo Nov 16, 2020 2:27 pm, insgesamt 1-mal geändert.
Grund: Formatierung verbessert. http://forum.r-statistik.de/viewtopic.php?f=20&t=29
schubbiaschwilli
Beiträge: 253
Registriert: Di Jun 27, 2017 12:09 pm

Re: Imputieren von fehlenden Werten

Beitrag von schubbiaschwilli »

Gude!

"bei denen pro Tag und Monat mehr als 20 % NA-Werte, also fehlende Werte vorliegen."
Du kannst die Funktion aggregate nutzen, um über die Tage die NAs zu Summieren. Da du aber davon schreibst, dass du alle 30 Minuten Daten hast, hast du mehrere Timestamps an einem Tag, weswegen ich das nur so hinbekomme, dass ich mit einer Schleife die Tabelle durchlaufe, und die einzelnen Werte (Spalten) mit is.na prüfe und das Ergebnis per Zeile speichere, und über das Datum gruppiere, und die Summe über dieses Ergebnis bilde. Ist vielleicht etwas konventionell, aber läuft erstmal.

Code: Alles auswählen

df$NumberOfNAs <- 0

for(i in 1:nrow(df)){
  df[i, "NumberOfNAs"] <- sum(is.na(df[i,2:7]))
}

aggregate(df$NumberOfNAs, by=list(as.Date(df$td)), FUN=sum)
So als ersten Wurf - Ist es das, was du mit dem ersten Teil deiner Frage meinst? Wenn du das jetzt mit der Anzahl der Datenpunkte vergleichst, kannst du ja bestimmen, ob deine 20% NAs vorliegen.
Die Anzahl, wie oft jeder Tag vorkommt, liefert bspw.:

Code: Alles auswählen

aggregate(!is.na(df$td), by=list(as.Date(df$td)), FUN=sum)
Nachtrag: Dein Code läuft bei mir nicht - Welche Pakete nutzt du?

Danke an jogo für's Formatieren.

Dank&Gruß
schubbiaschwilli
Effigy
Beiträge: 35
Registriert: Di Nov 26, 2019 11:44 am

Re: Imputieren von fehlenden Werten

Beitrag von Effigy »

Hey,

danke für deine Antwort. Ich habe es jetzt "manuell" für jede Station einzeln gemacht. Der Code sieht nun so aus (Beispiel für Temp_Dede also die Temperatur der Station Dede):

Code: Alles auswählen

# Aufbereitung von allen WS: Nach Tagen gruppieren, dann alle Tage mit >20 % NA rausfiltern
# Alle Tage mit <20 % NAs imputieren mit na_kalman
# Alle Monate mit  > 20 % rausfiltern
# Alle Monate mit <20 % Na imputieren mit na_kalman
# Für jede Station einzeln so machen, dann alle Stationen joinen und dann plotten

df %>% select (c("td","Temp_Dede")) %>%
            separate(td,c("Datum", "Zeit"), " ") %>%
            mutate(Datum= ymd(Datum)) %>%
            group_by(Datum) %>%
            mutate(Temp_Dede_mean = mean(is.na(Temp_Dede))) %>%
            filter(across(c("Temp_Dede_mean"))<0.2) %>% # alle Tage rausfiltern, wo mehr als 20 % NAs sind
            mutate(Temp_Dede_imp= if_else(is.na(Temp_Dede), na_kalman(Temp_Dede), Temp_Dede)) %>%
            select(c("Datum", "Temp_Dede_imp")) %>%
            group_by(Datum) %>%
            summarize_at(vars(Temp_Dede_imp), mean) %>%
            mutate(Temp_Dede_imp= round(Temp_Dede_imp,2)) %>%
            pad() %>% 
            mutate(mon= strftime(Datum, format = "%Y-%m"))%>%
            group_by(mon)%>%
            mutate(na_month_mean = mean(is.na(Temp_Dede_imp))) %>%
            filter(across(c("na_month_mean"))<0.2) %>% # alle Monate rausfiltern, an denen mehr als 20 % NAs sind
            mutate(Temp_Dede_imp= if_else(is.na(Temp_Dede_imp), na_kalman(Temp_Dede_imp), Temp_Dede_imp))%>%
            select(c("Datum", "Temp_Dede_imp")) %>%
            ungroup() %>%
            pad() -> WSDede_imp
Der Code spuckt einen Error aus, weil der df ja nur ein slice des original Datensatzes ist, aber bei mir klappt es. Meine Frage, die Sachen, die ich mache, kann man das auch eleganter und mit weniger Zeilen lösen? Außerdem:
Ich habe als Imputationsmethode na_kalman aus dem imputeTS package genommen. Ist das das Angemessene für meine Zwecke? Am liebsten wären mir Lösungen mit dem Pipe operator und dem dplyr package, weil ich damit gut umgehen kann und den Code lesbarer finde. Ich benutzte folgende packages, alle mit der neuesten Version. :)

library("tidyverse")
library ("lubridate")
library("padr")
library("imputeTS")

Liebe Grüße,
Max
Antworten