In einem data.frame (oder einer data.table) möchte ich NAs mit dem nächsten vorherigen Nicht-NA-Wert "vorwärts füllen". Ein einfaches Beispiel für die Verwendung von Vektoren (anstelle von a data.frame
) ist das folgende:
> y <- c(NA, 2, 2, NA, NA, 3, NA, 4, NA, NA)
Ich möchte eine Funktion fill.NAs()
, mit der ich Folgendes konstruieren kann yy
:
> yy
[1] NA NA NA 2 2 2 2 3 3 3 4 4
Ich muss diesen Vorgang für viele (insgesamt ~ 1 TB) kleine data.frame
s (~ 30-50 Mb) wiederholen , bei denen eine Zeile NA ist und alle ihre Einträge sind. Was ist ein guter Weg, um das Problem anzugehen?
Die hässliche Lösung, die ich mir ausgedacht habe, verwendet diese Funktion:
last <- function (x){
x[length(x)]
}
fill.NAs <- function(isNA){
if (isNA[1] == 1) {
isNA[1:max({which(isNA==0)[1]-1},1)] <- 0 # first is NAs
# can't be forward filled
}
isNA.neg <- isNA.pos <- isNA.diff <- diff(isNA)
isNA.pos[isNA.diff < 0] <- 0
isNA.neg[isNA.diff > 0] <- 0
which.isNA.neg <- which(as.logical(isNA.neg))
if (length(which.isNA.neg)==0) return(NULL) # generates warnings later, but works
which.isNA.pos <- which(as.logical(isNA.pos))
which.isNA <- which(as.logical(isNA))
if (length(which.isNA.neg)==length(which.isNA.pos)){
replacement <- rep(which.isNA.pos[2:length(which.isNA.neg)],
which.isNA.neg[2:max(length(which.isNA.neg)-1,2)] -
which.isNA.pos[1:max(length(which.isNA.neg)-1,1)])
replacement <- c(replacement, rep(last(which.isNA.pos), last(which.isNA) - last(which.isNA.pos)))
} else {
replacement <- rep(which.isNA.pos[1:length(which.isNA.neg)], which.isNA.neg - which.isNA.pos[1:length(which.isNA.neg)])
replacement <- c(replacement, rep(last(which.isNA.pos), last(which.isNA) - last(which.isNA.pos)))
}
replacement
}
Die Funktion fill.NAs
wird wie folgt verwendet:
y <- c(NA, 2, 2, NA, NA, 3, NA, 4, NA, NA)
isNA <- as.numeric(is.na(y))
replacement <- fill.NAs(isNA)
if (length(replacement)){
which.isNA <- which(as.logical(isNA))
to.replace <- which.isNA[which(isNA==0)[1]:length(which.isNA)]
y[to.replace] <- y[replacement]
}
Ausgabe
> y
[1] NA 2 2 2 2 3 3 3 4 4 4
... was zu funktionieren scheint. Aber Mann, ist es hässlich? Irgendwelche Vorschläge?
r
data.table
zoo
r-faq
Ryogi
quelle
quelle
roll=TRUE
indata.table
.fill
inR
tidyr::fill()
.Antworten:
Sie möchten wahrscheinlich die
na.locf()
Funktion aus dem Zoo- Paket verwenden, um die letzte Beobachtung fortzusetzen und Ihre NA-Werte zu ersetzen.Hier ist der Beginn des Verwendungsbeispiels auf der Hilfeseite:
quelle
na.locf
im Zoo sowohl mit gewöhnlichen Vektoren als auch mit Zooobjekten gearbeitet wird. Seinna.rm
Argument kann in einigen Anwendungen nützlich sein.na.locf(cz, na.rm=FALSE)
, um weiter zu führenNA
.Entschuldigen Sie, dass Sie eine alte Frage ausgegraben haben. Ich konnte die Funktion für diesen Job im Zug nicht nachschlagen, also habe ich selbst eine geschrieben.
Ich war stolz herauszufinden, dass es ein bisschen schneller ist.
Es ist jedoch weniger flexibel.
Aber es spielt gut damit
ave
, was ich brauchte.Bearbeiten
Da dies meine am meisten befürwortete Antwort wurde, wurde ich oft daran erinnert, dass ich meine eigene Funktion nicht benutze, weil ich oft das
maxgap
Argument des Zoos brauche . Da der Zoo in Randfällen einige seltsame Probleme hat, wenn ich dplyr + -Daten verwende, die ich nicht debuggen konnte, bin ich heute darauf zurückgekommen, um meine alte Funktion zu verbessern.Ich habe meine verbesserte Funktion und alle anderen Einträge hier verglichen. Für die Grundfunktionen
tidyr::fill
ist am schnellsten, ohne auch die Randfälle zu verfehlen. Der Rcpp-Eintrag von @BrandonBertelsen ist noch schneller, aber hinsichtlich des Eingabetyps unflexibel (er hat Kantenfälle aufgrund eines Missverständnisses von falsch getestetall.equal
).Wenn Sie brauchen
maxgap
, ist meine Funktion unten schneller als der Zoo (und hat nicht die seltsamen Probleme mit Daten).Ich habe die Dokumentation meiner Tests erstellt .
neue Funktion
Ich habe die Funktion auch in mein formr-Paket aufgenommen (nur Github).
quelle
df
mit mehreren Spalten anwenden möchten ?na.locf0
jetzt in Umfang und Leistung Ihrerrepeat_last
Funktion ähnlich ist . Der Anhaltspunkt war,diff
eher zu verwenden alscumsum
und zu vermeidenifelse
. Diena.locf.default
Hauptfunktion ist immer noch etwas langsamer, da sie einige weitere Überprüfungen durchführt und mehrere Spalten usw. verarbeitet.Um effizienter zu arbeiten, können wir das Paket data.table verwenden, um effizienter zu sein.
quelle
replaceNaWithLatest <- function( dfIn, nameColsNa = names(dfIn)[1] ){ dtTest <- data.table(dfIn) invisible(lapply(nameColsNa, function(nameColNa){ setnames(dtTest, nameColNa, "colNa") dtTest[, segment := cumsum(!is.na(colNa))] dtTest[, colNa := colNa[1], by = "segment"] dtTest[, segment := NULL] setnames(dtTest, "colNa", nameColNa) })) return(dtTest) }
eine
data.table
Lösung:Dieser Ansatz könnte auch mit vorwärts füllenden Nullen funktionieren:
Diese Methode ist sehr nützlich für Daten in großem Maßstab und dort, wo Sie eine Vorwärtsfüllung nach Gruppe (n) durchführen möchten, was bei trivial ist
data.table
. Fügen Sie einfach die Gruppe (n)by
vor dercumsum
Logik zur Klausel hinzu .quelle
Ich werfe meinen Hut hinein:
Richten Sie ein Basisbeispiel und einen Benchmark ein:
Und führen Sie einige Benchmarks durch:
Nur für den Fall:
Aktualisieren
Für einen numerischen Vektor ist die Funktion etwas anders:
quelle
Das hat bei mir funktioniert:
Geschwindigkeit ist auch vernünftig:
quelle
replace_na_with_last(c(NA,1:4,NA))
(dh sie sind mit dem folgenden Wert gefüllt). Dies ist auch das Standardverhalten vonimputeTS::na.locf(x, na.remaining = "rev")
.replace_na_with_last<-function(x,p=is.na,d=0)c(d,x)[cummax(seq_along(x)*(!p(x)))+1]
Probieren Sie diese Funktion aus. Das ZOO-Paket ist nicht erforderlich:
Beispiel:
quelle
if (!anyNA(x)) return(x)
.Eine Führung zu haben
NA
ist ein bisschen faltig, aber ich finde eine sehr lesbare (und vektorisierte) Möglichkeit, LOCF zu machen, wenn der führende Begriff nicht fehlt:na.omit(y)[cumsum(!is.na(y))]
Eine etwas weniger lesbare Modifikation funktioniert im Allgemeinen:
c(NA, na.omit(y))[cumsum(!is.na(y))+1]
gibt die gewünschte Ausgabe:
c(NA, 2, 2, 2, 2, 3, 3, 4, 4, 4)
quelle
Sie können die
data.table
Funktion verwendennafill
, die unter verfügbar istdata.table >= 1.12.3
.Wenn Ihr Vektor eine Spalte in a ist
data.table
, können Sie ihn auch mit folgendem Verweis aktualisierensetnafill
:Wenn Sie
NA
in mehreren Spalten haben ...... Sie können sie auf einmal durch Referenz füllen:
Beachten Sie, dass:
Die Funktionalität wird höchstwahrscheinlich bald erweitert; Weitere Informationen finden Sie in der offenen Ausgabe nafill, setnafill für Charakter, Faktor und andere Typen . Dort finden Sie auch eine vorübergehende Problemumgehung .
quelle
Das Tidyverse-Paket schlägt einen einfachen Weg vor, dies zu tun:
quelle
Es gibt eine Reihe von Paketen mit Funktionen
na.locf
(NA
Last Observation Carried Forward):xts
- -xts::na.locf
zoo
- -zoo::na.locf
imputeTS
- -imputeTS::na.locf
spacetime
- -spacetime::na.locf
Und auch andere Pakete, bei denen diese Funktion anders benannt ist.
quelle
Weiterverfolgung der Rcpp-Beiträge von Brandon Bertelsen. Für mich hat die NumericVector-Version nicht funktioniert: Sie hat nur die erste NA ersetzt. Das liegt daran, dass die
ina
Vektor zu Beginn der Funktion nur einmal ausgewertet wird.Stattdessen kann man genau den gleichen Ansatz wie für die IntegerVector-Funktion wählen. Folgendes hat bei mir funktioniert:
Wenn Sie eine CharacterVector-Version benötigen, funktioniert der gleiche grundlegende Ansatz auch:
quelle
Hier ist eine Modifikation der @ AdamO-Lösung. Dieser läuft schneller, weil er die
na.omit
Funktion umgeht . Dadurch werden dieNA
Werte im Vektor überschriebeny
(mit Ausnahme der führendenNA
s).quelle
Ich habe Folgendes versucht:
nullIdx erhält die IDX-Nummer, wenn masterData $ RequiredColumn einen Null / NA-Wert hat. In der nächsten Zeile ersetzen wir ihn durch den entsprechenden Idx-1-Wert, dh den letzten guten Wert vor jedem NULL / NA
quelle
1 NA NA
verwandelt sich in1 1 NA
. Auch ich denke dasas.array()
ist unnötig.Dies hat bei mir funktioniert, obwohl ich nicht sicher bin, ob es effizienter ist als andere Vorschläge.
quelle
Reduzieren ist ein schönes funktionales Programmierkonzept, das für ähnliche Aufgaben nützlich sein kann. Leider ist es in R ~ 70 mal langsamer als
repeat.before
in der obigen Antwort.quelle
Ich persönlich benutze diese Funktion. Ich weiß nicht, wie schnell oder langsam es ist. Aber es macht seinen Job, ohne Bibliotheken benutzen zu müssen.
Wenn Sie diese Funktion in einem Datenrahmen anwenden möchten, wenn Ihr Datenrahmen df heißt, dann einfach
quelle