Em um data.frame (ou data.table), eu gostaria de "preencher" NAs com o valor anterior não NA mais próximo. Um exemplo simples, usando vetores (em vez de a data.frame
) é o seguinte:
> y <- c(NA, 2, 2, NA, NA, 3, NA, 4, NA, NA)
Eu gostaria de uma função fill.NAs()
que me permita construir yy
tal que:
> yy
[1] NA NA NA 2 2 2 2 3 3 3 4 4
Preciso repetir esta operação para muitos s (total ~ 1 Tb) de tamanho pequeno data.frame
(~ 30-50 Mb), em que uma linha é NA são todas as entradas. Qual é uma boa maneira de abordar o problema?
A solução feia que eu criei usa esta função:
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
}
A função fill.NAs
é usada da seguinte maneira:
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]
}
Resultado
> y
[1] NA 2 2 2 2 3 3 3 4 4 4
... o que parece funcionar. Mas, cara, é feio! Alguma sugestão?
r
data.table
zoo
r-faq
Ryogi
fonte
fonte
roll=TRUE
emdata.table
.fill
emR
tidyr::fill()
.Respostas:
Você provavelmente deseja usar a
na.locf()
função do pacote zoo para levar a última observação adiante para substituir seus valores de NA.Aqui está o início de seu exemplo de uso na página de ajuda:
fonte
na.locf
no zoo funciona com vetores comuns e objetos do zoo. Seuna.rm
argumento pode ser útil em algumas aplicações.na.locf(cz, na.rm=FALSE)
para manter a liderançaNA
.Desculpe por desenterrar uma pergunta antiga. Eu não conseguia procurar a função para fazer esse trabalho no trem, então escrevi uma para mim.
Fiquei orgulhoso de descobrir que é um pouco mais rápido.
É menos flexível.
Mas funciona bem
ave
, e é disso que eu precisava.Editar
Como essa se tornou minha resposta mais votada, lembrei-me com frequência de que não uso minha própria função, porque muitas vezes preciso do
maxgap
argumento do zoológico . Como o zoo tem alguns problemas estranhos em casos extremos quando eu uso datas dplyr + que não pude depurar, voltei a isso hoje para melhorar minha função antiga.Comparei minha função aprimorada e todas as outras entradas aqui. Para o conjunto básico de recursos,
tidyr::fill
é mais rápido e também não falha nos casos extremos. A entrada Rcpp de @BrandonBertelsen ainda é mais rápida, mas é inflexível em relação ao tipo de entrada (ele testou casos de borda incorretamente devido a um mal-entendidoall.equal
).Se você precisar
maxgap
, minha função abaixo é mais rápida que o zoo (e não tem problemas estranhos com datas).Eu coloquei a documentação dos meus testes .
nova função
Eu também coloquei a função no meu pacote formr (somente Github).
fonte
df
com várias colunas?na.locf0
que agora é semelhante em escopo e desempenho à suarepeat_last
função. A pista era usardiff
e nãocumsum
evitarifelse
. A principalna.locf.default
função ainda é um pouco mais lento porque ele faz mais algumas verificações e alças múltiplas colunas etc.uma
data.table
solução:essa abordagem também pode funcionar com zeros de preenchimento à frente:
esse método se torna muito útil em dados em escala e onde você deseja executar um preenchimento direto por grupo (s), o que é trivial
data.table
. basta adicionar o (s) grupo (s) àby
cláusula anterior àcumsum
lógica.fonte
Lidando com um grande volume de dados, para ser mais eficiente, podemos usar o pacote data.table.
fonte
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) }
Jogando meu chapéu em:
Configure uma amostra básica e uma referência:
E execute alguns benchmarks:
Apenas no caso de:
Atualizar
Para um vetor numérico, a função é um pouco diferente:
fonte
Isso tem funcionado para mim:
a velocidade também é razoável:
fonte
replace_na_with_last(c(NA,1:4,NA))
(ou seja, eles são preenchidos com o seguinte valor). Esse também é o comportamento padrão deimputeTS::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]
Tente esta função. Não requer o pacote ZOO:
Exemplo:
fonte
if (!anyNA(x)) return(x)
.Ter uma liderança
NA
é um pouco complicado, mas acho uma maneira muito legível (e vetorizada) de executar o LOCF quando o termo principal não está faltando é:na.omit(y)[cumsum(!is.na(y))]
Uma modificação um pouco menos legível funciona em geral:
c(NA, na.omit(y))[cumsum(!is.na(y))+1]
fornece a saída desejada:
c(NA, 2, 2, 2, 2, 3, 3, 4, 4, 4)
fonte
Você pode usar a
data.table
funçãonafill
, disponível emdata.table >= 1.12.3
.Se seu vetor é uma coluna em a
data.table
, você também pode atualizá-lo por referência comsetnafill
:Se você tem
NA
em várias colunas ...... você pode preenchê-los por referência de uma só vez:
Observe que:
A funcionalidade provavelmente será estendida em breve; consulte o nafill de edição aberta, o conjunto de caracteres, fator e outros tipos , onde você também encontra uma solução temporária .
fonte
O pacote tidyverse propõe uma maneira simples de fazer isso:
fonte
Existem vários pacotes que oferecem funções
na.locf
(NA
Última Observação Realizada):xts
-xts::na.locf
zoo
-zoo::na.locf
imputeTS
-imputeTS::na.locf
spacetime
-spacetime::na.locf
E também outros pacotes em que essa função tem um nome diferente.
fonte
Acompanhamento das contribuições de Brandon Bertelsen ao Rcpp. Para mim, a versão do NumericVector não funcionou: substituiu apenas o primeiro NA. Isso ocorre porque o
ina
vetor é avaliado apenas uma vez, no início da função.Em vez disso, pode-se adotar exatamente a mesma abordagem da função IntegerVector. O seguinte funcionou para mim:
Caso você precise de uma versão do CharacterVector, a mesma abordagem básica também funcionará:
fonte
Aqui está uma modificação da solução do @ AdamO. Este funciona mais rápido, porque ignora a
na.omit
função. Isso substituirá osNA
valores no vetory
(exceto os iniciaisNA
).fonte
Eu tentei o abaixo:
nullIdx obtém o número idx onde sempre masterData $ RequiredColumn tem um valor Null / NA. Na próxima linha, substituí-lo pelo valor Idx-1 correspondente, ou seja, o último valor bom antes de cada NULL / NA
fonte
1 NA NA
se transforma1 1 NA
. Além disso, acho que issoas.array()
é desnecessário.Isso funcionou para mim, embora não tenha certeza se é mais eficiente do que outras sugestões.
fonte
Reduzir é um bom conceito de programação funcional que pode ser útil para tarefas semelhantes. Infelizmente em R é ~ 70 vezes mais lento que
repeat.before
na resposta acima.fonte
Eu pessoalmente uso essa função. Eu não sei o quão rápido ou lento é. Mas ele faz seu trabalho sem precisar usar bibliotecas.
se você deseja aplicar esta função em um quadro de dados, se o seu quadro de dados for chamado df, simplesmente
fonte