9 ago 2019

Valores perdidos (NA) en R: identificación y tratamiento (I)


Missing Values (NA) in R. En la siguiente entrada se muestran algunas operaciones básicas para la identificación y tratamiento de valores perdidos en R. Dado el siguiente vector de valores, se verifica que los valores NA son utilizados en R para señalar valore perdidos:

x<-c(24, 14, 17, 25, 12, NA, 11, NA)

Aunque en este ejemplo es fácil de identificar donde se encuentran los NA, en la mayoría de los casos prácticos el volumen de datos no permitirá se identifique con facilidad la presencia de algún valor perdido en el vector de datos. Sin embargo, en R la función is.na permite obtener un vector lógico con TRUE en los casos de valores perdidos. Al anidar la función any con la función is.na se verifica si un vector determinado tiene algún valor perdido, la función which permite identificar la posición de estos valores, mean el porcentaje de NA y sum la cantidad de NA:

> is.na(x)        # Vector lógico con T==NA
[1] FALSE FALSE FALSE FALSE FALSE  TRUE FALSE  TRUE

> any(is.na(x))   # TRUE = hay al menos un valor NA
[1] TRUE

> anyNA(x)        # Alternativa a lo anterior
[1] TRUE

> which(is.na(x)) # Indica las coordenadas donde están los NA
[1] 6 8

> mean(is.na(x))  # Porcentaje de valores NA
[1] 0.25

> sum(is.na(x))   # Cantidad de valores perdidos en el vector
[1] 2

R, también permite omitir los valores perdidos de un vector de datos usando las funciones na.omit y na.exlude:
   
>   na.omit(x)
[1] 24 14 17 25 12 11
attr(,"na.action")
[1] 6 8
attr(,"class")
[1] "omit"

> na.exclude(x)
[1] 24 14 17 25 12 11
attr(,"na.action")
[1] 6 8
attr(,"class")
[1] "exclude"

Adicionalmente, podemos omitir valores NA de un vector mediante indexación lógica:
  
>  x[!is.na(x)]
[1] 24 14 17 25 12 11

> all(na.omit(x) == x[!is.na(x)])
[1] TRUE

La función na.fail imprime un error en caso de encontrar valores perdidos, esta es usada como mensaje de advertencia en caso de condicionales o estructuras como las funciones:
  
> na.fail(x)
Error in na.fail.default(x) : missing values in object

Note que en caso de no existir valores NA, no se imprime el mensaje:

> na.fail(na.omit(x))
[1] 24 14 17 25 12 11
attr(,"na.action")
[1] 6 8
attr(,"class")
[1] "omit"

Otra forma de contabilizar valores NA es mediante la función table, esta nos permite obtener una tabla de frecuencia de nuestros datos:

y <-c(1, 0, 1, 1, 1, 0, NA, 0)

> table(y)
y
0 1 
3 4 

> table(y, useNA = "always")
y
0    1 <NA> 
  3    4    1 

En el caso de bases datos, que son más frecuente que los vectores, podemos también identificar valores NA. Las siguientes líneas de códigos insertan una base de datos llamada data, con los vectores creados anteriormente.

> data <- data.frame(mujer=y, edad=x)
> data
mujer edad
1     1   24
2     0   14
3     1   17
4     1   25
5     1   12
6     0   NA
7    NA   11
8     0   NA

Mediante la función vectorizada apply, podemos obtener, por columna o variable, la cantidad, el porcentaje o la ubicación de los valores lógicos de nuestra base de datos. Esto se logra anidando esta función apply con la función is.na vista anteriormente. Verifique que si cambiamos el 2 por el 1, repite el ejercicio anterior para cada una de las observaciones.

> apply(is.na(data), 2, mean)   # Porcentaje de NA por columna
mujer  edad 
0.125 0.250 

> apply(is.na(data), 2, sum)    # Cantidad de NA por columna
mujer  edad 
1     2 

> apply(is.na(data), 2, which)  # Posición de NA por columna
$mujer
[1] 7

$edad
[1] 6 8

Esta posición se puede verificar de forma directa para cada variable (columna):
  
>  which(is.na(data[ , 2]))
[1] 6 8

Ahora bien, si queremos ambos operadores (seguro si buscamos en internet hay alguna forma más bonita de logarlos, en este caso, lo que hacemos para que la función apply me aplique dos funciones, es crear una nueva función que haga las dos tareas a la vez):
  
  nerysF <-function(x) {
    a<-mean(x);
    b<-sum(x)
    return(c(a,b))
  }

> apply(is.na(data), 2, nerysF)
mujer edad peso
[1,] 0.125 0.25    0
[2,] 1.000 2.00    0

Agreguemos la variable peso a nuestra base de datos, la cual, de manera intencional no contiene valores NA:
  
  data$peso <- c(110, 150, 180, 125, 121, 121, 111, 150)

Identificar variables (columnas) con al menos 1 NA en mi base de datos:

La función apply, usando como argumento la función sum, nos permite verificar cuales variables tienen al menos un valor NA. Como ejemplo, creamos un vector lógico llamado VARna=TRUE en los casos donde la columna tenga al menos un valor NA. Posteriormente usamos la indexación lógica para acceder a los nombres de las columnas identificadas:
  
VARna <- apply(is.na(data), 2, sum)>=1
> VARna
mujer  edad  peso 
TRUE  TRUE FALSE 

> VARna[VARna==TRUE]
mujer  edad 
TRUE  TRUE 

La función complete.case permite obtener la cantidad de observaciones de la data, donde ninguna observación contiene observaciones perdidas. En tal caso, se utiliza el vector lógico complete.cases(data) para poder indexar la data, para poder acceder a estas observaciones, donde ninguna de las variables cumplen con ser NA.

> complete.cases(data)
[1]  TRUE  TRUE  TRUE  TRUE  TRUE FALSE FALSE FALSE
> data[complete.cases(data), ]
mujer edad peso
1     1   24  110
2     0   14  150
3     1   17  180
4     1   25  125
5     1   12  121

De forma más directa, na.omit permite realizar esta tarea directamente:
  
>   na.omit(data)
mujer edad peso
1     1   24  110
2     0   14  150
3     1   17  180
4     1   25  125
5     1   12  121

Adicionalmente, mediante el operador de negación (!) podemos acceder a las observaciones donde alguna de las variables tiene un valor NA.

> data[!complete.cases(data), ]
mujer edad peso
6     0   NA  121
7    NA   11  111
8     0   NA  150

Cuando utilizamos funciones también debemos de tener presente la presencia de valores NA en nuestros datos, debajo se colocan algunos ejemplos de cómo puede condicionarse el resultado a considerar o no los valores NA:
  
# NA en funciones y operaciones
# Media 
>   mean(x)
[1] NA

> mean(na.omit(x))
[1] 17.16667

> mean(x, na.rm = TRUE)
[1] 17.16667

#Ordenar datos
> sort(x)
[1] 11 12 14 17 24 25

> sort(x, na.last = TRUE)
[1] 11 12 14 17 24 25 NA NA

# Modelos de regresión
model0 <- lm(y ~ x, data = data)
model1 <- lm(y ~ x, data = data, na.action = na.exclude)
model2 <- lm(y ~ x, data = data, na.action = na.omit)

> resid(model0)
> resid(model1)
> resid(model2)

#Operaciones
data$edad^2  # NA^2 = NA,
data$edad+1  # NA+1=NA

Finalmente, podemos recodificar nuestros valores NA. insertando el promedio de nuestro vector en cada una de las observaciones con valores perdidos (Una mala idea). La librería tree permite hacer imputaciones sobre un conjunto mayor de valores.

> x2<-x
> x2[is.na(x)] <- mean(x, na.rm = TRUE)
  > cbind(x,x2)
x       x2
[1,] 24 24.00000
[2,] 14 14.00000
[3,] 17 17.00000
[4,] 25 25.00000
[5,] 12 12.00000
[6,] NA 17.16667
[7,] 11 11.00000
[8,] NA 17.16667

Referencias

-          Dhana, Klodian (2015). How to Deal with Missing Values in R. Consult: 8/9/2019. Disponible: https://datascienceplus.com/missing-values-in-r/.

-          Kabacoff, R. (2017). Missing Data. Quick-R. Consult: 8/9/2019. Disponible: https://www.statmethods.net/input/missingdata.html

-          UCLA (2012). How does R handle missing values?. Institute for Digital Research and Education. Consult: 8/9/2019. Disponible: https://stats.idre.ucla.edu/r/faq/how-does-r-handle-missing-values/.

-          Naval Postgraduate School. Missing Values in R. Consult: 8/9/2019. Disponible: https://faculty.nps.edu/sebuttre/home/R/missings.html.

-          Statistics Globe (nd). R Find Missing Values (6 Examples for Data Frame, Column & Vector).  Consult: 8/9/2019. Disponible: https://statisticsglobe.com/r-find-missing-values/.

Recesión plot en R usando ggplot (recession plot in r)

En el siguiente ejemplo se simular y replica -parcialmente- un ejemplo usado por el FMI para ilustrar la importancia del uso de series de ti...