W data.frame (lub data.table) chciałbym „wypełnić do przodu” NA najbliższą poprzednią wartością inną niż NA. Prosty przykład użycia wektorów (zamiast a data.frame
) jest następujący:
> y <- c(NA, 2, 2, NA, NA, 3, NA, 4, NA, NA)
Chciałbym mieć funkcję, fill.NAs()
która pozwoliłaby mi skonstruować yy
:
> yy
[1] NA NA NA 2 2 2 2 3 3 3 4 4
Muszę powtórzyć tę operację dla wielu (łącznie ~ 1 Tb) małych data.frame
s (~ 30-50 Mb), gdzie wiersz to NA to wszystkie jego wpisy. Jaki jest dobry sposób rozwiązania problemu?
Brzydkie rozwiązanie, które przygotowałem, wykorzystuje tę funkcję:
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
}
Funkcja fill.NAs
jest używana w następujący sposób:
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]
}
Wynik
> y
[1] NA 2 2 2 2 3 3 3 4 4 4
... który wydaje się działać. Ale człowieku, czy to brzydkie! Jakieś sugestie?
r
data.table
zoo
r-faq
Ryogi
źródło
źródło
roll=TRUE
wdata.table
.fill
wR
tidyr::fill()
.Odpowiedzi:
Prawdopodobnie chcesz użyć
na.locf()
funkcji z pakietu zoo , aby przenieść ostatnią obserwację do przodu, aby zastąpić wartości NA.Oto początek przykładu użycia ze strony pomocy:
źródło
na.locf
w zoo działa ze zwykłymi wektorami, a także z obiektami zoo. Jejna.rm
argument może być przydatny w niektórych aplikacjach.na.locf(cz, na.rm=FALSE)
aby kontynuowaćNA
.Przepraszam, że podchodzę do starego pytania. Nie mogłem znaleźć funkcji, aby wykonać tę pracę w pociągu, więc napisałem ją sam.
Byłem dumny, gdy dowiedziałem się, że jest trochę szybszy.
Jest jednak mniej elastyczny.
Ale dobrze się z tym bawi
ave
, czego potrzebowałem.Edytować
Ponieważ była to moja najbardziej pozytywna odpowiedź, często przypominano mi, że nie używam własnej funkcji, ponieważ często potrzebuję
maxgap
argumentu zoo . Ponieważ zoo ma dziwne problemy w skrajnych przypadkach, gdy używam dat dplyr +, których nie mogłem debugować, wróciłem do tego dzisiaj, aby ulepszyć moją starą funkcję.Testowałem moją ulepszoną funkcję i wszystkie inne wpisy tutaj. W przypadku podstawowego zestawu funkcji
tidyr::fill
jest najszybszy, a jednocześnie nie zawodzi w skrajnych przypadkach. Wpis Rcpp autorstwa @BrandonBertelsen jest jeszcze szybszy, ale jest nieelastyczny w odniesieniu do typu wejścia (niepoprawnie przetestował przypadki krawędzi z powodu niezrozumieniaall.equal
).Jeśli potrzebujesz
maxgap
, moja funkcja poniżej jest szybsza niż zoo (i nie ma dziwnych problemów z datami).Umieściłem dokumentację z moich testów .
nowa funkcja
Umieściłem także funkcję w moim pakiecie formr (tylko Github).
źródło
df
kolumny z wieloma kolumnami?na.locf0
która jest teraz podobna pod względem zakresu i wydajności do twojejrepeat_last
funkcji. Chodziło o to, abydiff
raczej używać niżcumsum
i unikaćifelse
. Głównana.locf.default
funkcja jest nadal nieco wolniejsza, ponieważ wykonuje więcej sprawdzeń i obsługuje wiele kolumn itp.data.table
rozwiązanie:to podejście może również działać z zerami wypełniającymi do przodu:
ta metoda staje się bardzo przydatna w przypadku danych na dużą skalę i tam, gdzie chciałbyś wykonać wypełnienie do przodu według grup (y), co jest trywialne w przypadku
data.table
. po prostu dodaj grupę (y) doby
klauzuli poprzedzającejcumsum
logikę.źródło
Mając do czynienia z dużym wolumenem danych, aby być bardziej wydajnym, możemy skorzystać z pakietu data.table.
źródło
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) }
Wrzucam kapelusz:
Skonfiguruj próbkę podstawową i test porównawczy:
I przeprowadź testy porównawcze:
W razie czego:
Aktualizacja
W przypadku wektora numerycznego funkcja jest nieco inna:
źródło
To zadziałało dla mnie:
prędkość też jest rozsądna:
źródło
replace_na_with_last(c(NA,1:4,NA))
(tj. są wypełnione następującą wartością). Jest to również domyślne zachowanieimputeTS::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]
Wypróbuj tę funkcję. Nie wymaga pakietu ZOO:
Przykład:
źródło
if (!anyNA(x)) return(x)
.Prowadzenie
NA
jest trochę zmarszczone, ale uważam, że bardzo czytelny (i wektoryzowany) sposób wykonywania LOCF, gdy nie brakuje wiodącego terminu, to:na.omit(y)[cumsum(!is.na(y))]
Nieco mniej czytelna modyfikacja działa ogólnie:
c(NA, na.omit(y))[cumsum(!is.na(y))+1]
daje żądaną wydajność:
c(NA, 2, 2, 2, 2, 3, 3, 4, 4, 4)
źródło
Możesz skorzystać z
data.table
funkcjinafill
dostępnej pod adresemdata.table >= 1.12.3
.Jeśli twój wektor jest kolumną w a
data.table
, możesz go również zaktualizować przez odniesieniesetnafill
:Jeśli masz
NA
w kilku kolumnach ...... możesz wypełnić je przez odniesienie za jednym razem:
Zauważ, że:
Funkcjonalność najprawdopodobniej wkrótce zostanie rozszerzona; zobacz otwarty problem nafill, setnafill dla znaków, współczynników i innych typów , gdzie również znajdziesz tymczasowe obejście .
źródło
Pakiet tidyverse proponuje prosty sposób, aby to zrobić:
źródło
Istnieje kilka pakietów oferujących funkcje
na.locf
(NA
Last Observation Carried Forward):xts
-xts::na.locf
zoo
-zoo::na.locf
imputeTS
-imputeTS::na.locf
spacetime
-spacetime::na.locf
A także inne pakiety, w których ta funkcja ma inną nazwę.
źródło
Kontynuacja wkładu Brandona Bertelsena w Rcpp. Dla mnie wersja NumericVector nie działała: zastąpiła tylko pierwszą NA. To dlatego, że
ina
wektor jest obliczany tylko raz, na początku funkcji.Zamiast tego można zastosować dokładnie to samo podejście, co w przypadku funkcji IntegerVector. Pracowały dla mnie:
Jeśli potrzebujesz wersji CharacterVector, działa również to samo podstawowe podejście:
źródło
Oto modyfikacja rozwiązania @ AdamO. Ten działa szybciej, ponieważ omija
na.omit
funkcję. Spowoduje to nadpisanieNA
wartości w wektorzey
(z wyjątkiem wiodącychNA
).źródło
Spróbowałem poniżej:
nullIdx pobiera numer idx, gdzie kiedykolwiek masterData $ RequiredColumn ma wartość Null / NA. W następnym wierszu zastępujemy go odpowiednią wartością Idx-1, czyli ostatnią dobrą wartością przed każdym NULL / NA
źródło
1 NA NA
zamienia się w1 1 NA
. Myślę też, żeas.array()
jest to niepotrzebne.U mnie to zadziałało, chociaż nie jestem pewien, czy jest bardziej wydajne niż inne sugestie.
źródło
Reduce to fajna funkcjonalna koncepcja programowania, która może być przydatna w podobnych zadaniach. Niestety w R jest ~ 70 razy wolniej niż
repeat.before
w powyższej odpowiedzi.źródło
Osobiście korzystam z tej funkcji. Nie wiem, jak szybko czy wolno. Ale wykonuje swoją pracę bez konieczności korzystania z bibliotek.
jeśli chcesz zastosować tę funkcję w ramce danych, jeśli twoja ramka danych nazywa się df, to po prostu
źródło