Question Comment remplacer les valeurs NA par des zéros dans une image R?


j'ai un data.frame et certaines colonnes ont NA valeurs. Je veux remplacer le NAs avec des zéros. Comment je fais ça?


522
2017-11-17 03:45


origine


Réponses:


Voir mon commentaire dans la réponse @ gsk3. Un exemple simple:

> m <- matrix(sample(c(NA, 1:10), 100, replace = TRUE), 10)
> d <- as.data.frame(m)
   V1 V2 V3 V4 V5 V6 V7 V8 V9 V10
1   4  3 NA  3  7  6  6 10  6   5
2   9  8  9  5 10 NA  2  1  7   2
3   1  1  6  3  6 NA  1  4  1   6
4  NA  4 NA  7 10  2 NA  4  1   8
5   1  2  4 NA  2  6  2  6  7   4
6  NA  3 NA NA 10  2  1 10  8   4
7   4  4  9 10  9  8  9  4 10  NA
8   5  8  3  2  1  4  5  9  4   7
9   3  9 10  1  9  9 10  5  3   3
10  4  2  2  5 NA  9  7  2  5   5

> d[is.na(d)] <- 0

> d
   V1 V2 V3 V4 V5 V6 V7 V8 V9 V10
1   4  3  0  3  7  6  6 10  6   5
2   9  8  9  5 10  0  2  1  7   2
3   1  1  6  3  6  0  1  4  1   6
4   0  4  0  7 10  2  0  4  1   8
5   1  2  4  0  2  6  2  6  7   4
6   0  3  0  0 10  2  1 10  8   4
7   4  4  9 10  9  8  9  4 10   0
8   5  8  3  2  1  4  5  9  4   7
9   3  9 10  1  9  9 10  5  3   3
10  4  2  2  5  0  9  7  2  5   5

Il n'y a pas besoin de postuler apply. =)

MODIFIER

Vous devriez également jeter un coup d'œil à norm paquet. Il a beaucoup de fonctionnalités intéressantes pour l'analyse des données manquantes. =)


637
2017-11-17 11:48



L'hybride dplyr / Base R option: mutate_all(funs(replace(., is.na(.), 0)))) est plus de deux fois plus rapide que la base R d[is.na(d)] <- 0 option. (Veuillez consulter les analyses de référence ci-dessous.)

Si vous êtes aux prises avec des données massives, data.table est l'option la plus rapide: 30% de temps en moins dplyr, et 3 fois plus vite que Base R approches. Il modifie également les données en place, ce qui vous permet de travailler avec près de deux fois plus de données à la fois.


Un regroupement d'autres approches utiles de remplacement indirect

Emplacement: 

  • indice  mutate_at(c(5:10), funs(replace(., is.na(.), 0))) 
  • référence directe  mutate_at(vars(var5:var10), funs(replace(., is.na(.), 0))) 
  • match fixe  mutate_at(vars(contains("1")), funs(replace(., is.na(.), 0)))
    • ou à la place de contains(), essayez ends_with(),starts_with()
  • correspondance de motif  mutate_at(vars(matches("\\d{2}")), funs(replace(., is.na(.), 0)))

Conditionnellement:
(changez juste numeric (colonnes) et laissez la chaîne (colonnes) seule.)

  • entiers  mutate_if(is.integer, funs(replace(., is.na(.), 0))) 
  • double  mutate_if(is.numeric, funs(replace(., is.na(.), 0))) 
  • cordes  mutate_if(is.character, funs(replace(., is.na(.), 0))) 

L'analyse complète -

Approches testées:

# Base R: 
baseR.sbst.rssgn   <- function(x) { x[is.na(x)] <- 0; x }
baseR.replace      <- function(x) { replace(x, is.na(x), 0) }
baseR.for          <- function(x) { for(j in 1:ncol(x))
                                    x[[j]][is.na(x[[j]])] = 0 }
# tidyverse
## dplyr
library(tidyverse)
dplyr_if_else      <- function(x) { mutate_all(x, funs(if_else(is.na(.), 0, .))) }
dplyr_coalesce     <- function(x) { mutate_all(x, funs(coalesce(., 0))) }

## tidyr
tidyr_replace_na   <- function(x) { replace_na(x, as.list(setNames(rep(0, 10), as.list(c(paste0("var", 1:10)))))) }

## hybrid 
hybrd.ifelse     <- function(x) { mutate_all(x, funs(ifelse(is.na(.), 0, .))) }
hybrd.rplc_all   <- function(x) { mutate_all(x, funs(replace(., is.na(.), 0))) }
hybrd.rplc_at.idx<- function(x) { mutate_at(x, c(1:10), funs(replace(., is.na(.), 0))) }
hybrd.rplc_at.nse<- function(x) { mutate_at(x, vars(var1:var10), funs(replace(., is.na(.), 0))) }
hybrd.rplc_at.stw<- function(x) { mutate_at(x, vars(starts_with("var")), funs(replace(., is.na(.), 0))) }
hybrd.rplc_at.ctn<- function(x) { mutate_at(x, vars(contains("var")), funs(replace(., is.na(.), 0))) }
hybrd.rplc_at.mtc<- function(x) { mutate_at(x, vars(matches("\\d+")), funs(replace(., is.na(.), 0))) }
hybrd.rplc_if    <- function(x) { mutate_if(x, is.numeric, funs(replace(., is.na(.), 0))) }

