210 votes

Remplacement des NA par la dernière valeur non-NA

Dans un data.frame (ou data.table ), j'aimerais "remplir en avant" les NA avec la valeur non-NA précédente la plus proche. Un exemple simple, utilisant des vecteurs (au lieu de data.frame ) est le suivant :

> y <- c(NA, 2, 2, NA, NA, 3, NA, 4, NA, NA)

Je voudrais une fonction fill.NAs() qui me permet de construire yy de telle sorte que :

> yy
[1] NA NA NA  2  2  2  2  3  3  3  4  4

Je dois répéter cette opération pour un grand nombre de petits fichiers (~1 To au total). data.frame (~30-50 Mb), où une ligne est NA si toutes ses entrées le sont. Quelle est la bonne façon d'aborder le problème ?

La solution laide que j'ai élaborée utilise cette fonction :

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
}

La fonction fill.NAs est utilisé comme suit :

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]
} 

Sortie

> y
[1] NA  2  2  2  2  3  3  3  4  4  4

... ce qui semble fonctionner. Mais, bon sang, c'est moche ! Des suggestions ?

8voto

stats0007 Points 770

Il y a un tas de paquets qui offrent na.locf ( NA Fonctions de la dernière observation reportée) :

  • xts - xts::na.locf
  • zoo - zoo::na.locf
  • imputeTS - imputeTS::na.locf
  • spacetime - spacetime::na.locf

Et aussi d'autres paquets où cette fonction est nommée différemment.

3voto

Evan Cortens Points 650

Suivi des contributions de Brandon Bertelsen sur Rcpp. Pour moi, la version NumericVector ne fonctionnait pas : elle ne remplaçait que le premier NA. C'est parce que le ina n'est évalué qu'une seule fois, au début de la fonction.

Au lieu de cela, on peut adopter exactement la même approche que pour la fonction IntegerVector. Ce qui suit a fonctionné pour moi :

library(Rcpp)
cppFunction('NumericVector na_locf_numeric(NumericVector x) {
  R_xlen_t n = x.size();
  for(R_xlen_t i = 0; i<n; i++) {
    if(i > 0 && !R_finite(x[i]) && R_finite(x[i-1])) {
      x[i] = x[i-1];
    }
  }
  return x;
}')

Si vous avez besoin d'une version CharacterVector, la même approche de base fonctionne également :

cppFunction('CharacterVector na_locf_character(CharacterVector x) {
  R_xlen_t n = x.size();
  for(R_xlen_t i = 0; i<n; i++) {
    if(i > 0 && x[i] == NA_STRING && x[i-1] != NA_STRING) {
      x[i] = x[i-1];
    }
  }
  return x;
}')

3voto

Voici une modification de la solution de @AdamO. Celle-ci s'exécute plus rapidement, car elle contourne l'option na.omit fonction. Cela écrasera le NA valeurs dans le vecteur y (à l'exception de l'avance NA s).

   z  <- !is.na(y)                  # indicates the positions of y whose values we do not want to overwrite
   z  <- z | !cumsum(z)             # for leading NA's in y, z will be TRUE, otherwise it will be FALSE where y has a NA and TRUE where y does not have a NA
   y  <- y[z][cumsum(z)]

3voto

polkas Points 1336

Je veux ajouter une solution suivante qui utilise la fonction runner r cran package.

library(runner)
y <- c(NA, 2, 2, NA, NA, 3, NA, 4, NA, NA)
fill_run(y, FALSE)
 [1] NA  2  2  2  2  3  3  4  4  4

L'ensemble est optimisé et la majeure partie a été écrite en cpp. Il offre donc une grande efficacité.

1voto

Valentas Points 31
fill.NAs <- function(x) {is_na<-is.na(x); x[Reduce(function(i,j) if (is_na[j]) i else j, seq_len(length(x)), accumulate=T)]}

fill.NAs(c(NA, 2, 2, NA, NA, 3, NA, 4, NA, NA))

[1] NA  2  2  2  2  3  3  4  4  4

La réduction est un concept de programmation fonctionnelle intéressant qui peut être utile pour des tâches similaires. Malheureusement, dans R, il est ~70 fois plus lent que repeat.before dans la réponse ci-dessus.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X