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 ?

1voto

J'utilise personnellement cette fonction. Je ne sais pas si elle est rapide ou lente. Mais elle fait son travail sans avoir à utiliser de bibliothèques.

replace_na_with_previous<-function (vector) {
        if (is.na(vector[1])) 
            vector[1] <- na.omit(vector)[1]
        for (i in 1:length(vector)) {
            if ((i - 1) > 0) {
                if (is.na(vector[i])) 
                    vector[i] <- vector[i - 1]
            }
        }
        return(vector)
    }

si vous voulez appliquer cette fonction dans un cadre de données, si votre cadre de données s'appelle df alors il suffit de

df[]<-lapply(df,replace_na_with_previous)

1voto

Ni-Ar Points 129

Je publie ce message ici car il pourrait être utile à d'autres personnes ayant des problèmes similaires à la question posée.

Le plus récent tidyverse en utilisant la vctrs peut être compilé avec mutate pour créer une nouvelle colonne

library(dplyr)
library(magrittr)
library(vctrs)

as.data.frame(y) %>%
  mutate(y_filled = vec_fill_missing(y, direction = c("down")) )

Renvoie à

   y  y_filled
1  NA       NA
2   2        2
3   2        2
4  NA        2
5  NA        2
6   3        3
7  NA        3
8   4        4
9  NA        4
10 NA        4

Tout en changeant le "sens de remplissage" en 'up' résulte en :

    y  y_filled
1  NA        2
2   2        2
3   2        2
4  NA        3
5  NA        3
6   3        3
7  NA        4
8   4        4
9  NA       NA
10 NA       NA

Vous pouvez aussi essayer "downup" o "updown"

Veuillez noter que cette solution est encore en phase d'expérimentation et que la syntaxe peut changer.

0voto

Abhishek Lahiri Points 93

J'ai essayé ce qui suit :

nullIdx <- as.array(which(is.na(masterData$RequiredColumn)))
masterData$RequiredColumn[nullIdx] = masterData$RequiredColumn[nullIdx-1]

nullIdx obtient le numéro d'identification lorsque masterData$RequiredColumn a une valeur Null/ NA. Dans la ligne suivante, nous le remplaçons par la valeur Idx-1 correspondante, c'est-à-dire la dernière bonne valeur avant chaque NULL/ NA.

0voto

dmca Points 633

Cela a fonctionné pour moi, bien que je ne sois pas sûr que ce soit plus efficace que d'autres suggestions.

rollForward <- function(x){
  curr <- 0
  for (i in 1:length(x)){
    if (is.na(x[i])){
      x[i] <- curr
    }
    else{
      curr <- x[i]
    }
  }
  return(x)
}

0voto

unmark1 Points 282

Trop tard pour la fête, mais une réponse très concise et extensible à utiliser avec library(data.table) et donc utilisable comme dt[,SomeVariable:= FunctionBellow, by = list(group)] .

library(imputeTS)
y <- c(NA, 2, 2, NA, NA, 3, NA, 4, NA, NA)
y
[1] NA  2  2 NA NA  3 NA  4 NA NA
imputeTS::na_locf(imputeTS::na_locf(y,option = "nocb"),option="locf")
[1] 2 2 2 3 3 3 4 4 4 4

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