# data.table   
library(data.table)
DT.for.set.nms   <- function(x) { for (j in names(x))
                                    set(x,which(is.na(x[[j]])),j,0) }
DT.for.set.sqln  <- function(x) { for (j in seq_len(ncol(x)))
                                    set(x,which(is.na(x[[j]])),j,0) }

Le code pour cette analyse:

library(microbenchmark)
# 20% NA filled dataframe of 5 Million rows and 10 columns
set.seed(42) # to recreate the exact dataframe
dfN <- as.data.frame(matrix(sample(c(NA, as.numeric(1:4)), 5e6*10, replace = TRUE),
                            dimnames = list(NULL, paste0("var", 1:10)), 
                            ncol = 10))
# Running 250 trials with each replacement method 
# (the functions are excecuted locally - so that the original dataframe remains unmodified in all cases)
perf_results <- microbenchmark(
    hybrid.ifelse    = hybrid.ifelse(copy(dfN)),
    dplyr_if_else    = dplyr_if_else(copy(dfN)),
    baseR.sbst.rssgn = baseR.sbst.rssgn(copy(dfN)),
    baseR.replace    = baseR.replace(copy(dfN)),
    dplyr_coalesce   = dplyr_coalesce(copy(dfN)),
    hybrd.rplc_at.nse= hybrd.rplc_at.nse(copy(dfN)),
    hybrd.rplc_at.stw= hybrd.rplc_at.stw(copy(dfN)),
    hybrd.rplc_at.ctn= hybrd.rplc_at.ctn(copy(dfN)),
    hybrd.rplc_at.mtc= hybrd.rplc_at.mtc(copy(dfN)),
    hybrd.rplc_at.idx= hybrd.rplc_at.idx(copy(dfN)),
    hybrd.rplc_if    = hybrd.rplc_if(copy(dfN)),
    tidyr_replace_na = tidyr_replace_na(copy(dfN)),
    baseR.for        = baseR.for(copy(dfN)),
    DT.for.set.nms   = DT.for.set.nms(copy(dfN)),
    DT.for.set.sqln  = DT.for.set.sqln(copy(dfN)),
    times = 250L
)

Résumé des résultats

> perf_results
Unit: milliseconds
              expr       min        lq      mean    median        uq      max neval
     hybrid.ifelse 5250.5259 5620.8650 5809.1808 5759.3997 5947.7942 6732.791   250
     dplyr_if_else 3209.7406 3518.0314 3653.0317 3620.2955 3746.0293 4390.888   250
  baseR.sbst.rssgn 1611.9227 1878.7401 1964.6385 1942.8873 2031.5681 2485.843   250
     baseR.replace 1559.1494 1874.7377 1946.2971 1920.8077 2002.4825 2516.525   250
    dplyr_coalesce  949.7511 1231.5150 1279.3015 1288.3425 1345.8662 1624.186   250
 hybrd.rplc_at.nse  735.9949  871.1693 1016.5910 1064.5761 1104.9590 1361.868   250
 hybrd.rplc_at.stw  704.4045  887.4796 1017.9110 1063.8001 1106.7748 1338.557   250
 hybrd.rplc_at.ctn  723.9838  878.6088 1017.9983 1063.0406 1110.0857 1296.024   250
 hybrd.rplc_at.mtc  686.2045  885.8028 1013.8293 1061.2727 1105.7117 1269.949   250
 hybrd.rplc_at.idx  696.3159  880.7800 1003.6186 1038.8271 1083.1932 1309.635   250
     hybrd.rplc_if  705.9907  889.7381 1000.0113 1036.3963 1083.3728 1338.190   250
  tidyr_replace_na  680.4478  973.1395  978.2678 1003.9797 1051.2624 1294.376   250
         baseR.for  670.7897  965.6312  983.5775 1001.5229 1052.5946 1206.023   250
    DT.for.set.nms  496.8031  569.7471  695.4339  623.1086  861.1918 1067.640   250
   DT.for.set.sqln  500.9945  567.2522  671.4158  623.1454  764.9744 1033.463   250

Boxplot des résultats (sur une échelle logarithmique)

# adjust the margins to prepare for better boxplot printing
par(mar=c(8,5,1,1) + 0.1) 
# generate boxplot
boxplot(opN, las = 2, xlab = "", ylab = "log(time)[milliseconds]")

Boxplot Comparison of Elapsed Time

Carte de dispersion à code couleur des essais (sur une échelle logarithmique)

qplot(y=time/10^9, data=opN, colour=expr) + 
    labs(y = "log10 Scaled Elapsed Time per Trial (secs)", x = "Trial Number") +
    scale_y_log10(breaks=c(1, 2, 4))

Scatterplot of All Trial Times

Une note sur les autres performants

Lorsque les ensembles de données deviennent plus grands, Tidyr's replace_na avait historiquement retiré en avant. Avec la collection actuelle de 50 millions de points de données à parcourir, il fonctionne presque aussi bien qu'un Base R Pour la boucle. Je suis curieux de voir ce qui se passe pour différentes tailles de données.

Des exemples supplémentaires pour le mutate et summarize  _at et _all les variantes de fonction peuvent être trouvées ici: https://rdrr.io/cran/dplyr/man/summarise_all.html De plus, j'ai trouvé des démonstrations utiles et des collections d'exemples ici: https://blog.exploratory.io/dplyr-0-5-is-awesome-heres-why-be095fd4eb8a

Attributions et appréciations

Avec des remerciements spéciaux à:

  • Tyler Rinker et Akrun pour démontrer un microbenchmark.
  • alexis_laz pour travailler sur m'aider à comprendre l'utilisation de local()et (avec l'aide patiente de Frank, aussi) le rôle que la coercition silencieuse joue en accélérant beaucoup de ces approches.
  • ArthurYip pour le poke pour ajouter le plus récent coalesce() fonctionner dans et mettre à jour l'analyse.
  • Gregor pour le coup de pouce pour comprendre le data.table fonctionne assez bien pour finalement les inclure dans la gamme.
  • Base R Pour la boucle: alexis_laz
  • data.table Pour les boucles: Matt_Dowle

(Bien sûr, veuillez les joindre et leur donner des votes positifs si vous trouvez ces approches utiles.)

Note sur mon utilisation de Numerics:  Si vous avez un jeu de données entier pur, toutes vos fonctions seront plus rapides. S'il te plait regarde Le travail de alexiz_laz pour plus d'informations. IRL, je ne me souviens pas d'avoir rencontré un ensemble de données contenant plus de 10-15% d'entiers, donc j'exécute ces tests sur des bases de données entièrement numériques.


123
2018-01-11 08:10



Pour un seul vecteur:

x <- c(1,2,NA,4,5)
x[is.na(x)] <- 0

Pour un data.frame, faites une fonction parmi celles ci-dessus, puis apply aux colonnes.

Veuillez fournir un exemple reproductible la prochaine fois comme détaillé ici:

Comment faire un grand exemple R reproductible?


102
2017-11-17 03:50



Exemple dplyr:

library(dplyr)

df1 <- df1 %>%
    mutate(myCol1 = if_else(is.na(myCol1), 0, myCol1))

Remarque: Cela fonctionne par colonne sélectionnée, si nous devons faire cela pour toutes les colonnes, voir @reidjaxLa réponse en utilisant mutate_each.


60
2018-05-08 16:15



Si nous essayons de remplacer NAs lors de l'exportation, par exemple lors de l'écriture sur csv, nous pouvons utiliser:

  write.csv(data, "data.csv", na = "0")

42
2018-02-21 16:27



Je sais que la question a déjà reçu une réponse, mais cela pourrait être plus utile pour certains:

Définir cette fonction:

na.zero <- function (x) {
    x[is.na(x)] <- 0
    return(x)
}

Maintenant, chaque fois que vous avez besoin de convertir les NA dans un vecteur à zéro, vous pouvez faire:

na.zero(some.vector)

40
2017-09-24 13:49



Approche plus générale de l'utilisation replace() dans la matrice ou le vecteur pour remplacer NA à 0

Par exemple:

> x <- c(1,2,NA,NA,1,1)
> x1 <- replace(x,is.na(x),0)
> x1
[1] 1 2 0 0 1 1

C'est aussi une alternative à l'utilisation ifelse() dans dplyr

df = data.frame(col = c(1,2,NA,NA,1,1))
df <- df %>%
   mutate(col = replace(col,is.na(col),0))

18
2018-02-25 04:30



Avec dplyr 0.5.0, vous pouvez utiliser coalesce fonction qui peut être facilement intégrée dans %>% pipeline en faisant coalesce(vec, 0). Cela remplace toutes les AN dans vec avec 0:

Disons que nous avons un cadre de données avec NAs:

library(dplyr)
df <- data.frame(v = c(1, 2, 3, NA, 5, 6, 8))

df
#    v
# 1  1
# 2  2
# 3  3
# 4 NA
# 5  5
# 6  6
# 7  8

df %>% mutate(v = coalesce(v, 0))
#   v
# 1 1
# 2 2
# 3 3
# 4 0
# 5 5
# 6 6
# 7 8

16
2017-09-16 21:25