1
votes

Remplacez NA par la valeur du groupe en boucle sur plusieurs colonnes

Je veux remplacer NA par la valeur du groupe (pas la moyenne ou la médiane, car certaines colonnes sont des caractères ou des facteurs) et je veux faire cela pour une liste de colonnes utilisant le même groupe pour tous.

Exemple de données:

mydf[,-c(1] <- lapply(mydf, function(x) 
  replace(x, is.na(x), x[!is.na(x)]))

> mydf
  ID V1  V2   V3 V4
1  1  1 HEJ  700  3
2  1  1 HEJ  700  1
3  1  1 BOS  700  3
4  2  2 BOS 3000  1
5  2  2 BOS  700  1
6  2  2 BOS 3000  1
7  3  3  IB 1000  2
8  3  3  IB 1000  1

Donc, je sais que si je ne voulais faire cela que pour une colonne, je:

names <- colnames(mydf[,-c(1)])

for(j in seq_along(nm1)){

   set(mydf,
       i = which(is.na(mydf[[names[j]]])),
       j = names[j],
       value = mydf[[names[j]]][is.na(mydf[[names[j]]])])
}

Et puis j'obtiendrais:

> mydf
   ID  V1   V2 V3   V4
1:  1 HEJ  700  3 <NA>
2:  1 HEJ  700  3 <NA>
3:  1 HEJ  700  3 <NA>
4:  2 BOS 3000  1   LA
5:  2 BOS 3000  1   LA
6:  2 BOS 3000  1   LA
7:  3  IB 1000  2   FE
8:  3  IB 1000  2   FE

Mais j'ai beaucoup de colonnes, donc je dois les mettre dans une boucle.

Résultat souhaité:

> mydf
   ID  V1   V2 V3   V4
1:  1 HEJ  700 NA <NA>
2:  1 HEJ  700 NA <NA>
3:  1 HEJ   NA  3 <NA>
4:  2 BOS 3000  1 <NA>
5:  2 BOS   NA  1 <NA>
6:  2 BOS   NA  1   LA
7:  3  IB   NA  2 <NA>
8:  3  IB 1000 NA   FE

Et voici ce que j'ai essayé sans chance:

setDT(mydf)[, V1:= 
                V1[!is.na(V1)][1L],
                by = ID]

Et j'ai aussi essayé ceci:

ID <- c(1,1,1,2,2,2,3,3)
V1 <- c(NA,"HEJ",NA,"BOS","BOS",NA,"IB","IB")
V2 <- c(700,700,NA,3000,NA,NA,NA,1000)
V3 <- c(NA,NA,3,1,1,1,2,NA)
V4 <- c(NA,NA,NA,NA,NA,"LA",NA,"FE")
mydf <- data.frame(ID,V1,V2,V3,V4)

> mydf
  ID   V1   V2 V3   V4
1  1 <NA>  700 NA <NA>
2  1  HEJ  700 NA <NA>
3  1 <NA>   NA  3 <NA>
4  2  BOS 3000  1 <NA>
5  2  BOS   NA  1 <NA>
6  2 <NA>   NA  1   LA
7  3   IB   NA  2 <NA>
8  3   IB 1000 NA   FE

Je suis désolé si je pose une question qui existe déjà mais je n'ai pas pu la trouver. J'espère que quelqu'un pourra m'aider à nettoyer mes données désordonnées :)


0 commentaires

3 Réponses :


1
votes

Une possibilité de dplyr et tidyr pourrait être:

mydf %>%
 group_by(ID) %>%
 fill(-ID, .direction = "downup")

     ID V1       V2    V3 V4   
  <dbl> <fct> <dbl> <dbl> <fct>
1     1 HEJ     700     3 <NA> 
2     1 HEJ     700     3 <NA> 
3     1 HEJ     700     3 <NA> 
4     2 BOS    3000     1 LA   
5     2 BOS    3000     1 LA   
6     2 BOS    3000     1 LA   
7     3 IB     1000     2 FE   
8     3 IB     1000     2 FE 


5 commentaires

Edit: demandé si cela fonctionne si la valeur inférieure est NA, mais je peux voir que mes exemples de données ont déjà un cas de colonne comme celui-ci (V3)


Pouvez-vous s'il vous plaît élaborer cette question? Je ne suis pas sûr de comprendre :)


Cela ne fonctionne pas sur mes exemples de données ... J'obtiens l'erreur "Erreur dans match.arg (.direction): 'arg' devrait être l'un des" down "," up ""


essayé avec le haut et le bas, et aucun des deux ne fournit la sortie souhaitée


Cet argument est relativement nouveau, essayez de mettre à jour vers la dernière version de tidyr .



1
votes

Nous pouvons utiliser .SDcols pour appliquer la fonction à plusieurs colonnes.

library(data.table)

cols <- names(mydf[-1])
setDT(mydf)

mydf[, (cols):= lapply(.SD, function(x) 
         replace(x, is.na(x), x[!is.na(x)][1])),.SDcols = cols, by = ID]
mydf

#   ID  V1   V2 V3   V4
#1:  1 HEJ  700  3 <NA>
#2:  1 HEJ  700  3 <NA>
#3:  1 HEJ  700  3 <NA>
#4:  2 BOS 3000  1   LA
#5:  2 BOS 3000  1   LA
#6:  2 BOS 3000  1   LA
#7:  3  IB 1000  2   FE
#8:  3  IB 1000  2   FE


2 commentaires

Merci, ça marche, mais ça écrase pour ces colonnes sans NA et je ne veux pas ça, donc je pense que pour mon cas, le remplissage fonctionne mieux. Merci quand même. C'était quelque chose comme ça que je cherchais.


@ LouiseSørensen Mise à jour de la réponse pour ne remplacer que les valeurs NA .



0
votes

Nous pouvons utiliser na.locf de zoo

library(data.table)
setDT(df1)[, na.locf(.SD), by = ID, .SDcols = V2:V4]


0 commentaires