Pregunta ¿Cómo reemplazo los valores de NA con ceros en un marco de datos R?


tengo un data.frame y algunas columnas tienen NA valores. Quiero reemplazar el NAs con ceros. ¿Cómo hago esto?


522
2017-11-17 03:45


origen


Respuestas:


Ver mi comentario en @ gsk3 respuesta. Un simple ejemplo:

> 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

No hay necesidad de aplicar apply. =)

EDITAR

También debería echar un vistazo a norm paquete. Tiene muchas características agradables para el análisis de datos faltantes. =)


637
2017-11-17 11:48



El hibrido dplyr / Base R opción: mutate_all(funs(replace(., is.na(.), 0)))) es más de dos veces más rápido que la base R d[is.na(d)] <- 0 opción. (Consulte los análisis de referencia a continuación).

Si estás luchando con marcos de datos masivos, data.table es la opción más rápida de todas: 30% menos de tiempo que dplyry 3 veces más rápido que el Base R enfoques. También modifica los datos en su lugar, lo que le permite trabajar con casi el doble de la información a la vez.


Una agrupación de otros enfoques útiles de reemplazo tidyverse

Locationally: 

  • índice  mutate_at(c(5:10), funs(replace(., is.na(.), 0))) 
  • referencia directa  mutate_at(vars(var5:var10), funs(replace(., is.na(.), 0))) 
  • arreglo  mutate_at(vars(contains("1")), funs(replace(., is.na(.), 0)))
    • o en lugar de contains(), tratar ends_with(),starts_with()
  • coincidencia de patrones  mutate_at(vars(matches("\\d{2}")), funs(replace(., is.na(.), 0)))

Condicionalmente:
(cambie solo numérico (columnas) y deje cadena (columnas) solo).

  • enteros  mutate_if(is.integer, funs(replace(., is.na(.), 0))) 
  • dobles  mutate_if(is.numeric, funs(replace(., is.na(.), 0))) 
  • instrumentos de cuerda  mutate_if(is.character, funs(replace(., is.na(.), 0))) 

El análisis completo -

Enfoques probados:

# 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) }

El código para este análisis:

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
)

Resumen de Resultados

> 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

Cuadro de caja de resultados (en una escala de registro)

# 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

Diagrama de dispersión codificado por colores de los ensayos (en una escala logarítmica)

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

Una nota sobre los otros de alto rendimiento

Cuando los conjuntos de datos crecen, Tidyr'' s replace_na históricamente se había retirado al frente. Con la recopilación actual de puntos de datos de 50M, se ejecuta casi exactamente tan bien como Base R En bucle. Tengo curiosidad por ver qué sucede con los dataframes de diferentes tamaños.

Ejemplos adicionales para el mutate y summarize  _at y _all las variantes de función se pueden encontrar aquí: https://rdrr.io/cran/dplyr/man/summarise_all.html Además, encontré demostraciones útiles y colecciones de ejemplos aquí: https://blog.exploratory.io/dplyr-0-5-is-awesome-heres-why-be095fd4eb8a

Atribuciones y apreciaciones

Con un agradecimiento especial a:

  • Tyler Rinker y Akrun para demostrar microbenchmark.
  • alexis_laz para trabajar en ayudarme a entender el uso de local()y (con la ayuda paciente de Frank también) el papel que desempeña la coacción silenciosa para acelerar muchos de estos enfoques.
  • ArthurYip para el poke para agregar el más nuevo coalesce() funcionar y actualizar el análisis.
  • Gregor por el empujoncito para descubrir el data.table funciona lo suficientemente bien como para finalmente incluirlos en la alineación.
  • Base R En bucle: alexis_laz
  • tabla de datos Para bucles: Matt_Dowle

(Por supuesto, comuníquese con ellos y ofrézcales votaciones, también, si considera que estos enfoques son útiles).

Nota sobre mi uso de Numerics:  Si tiene un conjunto de datos entero puro, todas sus funciones se ejecutarán más rápido. Por favor mira El trabajo de alexiz_laz para más información. IRL, no recuerdo haber encontrado un conjunto de datos que contenga más del 10-15% de enteros, por lo que estoy ejecutando estas pruebas en marcos de datos totalmente numéricos.


123
2018-01-11 08:10



Para un solo vector:

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

Para un data.frame, haga una función de lo anterior, luego apply a las columnas

Proporcione un ejemplo reproducible la próxima vez como se detalla aquí:

¿Cómo hacer un gran ejemplo reproducible de R?


102
2017-11-17 03:50



dplyr ejemplo:

library(dplyr)

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

Nota: Esto funciona por columna seleccionada, si necesitamos hacer esto para todas las columnas, vea @reidjaxLa respuesta de usando mutate_each.


60
2018-05-08 16:15



Si estamos tratando de reemplazar NAAl exportar, por ejemplo, al escribir en csv, podemos usar:

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

42
2018-02-21 16:27



Sé que la pregunta ya está respondida, pero hacerlo de esta manera podría ser más útil para algunos:

Definir esta función:

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

Ahora, cada vez que necesite convertir NA en un vector a cero, puede hacer:

na.zero(some.vector)

40
2017-09-24 13:49



Un enfoque más general de uso replace() en matriz o vector para reemplazar NA a 0

Por ejemplo:

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

Esta es también una alternativa al uso ifelse() en 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



Con dplyr 0.5.0, puedes usar coalesce función que puede integrarse fácilmente en %>% tubería haciendo coalesce(vec, 0). Esto reemplaza todas las NA en vec con 0:

Digamos que tenemos un marco de datos con 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



Otro ejemplo usando imputeTS paquete:

library(imputeTS)
na.replace(yourDataframe, 0)

8
2017-11-10 18:21



Si desea reemplazar NA en variables de factores, esto podría ser útil:

n <- length(levels(data.vector))+1

data.vector <- as.numeric(data.vector)
data.vector[is.na(data.vector)] <- n
data.vector <- as.factor(data.vector)
levels(data.vector) <- c("level1","level2",...,"leveln", "NAlevel") 

Transforma un factor-vector en un vector numérico y agrega otro nivel de factor numérico artificial, que luego se transforma de nuevo en un factor-vector con un "nivel de NA" adicional de su elección.


8
2018-03-17 08:55