Tratamiento de datos faltantes (Técnicas de Imputación)
Juan Manuel Fernandez
En esta clase vamos a trabajar tips para implementar las siguientes técnicas en R:
En esta clase vamos a trabajar con el dataset iris. Puntualmente, trabajaremos con el atributo Petal.Length, al cual le agregamos aleatoriamente valores faltantes:
faltantes = sum(is.na(iris$Petal.Length))
cat("El atributo Petal.Length posee", faltantes, "NA.")
El atributo Petal.Length posee 27 NA.
cat('Proporción NA (%):', mean(is.na(iris$Petal.Length))*100)
Proporción NA (%): 18
Además, de ahora en adelante trabajamos con “original”:
original = iris$Petal.Length
Los valores faltantes limitan algunas tareas de análisis de datos, por ejemplo el cálculo de funciones:
print(mean(original))
[1] NA
No obstante, en R generalmente existen parámetros en las funciones para ejecutarlas sin necesidad de imputar los faltantes:
print(mean(original, na.rm = TRUE))
[1] 3.649593
No obstante, estos parámetros muchas veces varían de acuerdo a la librería.
La alternativa más simple, es trabajar únicamente con los registros completos de un dataset. Podemos filtrarlos con la función na.omit():
iris.reg_completos<-na.omit(iris)
nrow(iris.reg_completos)
[1] 113
También podemos filtrar los faltantes para una columna:
reg.completos<-iris[!is.na(original),]
nrow(reg.completos)
[1] 123
No obsstante, esta no siempre es una opción viable.
Para realizar una sustitución por la media, seleccionamos las instancias con valor faltante y las reemplazamos por la media de ese atributo:
# inicializamos el atributo media_imp con "original"
media_imp = original
# Y a continuación sustituimos los faltantes por la media
media_imp[is.na(original)]<-mean(original, na.rm = TRUE)
A continuación verificamos que no quedan faltantes:
sum(is.na(media_imp))
[1] 0
Para hot deck, vamos a utilizar la librería VIM. La función hotdeck imputa los datos directamente sobre el atributo del parámetro y genera un nuevo atributo -boolean- que indica las instancias imputadas:
library(VIM) # Cargamos la librería
# Definimos un dataframe auxiliar
hot.deck_imp<-hotdeck(iris, variable="Petal.Length")$Petal.Length
# Se genera un nuevo atributo booleando con las imputaciones
original_imp<-hotdeck(iris, variable="Petal.Length")$Petal.Length_imp
A continuación verificamos que no quedan faltantes:
sum(is.na(hot.deck_imp))
[1] 0
Aquí vamos a ajustar un modelo de Regresión en función de una o varias características (Aquí nos limitamos a una):
#Armamos un modelo rl simple
rl_model<-lm(original ~ iris$Sepal.Length, data = iris)
# Si quisieramos ajutar un múltiple:
# rl_model<-lm(original ~ iris$Sepal.Width+iris$Petal.Length, data = iris)
# Imprimimos los coeficientes del modelo
print(rl_model$coefficients)
(Intercept) iris$Sepal.Length
-7.625281 1.929364
Luego, con la instrucción summary tenemos un resumen con los coeficientes y la eficiencia del modelo ajustado, entre otras cosas
summary(rl_model)
Analizamos el modelo:
Call:
lm(formula = original ~ iris$Sepal.Length, data = iris)
Residuals:
Min 1Q Median 3Q Max
-2.36503 -0.58733 -0.03677 0.54910 1.62791
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -7.62528 0.53787 -14.18 <2e-16 ***
iris$Sepal.Length 1.92936 0.09128 21.14 <2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 0.8489 on 119 degrees of freedom
(29 observations deleted due to missingness)
Multiple R-squared: 0.7897, Adjusted R-squared: 0.7879
F-statistic: 446.7 on 1 and 119 DF, p-value: < 2.2e-16
Ahora, solo queda imputar los valores faltantes en función del modelo generado:
# inicializamos el atributo regresion_imp con "original"
regresion_imp = original
# Filtro los valores de Sepal.Length donde original es NA
SL<-iris$Sepal.Length[is.na(original)]
coef<-rl_model$coefficients
# Hacemos la imputación
regresion_imp[is.na(regresion_imp)]<-coef[1]+SL*coef[2]
A continuación verificamos que no quedan faltantes:
sum(is.na(regresion_imp))
[1] 0
MICE es una técnica de imputación múltiple:
library(mice) # Cargamos la librería
# Imputamos los datos con pmm (media, para valores numéricos)
imputed_Data <- mice(iris, m=5, maxit = 3, method = 'pmm', printFlag=F)
Ahora, con la función complete recuperamos los datos completos:
#Tomamos los datos completos
completeData <- complete(imputed_Data)
# Los asignamos a una nueva variable
mice_imp <- completeData$Petal.Length
# Se verifica que todos los NA hayan sido imputados:
sum(is.na(mice_imp))
[1] 0
Ahora, analizamos gráficamente la distribución original y su variación luego de realizar las imputaciones:
# Analisis grafico de los resultados
plot(density(original, na.rm=TRUE), type = "l", col="red", ylab = "Original", ylim=c(0,0.3), main="Análisis de métodos de imputación")
lines(density(media_imp, na.rm=TRUE), type = "l", col="blue")
lines(density(regresion_imp, na.rm=TRUE), type = "l", col="green")
lines(density(hot.deck_imp, na.rm=TRUE), type = "l", col="yellow")
lines(density(mice_imp, na.rm=TRUE), type = "l", col="black")
legend(6.5, 0.3, legend=c("Original", "Media", 'Regresión', 'Hotdeck', 'MICE'), col=c("red", "blue", 'green','yellow', "black"), lty=1, cex=0.8)
Obtenemos los siguientes gráficos de densidad: