Preprocessing: Imputación de datos faltantes

Material docente en Quarto a partir del script original

Autores/as

Sergi Ramírez

Dante Conti

1 Introducción

Este documento docente presenta, de forma estructurada y comentada, distintos métodos de imputación de datos faltantes en R.

Se ha respetado al máximo la organización del script original, incorporando:

  • explicación conceptual antes de cada bloque,
  • comentarios detallados dentro del código,
  • separación por apartados para facilitar la docencia,
  • y una estructura adecuada para usar directamente en Quarto.

1.1 Objetivos docentes

Al finalizar este material, el estudiante debería poder:

  1. generar artificialmente valores perdidos,
  2. explorar patrones de missingness,
  3. distinguir entre técnicas básicas y avanzadas de imputación,
  4. aplicar métodos como media, aregImpute, MI, MICE, KNN y missForest,
  5. comparar distribuciones originales e imputadas.

2 Carga de librerías

En este primer bloque cargamos todos los paquetes necesarios para trabajar con visualización, exploración de valores perdidos e imputación.

# Carreguem les llibreries =====================================================
# Vector con los paquetes que vamos a necesitar durante toda la práctica.
# Incluye paquetes para:
# - visualización (ggplot2, plotly, visdata, VIM)
# - manipulación de datos (dplyr, tidyverse)
# - análisis de missingness (naniar, mi)
# - imputación (Hmisc, mice, DMwR, missForest)
list.of.packages <- c(
  "ggplot2", "plotly", "dplyr", "tidyverse", "naniar",
  "mi", "Hmisc", "mice", "pool", "VIM", "missForest", "RANN", "caret"
)

# Detectamos qué paquetes no están instalados en el sistema.
new.packages <- list.of.packages[!(list.of.packages %in% installed.packages()[, "Package"])]

# Si falta alguno, lo instalamos automáticamente.
if (length(new.packages) > 0) {
  install.packages(new.packages)
}

# Cargamos todos los paquetes en memoria.
lapply(list.of.packages, require, character.only = TRUE)
[[1]]
[1] TRUE

[[2]]
[1] TRUE

[[3]]
[1] TRUE

[[4]]
[1] TRUE

[[5]]
[1] TRUE

[[6]]
[1] TRUE

[[7]]
[1] TRUE

[[8]]
[1] TRUE

[[9]]
[1] TRUE

[[10]]
[1] TRUE

[[11]]
[1] TRUE

[[12]]
[1] TRUE

[[13]]
[1] TRUE
# Limpiamos objetos auxiliares que ya no necesitamos y lanzamos el recolector
# de basura para liberar memoria.
rm(list.of.packages, new.packages)
gc()
          used  (Mb) gc trigger  (Mb) max used  (Mb)
Ncells 3625439 193.7    7357892 393.0  4386978 234.3
Vcells 6067688  46.3   10146329  77.5  8360610  63.8

3 Generación de datos con valores perdidos

Para practicar métodos de imputación, primero generamos una versión incompleta del conjunto de datos iris, introduciendo un 10% de valores ausentes de forma artificial.

# Generate data with NA's ======================================================

# Generamos una copia del dataset iris con aproximadamente un 10% de valores NA.
# La función prodNA del paquete missForest introduce missing values de forma
# artificial para que podamos practicar imputación.
iris.mis <- missForest::prodNA(iris, noNA = 0.1)

# Otras formas de crear valores ausentes artificialmente en un dataframe:
# iris.mis <- mi::create.missing(iris, pct.mis = 10)

# Mostramos un resumen básico del dataset con NA para inspeccionar su estructura.
summary(iris.mis)
  Sepal.Length    Sepal.Width     Petal.Length    Petal.Width   
 Min.   :4.300   Min.   :2.000   Min.   :1.000   Min.   :0.100  
 1st Qu.:5.100   1st Qu.:2.800   1st Qu.:1.600   1st Qu.:0.300  
 Median :5.700   Median :3.000   Median :4.300   Median :1.300  
 Mean   :5.796   Mean   :3.065   Mean   :3.726   Mean   :1.168  
 3rd Qu.:6.350   3rd Qu.:3.400   3rd Qu.:5.100   3rd Qu.:1.800  
 Max.   :7.700   Max.   :4.400   Max.   :6.900   Max.   :2.500  
 NA's   :11      NA's   :17      NA's   :13      NA's   :15     
       Species  
 setosa    :44  
 versicolor:45  
 virginica :42  
 NA's      :19  
                
                
                

4 Little Test

Una cuestión importante en datos faltantes es entender si los NA aparecen completamente al azar o siguen algún patrón. Para ello usamos el test MCAR.

# Little Test ==================================================================

# Aplicamos el test MCAR (Missing Completely At Random).
# Este contraste ayuda a evaluar si los datos faltantes se generan de manera
# completamente aleatoria.
naniar::mcar_test(iris.mis)
# Interpretación conceptual:
# - Si el p-valor es pequeño, rechazamos que los missing sean completamente aleatorios.
# - Si el p-valor no es pequeño, no tenemos evidencia para rechazar MCAR.

# Nota importante docente:
# En el script original aparece el comentario:
# "If the test p-value is less than 0 this means..."
# Lo correcto estadísticamente sería hablar de un p-valor bajo, por ejemplo < 0.05.

5 Patrones descriptivos de valores perdidos en una base de datos

Antes de imputar, conviene visualizar y describir los patrones de ausencia. Esto ayuda a detectar si hay variables particularmente afectadas o relaciones entre missingness y otras variables.

5.1 Exploración visual de missingness

# Descriptive NA's patterns in a databases =====================================

# Cargamos explícitamente visdat para sus funciones de visualización.
library(visdat)

# Visualizamos el tipo de dato y la presencia de NA en airquality.
vis_dat(airquality)

# Visualizamos el tipo de dato y la presencia de NA en iris.mis.
vis_dat(iris.mis)

# Representación específica de valores perdidos en airquality.
vis_miss(airquality)

# Representación específica de valores perdidos en iris.mis.
vis_miss(iris.mis)

5.2 Relación entre valores perdidos y variables numéricas

# Gráfico básico de dispersión sin destacar missingness.
ggplot(airquality, aes(x = Solar.R, y = Ozone)) +
  geom_point()

# Gráfico de dispersión destacando las observaciones con missingness.
ggplot(airquality, aes(x = Solar.R, y = Ozone)) +
  geom_miss_point()

# Añadimos facetado por mes para observar si el patrón de NA cambia según el mes.
ggplot(airquality, aes(x = Solar.R, y = Ozone)) +
  geom_miss_point() +
  facet_wrap(~Month)

# Mismo gráfico anterior pero con tema oscuro para enfatizar la visualización.
ggplot(airquality, aes(x = Solar.R, y = Ozone)) +
  geom_miss_point() +
  facet_wrap(~Month) +
  theme_dark()

5.3 Visualización del número de missings por variable

# Visualizamos cuántos NA tiene cada variable.
gg_miss_var(airquality) +
  labs(y = "Look at all the missing ones")

5.4 Sombra de missingness y resumen por grupos

# Detect NA in Dataframe =======================================================

# Creamos un shadow matrix: añade variables auxiliares con sufijo _NA que indican
# si el valor original era NA o no.
aq_shadow <- bind_shadow(airquality)

# Calculamos estadísticas descriptivas de Solar.R separando los registros según
# si Ozone es NA o no.
airquality %>%
  bind_shadow() %>%
  group_by(Ozone_NA) %>%
  summarise_at(
    .vars = "Solar.R",
    .funs = c("mean", "sd", "var", "min", "max"),
    na.rm = TRUE
  )
# Representamos la distribución de la temperatura diferenciando los casos en los
# que Ozone está ausente de aquellos en los que no lo está.
ggplot(aq_shadow, aes(x = Temp, colour = Ozone_NA)) +
  geom_density()

5.5 Estadísticos de missingness

# Extract statistics with NAs in Data Frame ====================================

# Proporción de casos (filas) que contienen algún NA.
prop_miss_case(airquality)
[1] 0.2745098
# Porcentaje de casos con NA.
pct_miss_case(airquality)
[1] 27.45098
# Resumen por fila del patrón de missingness.
miss_case_summary(airquality)
# Tabla de frecuencia del número de missings por fila.
miss_case_table(airquality)
# Proporción de missings por variable.
prop_miss_var(airquality)
[1] 0.3333333
# Porcentaje de missings por variable.
pct_miss_var(airquality)
[1] 33.33333
# Resumen de missingness por variable.
miss_var_summary(airquality)
# Tabla de frecuencia de missings por variable.
miss_var_table(airquality)

6 1.1 Imputación básica

En este apartado se ilustran técnicas sencillas de imputación, útiles para comprender la lógica general aunque normalmente insuficientes en contextos reales complejos.

6.1 Imputación con la media y con valores aleatorios

# 1.1 Basic Imputation  ========================================================

# Imputamos los NA de Sepal.Length usando la media de la variable.
# La función impute del paquete Hmisc devuelve una versión imputada de la variable.
iris.mis[, "imputed_Sepal.Length"] <- with(iris.mis, impute(Sepal.Length, mean))

# Imputamos los NA de Sepal.Length usando valores aleatorios observados.
iris.mis[, "imputed_Sepal.Length2"] <- with(iris.mis, impute(Sepal.Length, "random"))

# Nota docente:
# De forma análoga se podrían usar estrategias como min, max o median.

# Reestructuramos los datos a formato largo para comparar distribuciones.
df_long <- iris.mis %>%
  select(Sepal.Length, imputed_Sepal.Length, imputed_Sepal.Length2) %>%
  pivot_longer(cols = everything(), names_to = "Variable", values_to = "Valor")

# Representamos la densidad de la variable original y de las variables imputadas.
ggplot(df_long, aes(x = Valor, fill = Variable)) +
  geom_density(alpha = 0.3) +
  labs(
    title = "Densidad de las tres variables",
    x = "Valor",
    y = "Densidad"
  ) +
  theme_minimal() +
  scale_fill_manual(values = c("blue", "red", "green"))

# Eliminamos las variables auxiliares para no contaminar el dataframe original.
iris.mis[, c("imputed_Sepal.Length", "imputed_Sepal.Length2")] <- NULL

6.2 Imputación con aregImpute

aregImpute implementa una imputación basada en modelos aditivos flexibles y múltiples simulaciones. Aquí se aplica sobre todas las variables del dataset.

# ------------------------------------------------------------------------------
# using argImpute

# Ajustamos el modelo de imputación.
# n.impute = 5 indica que se generan 5 imputaciones para los valores ausentes.
impute_arg <- aregImpute(
  ~ Sepal.Length + Sepal.Width + Petal.Length + Petal.Width + Species,
  data = iris.mis,
  n.impute = 5
)
Iteration 1 
Iteration 2 
Iteration 3 
Iteration 4 
Iteration 5 
Iteration 6 
Iteration 7 
Iteration 8 
# Mostramos el objeto resultante para inspeccionar la imputación.
impute_arg

Multiple Imputation using Bootstrap and PMM

aregImpute(formula = ~Sepal.Length + Sepal.Width + Petal.Length + 
    Petal.Width + Species, data = iris.mis, n.impute = 5)

n: 150  p: 5    Imputations: 5      nk: 3 

Number of NAs:
Sepal.Length  Sepal.Width Petal.Length  Petal.Width      Species 
          11           17           13           15           19 

             type d.f.
Sepal.Length    s    2
Sepal.Width     s    2
Petal.Length    s    2
Petal.Width     s    2
Species         c    2

Transformation of Target Variables Forced to be Linear

R-squares for Predicting Non-Missing Values for Each Variable
Using Last Imputations of Predictors
Sepal.Length  Sepal.Width Petal.Length  Petal.Width      Species 
       0.855        0.621        0.979        0.962        0.989 
# Revisamos específicamente las imputaciones generadas para Sepal.Length.
impute_arg$imputed$Sepal.Length
    [,1] [,2] [,3] [,4] [,5]
6    5.2  5.1  5.2  5.7  5.7
37   4.6  4.9  4.8  5.4  4.9
50   5.4  5.2  4.8  4.7  5.0
51   5.9  6.9  5.9  6.0  6.1
75   5.7  5.7  5.7  5.5  6.2
87   6.3  5.9  6.4  6.0  6.1
88   4.9  6.3  5.8  5.8  5.2
106  6.6  6.0  6.0  6.6  6.5
116  6.0  5.9  6.7  6.3  6.4
132  7.7  7.7  7.7  7.7  7.7
145  7.1  6.3  6.8  7.2  6.3
# Calculamos la media de las 5 imputaciones para cada observación ausente.
imputed_Sepal.Length <- rowMeans(impute_arg$imputed$Sepal.Length)

# Creamos una nueva variable partiendo de la variable real original.
new_var_imputed <- iris$Sepal.Length

# Sustituimos únicamente las posiciones que tenían NA por la media imputada.
new_var_imputed[as.numeric(names(imputed_Sepal.Length))] <- imputed_Sepal.Length

# Comparamos la distribución real frente a la imputada.
newBD <- data.frame(real = iris[, "Sepal.Length"], imputed = new_var_imputed)

df_long <- newBD %>%
  pivot_longer(cols = everything(), names_to = "Variable", values_to = "Valor")

ggplot(df_long, aes(x = Valor, fill = Variable)) +
  geom_density(alpha = 0.3) +
  labs(
    title = "Densidad de las dos variables",
    x = "Valor",
    y = "Densidad"
  ) +
  theme_minimal() +
  scale_fill_manual(values = c("blue", "red"))


7 1.1.1 Multiple Iterative Regression Imputation (MI method)

Este bloque usa el paquete mi, que implementa imputación múltiple mediante aproximaciones iterativas basadas en modelos probabilísticos.

# 1.1.1 Multiple Iterative Regression Imputation (MI method) -------------------

# Ejecutamos la imputación múltiple con una semilla fija para garantizar
# reproducibilidad.
mi_data <- mi(iris.mis, seed = 335)

# Resumen del modelo de imputación.
summary(mi_data)
$Sepal.Length
$Sepal.Length$is_missing
missing
FALSE  TRUE 
  139    11 

$Sepal.Length$imputed
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
-0.8313 -0.1808  0.3217  0.2455  0.6659  1.3929 

$Sepal.Length$observed
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
-0.92667 -0.43102 -0.05928  0.00000  0.37441  1.17984 


$Sepal.Width
$Sepal.Width$is_missing
missing
FALSE  TRUE 
  133    17 

$Sepal.Width$imputed
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
-0.91754 -0.32512  0.05625  0.03350  0.40277  0.99748 

$Sepal.Width$observed
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
-1.1920 -0.2963 -0.0724  0.0000  0.3755  1.4951 


$Petal.Length
$Petal.Length$is_missing
missing
FALSE  TRUE 
  137    13 

$Petal.Length$imputed
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
-0.77755 -0.50058  0.27530  0.08156  0.43277  0.90765 

$Petal.Length$observed
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
-0.7797 -0.6081  0.1643  0.0000  0.3932  0.9081 


$Petal.Width
$Petal.Width$is_missing
missing
FALSE  TRUE 
  135    15 

$Petal.Width$imputed
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
-0.8516 -0.1801  0.1935  0.1346  0.5115  0.8273 

$Petal.Width$observed
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
-0.70623 -0.57400  0.08718  0.00000  0.41776  0.88058 


$Species
$Species$crosstab
            
             observed imputed
  setosa          176      27
  versicolor      180      22
  virginica       168      27
# Gráficos diagnósticos del proceso de imputación.
plot(mi_data)

# Evitamos que R pregunte entre gráficos cuando hay múltiples paneles.
par(ask = FALSE)

# Revisamos la estructura interna del objeto de imputación.
mi_data@data
$`chain:1`
Object of class missing_data.frame with 150 observations on 5 variables

There are 14 missing data patterns

Append '@patterns' to this missing_data.frame to access the corresponding pattern for every observation or perhaps use table()

                              type missing method  model
Sepal.Length            continuous      11    ppd linear
Sepal.Width             continuous      17    ppd linear
Petal.Length            continuous      13    ppd linear
Petal.Width             continuous      15    ppd linear
Species      unordered-categorical      19    ppd mlogit

                  family     link transformation
Sepal.Length    gaussian identity    standardize
Sepal.Width     gaussian identity    standardize
Petal.Length    gaussian identity    standardize
Petal.Width     gaussian identity    standardize
Species      multinomial    logit           <NA>

$`chain:2`
Object of class missing_data.frame with 150 observations on 5 variables

There are 14 missing data patterns

Append '@patterns' to this missing_data.frame to access the corresponding pattern for every observation or perhaps use table()

                              type missing method  model
Sepal.Length            continuous      11    ppd linear
Sepal.Width             continuous      17    ppd linear
Petal.Length            continuous      13    ppd linear
Petal.Width             continuous      15    ppd linear
Species      unordered-categorical      19    ppd mlogit

                  family     link transformation
Sepal.Length    gaussian identity    standardize
Sepal.Width     gaussian identity    standardize
Petal.Length    gaussian identity    standardize
Petal.Width     gaussian identity    standardize
Species      multinomial    logit           <NA>

$`chain:3`
Object of class missing_data.frame with 150 observations on 5 variables

There are 14 missing data patterns

Append '@patterns' to this missing_data.frame to access the corresponding pattern for every observation or perhaps use table()

                              type missing method  model
Sepal.Length            continuous      11    ppd linear
Sepal.Width             continuous      17    ppd linear
Petal.Length            continuous      13    ppd linear
Petal.Width             continuous      15    ppd linear
Species      unordered-categorical      19    ppd mlogit

                  family     link transformation
Sepal.Length    gaussian identity    standardize
Sepal.Width     gaussian identity    standardize
Petal.Length    gaussian identity    standardize
Petal.Width     gaussian identity    standardize
Species      multinomial    logit           <NA>

$`chain:4`
Object of class missing_data.frame with 150 observations on 5 variables

There are 14 missing data patterns

Append '@patterns' to this missing_data.frame to access the corresponding pattern for every observation or perhaps use table()

                              type missing method  model
Sepal.Length            continuous      11    ppd linear
Sepal.Width             continuous      17    ppd linear
Petal.Length            continuous      13    ppd linear
Petal.Width             continuous      15    ppd linear
Species      unordered-categorical      19    ppd mlogit

                  family     link transformation
Sepal.Length    gaussian identity    standardize
Sepal.Width     gaussian identity    standardize
Petal.Length    gaussian identity    standardize
Petal.Width     gaussian identity    standardize
Species      multinomial    logit           <NA>

8 1.1.2 Media condicionada por variable objetivo

Aquí se aplica una estrategia simple pero útil en docencia: imputar con la media del grupo, definida por una variable objetivo o categórica, en este caso Species.

# 1.1.2 MEAN WITH TARGET VARS --------------------------------------------------

# Definimos la variable objetivo o de agrupación.
target <- "Species"

# Construimos un dataset sin la variable target para introducir NA únicamente
# en las variables predictoras.
data <- subset(iris, select = -c(get(target)))

# Generamos valores ausentes de forma artificial.
data <- prodNA(data, noNA = 0.1)

# Recuperamos la variable target original.
data$Species <- iris[, target]

# Identificamos qué variables debemos imputar: todas excepto la target.
varImp <- colnames(data)[which(!colnames(data) %in% target)]

# Calculamos la media de cada variable numérica dentro de cada grupo de Species.
means <- aggregate(data[, varImp], list(data[, target]), mean, na.rm = TRUE)

# Imputamos los valores ausentes usando la media de su grupo.
for (c in varImp) {
  for (g in means[, "Group.1"]) {
    # Condición lógica: filas que pertenecen al grupo g.
    cond <- data[, target] == g

    # Detectamos los NA de la variable c dentro de ese grupo.
    na_index <- is.na(data[, c]) & cond

    # Sustituimos cada NA por la media del grupo correspondiente.
    data[na_index, c] <- means[means[, "Group.1"] == g, c]
  }
}

# Resumen del dataset imputado.
summary(data)
  Sepal.Length    Sepal.Width     Petal.Length    Petal.Width   
 Min.   :4.300   Min.   :2.000   Min.   :1.000   Min.   :0.100  
 1st Qu.:5.100   1st Qu.:2.800   1st Qu.:1.500   1st Qu.:0.300  
 Median :5.900   Median :3.000   Median :4.270   Median :1.326  
 Mean   :5.862   Mean   :3.071   Mean   :3.756   Mean   :1.201  
 3rd Qu.:6.400   3rd Qu.:3.400   3rd Qu.:5.100   3rd Qu.:1.800  
 Max.   :7.900   Max.   :4.400   Max.   :6.900   Max.   :2.500  
       Species  
 setosa    :50  
 versicolor:50  
 virginica :50  
                
                
                

8.1 Comparación visual entre original e imputado

# Marcamos el origen de cada dataset para poder compararlos visualmente.
iris[, "Tipo"] <- "original"
data[, "Tipo"] <- "imputed"

# Fusionamos ambos dataframes en uno solo.
data_long <- bind_rows(iris, data)

# Seleccionamos únicamente las variables numéricas para representar densidades.
cols_numeric <- names(data_long)[sapply(data_long, is.numeric) & names(data_long) != "Tipo"]

# Pasamos a formato largo para facetar un gráfico por variable.
data_long <- data_long %>%
  pivot_longer(cols = all_of(cols_numeric), names_to = "Variable", values_to = "Valor")

# Representamos la comparación de distribuciones por variable.
ggplot(data_long, aes(x = Valor, fill = Tipo)) +
  geom_density(alpha = 0.3) +
  facet_wrap(~Variable, scales = "free") +
  labs(
    title = "Comparación de Distribuciones: Original vs Imputado",
    x = "Valor",
    y = "Densidad"
  ) +
  theme_minimal() +
  scale_fill_manual(values = c("blue", "red"))

# Nota docente:
# En el script original aparece esta línea:
# iris.mis[, "Tipo"] <- NULL
# Sin embargo, la columna Tipo se añadió a iris y data, no a iris.mis.
# Por coherencia, eliminamos la columna Tipo del dataset iris original.
iris[, "Tipo"] <- NULL

9 1.2 Multiple Imputation by Chained Equations (MICE)

MICE es uno de los métodos más utilizados en estadística aplicada para imputación múltiple. La idea es modelar cada variable con missingness condicionalmente al resto de variables.

# 1.2 Multiple Imputation by Chained Equations (MICE) ==========================
# https://amices.org/mice/

# Eliminamos variables categóricas para quedarnos únicamente con variables
# numéricas en este ejemplo concreto.
quiCat <- which(sapply(iris.mis, class) %in% c("character", "factor"))
categories <- names(iris.mis)[quiCat]
iris.mis2 <- subset(iris.mis, select = -c(get(categories)))

# Mostramos un resumen del dataset resultante.
summary(iris.mis2)
  Sepal.Length    Sepal.Width     Petal.Length    Petal.Width   
 Min.   :4.300   Min.   :2.000   Min.   :1.000   Min.   :0.100  
 1st Qu.:5.100   1st Qu.:2.800   1st Qu.:1.600   1st Qu.:0.300  
 Median :5.700   Median :3.000   Median :4.300   Median :1.300  
 Mean   :5.796   Mean   :3.065   Mean   :3.726   Mean   :1.168  
 3rd Qu.:6.350   3rd Qu.:3.400   3rd Qu.:5.100   3rd Qu.:1.800  
 Max.   :7.700   Max.   :4.400   Max.   :6.900   Max.   :2.500  
 NA's   :11      NA's   :17      NA's   :13      NA's   :15     
# Visualizamos el patrón de datos ausentes.
par(mfrow = c(1, 1))
md.pattern(iris.mis2, rotate.names = TRUE)

    Sepal.Length Petal.Length Petal.Width Sepal.Width   
100            1            1           1           1  0
14             1            1           1           0  1
11             1            1           0           1  1
2              1            1           0           0  2
11             1            0           1           1  1
1              1            0           1           0  2
8              0            1           1           1  1
2              0            1           0           1  2
1              0            0           1           1  2
              11           13          15          17 56

9.1 Visualización del patrón con VIM

# Look the NA's with VIM packages
mice_plot <- aggr(
  iris.mis2,
  col = c("navyblue", "yellow"),
  numbers = TRUE,
  sortVars = TRUE,
  labels = names(iris.mis),
  cex.axis = .7,
  gap = 3,
  ylab = c("Missing data", "Pattern")
)


 Variables sorted by number of missings: 
     Variable      Count
  Sepal.Width 0.11333333
  Petal.Width 0.10000000
 Petal.Length 0.08666667
 Sepal.Length 0.07333333

9.2 Ejecución de MICE

# Multiple impute the missing values.
# m = 5 indica que generamos 5 datasets imputados.
# maxit = 50 fija el número máximo de iteraciones.
# method = 'pmm' usa Predictive Mean Matching.
imputed_Data <- mice(iris.mis2, m = 5, maxit = 50, method = "pmm", seed = 500)

 iter imp variable
  1   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  1   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  1   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  1   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  1   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  2   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  2   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  2   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  2   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  2   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  3   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  3   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  3   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  3   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  3   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  4   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  4   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  4   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  4   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  4   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  5   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  5   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  5   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  5   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  5   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  6   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  6   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  6   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  6   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  6   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  7   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  7   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  7   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  7   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  7   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  8   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  8   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  8   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  8   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  8   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  9   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  9   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  9   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  9   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  9   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  10   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  10   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  10   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  10   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  10   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  11   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  11   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  11   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  11   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  11   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  12   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  12   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  12   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  12   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  12   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  13   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  13   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  13   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  13   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  13   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  14   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  14   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  14   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  14   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  14   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  15   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  15   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  15   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  15   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  15   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  16   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  16   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  16   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  16   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  16   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  17   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  17   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  17   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  17   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  17   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  18   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  18   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  18   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  18   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  18   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  19   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  19   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  19   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  19   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  19   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  20   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  20   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  20   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  20   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  20   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  21   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  21   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  21   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  21   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  21   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  22   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  22   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  22   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  22   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  22   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  23   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  23   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  23   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  23   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  23   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  24   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  24   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  24   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  24   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  24   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  25   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  25   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  25   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  25   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  25   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  26   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  26   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  26   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  26   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  26   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  27   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  27   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  27   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  27   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  27   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  28   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  28   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  28   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  28   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  28   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  29   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  29   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  29   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  29   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  29   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  30   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  30   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  30   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  30   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  30   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  31   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  31   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  31   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  31   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  31   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  32   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  32   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  32   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  32   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  32   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  33   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  33   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  33   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  33   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  33   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  34   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  34   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  34   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  34   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  34   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  35   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  35   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  35   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  35   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  35   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  36   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  36   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  36   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  36   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  36   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  37   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  37   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  37   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  37   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  37   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  38   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  38   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  38   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  38   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  38   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  39   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  39   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  39   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  39   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  39   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  40   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  40   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  40   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  40   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  40   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  41   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  41   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  41   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  41   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  41   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  42   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  42   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  42   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  42   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  42   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  43   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  43   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  43   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  43   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  43   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  44   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  44   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  44   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  44   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  44   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  45   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  45   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  45   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  45   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  45   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  46   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  46   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  46   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  46   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  46   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  47   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  47   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  47   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  47   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  47   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  48   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  48   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  48   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  48   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  48   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  49   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  49   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  49   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  49   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  49   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  50   1  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  50   2  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  50   3  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  50   4  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
  50   5  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
# Resumen del objeto de imputación.
summary(imputed_Data)
Class: mids
Number of multiple imputations:  5 
Imputation methods:
Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
       "pmm"        "pmm"        "pmm"        "pmm" 
PredictorMatrix:
             Sepal.Length Sepal.Width Petal.Length Petal.Width
Sepal.Length            0           1            1           1
Sepal.Width             1           0            1           1
Petal.Length            1           1            0           1
Petal.Width             1           1            1           0
# Inspección visual de la calidad de imputación para Sepal.Width.
stripplot(imputed_Data, Sepal.Width, pch = 19, xlab = "Imputation number")

# Valores imputados específicamente en Sepal.Width.
imputed_Data$imp$Sepal.Width
# Recuperamos los datos completos en formato largo.
completeData <- mice::complete(imputed_Data, action = "long")

# Mostramos las primeras filas como inspección rápida.
head(completeData)

9.3 Ejercicio propuesto

Desplegar una visualización múltiple que compare, para todas las variables numéricas del dataframe, la distribución original frente a la distribución imputada.


10 1.3 KNN

El método KNN imputa los valores faltantes utilizando observaciones cercanas en el espacio de variables. Es una técnica sencilla y efectiva cuando existe una estructura local útil en los datos.

# 1.3 KNN  =====================================================================

# Identificamos el tipo de cada variable.
tipos <- sapply(iris.mis, class)

# Seleccionamos únicamente variables numéricas e integer.
varNum <- names(tipos)[which(tipos %in% c("numeric", "integer"))]

# Aplicamos imputación KNN con k = 1.
# Es decir, cada valor ausente se imputará usando su vecino más cercano.
preProcValues <- caret::preProcess(iris.mis[, varNum], method = "knnImpute", k = 1) 
data_knn_imputation <- predict(preProcValues, iris.mis)

# Resumen del dataset imputado.
summary(data_knn_imputation)
  Sepal.Length        Sepal.Width          Petal.Length      
 Min.   :-1.853335   Min.   :-2.384e+00   Min.   :-1.559403  
 1st Qu.:-0.862037   1st Qu.:-5.927e-01   1st Qu.:-1.259028  
 Median : 0.005349   Median :-1.448e-01   Median : 0.385883  
 Mean   : 0.042522   Mean   : 1.122e-05   Mean   : 0.007888  
 3rd Qu.: 0.748822   3rd Qu.: 6.949e-01   3rd Qu.: 0.786383  
 Max.   : 2.359682   Max.   : 2.990e+00   Max.   : 1.816241  
  Petal.Width             Species  
 Min.   :-1.41246   setosa    :44  
 1st Qu.:-1.14799   versicolor:45  
 Median : 0.17435   virginica :42  
 Mean   : 0.03771   NA's      :19  
 3rd Qu.: 0.83553                  
 Max.   : 1.76117                  
# Otra forma seria utilizando el paquete VIM 
data_knn_imputation <- VIM::kNN(iris.mis[, varNum], k = 1)
 Sepal.Width Petal.Length  Petal.Width  Sepal.Width Petal.Length  Petal.Width 
         2.0          1.0          0.1          4.4          6.9          2.5 
Sepal.Length Petal.Length  Petal.Width Sepal.Length Petal.Length  Petal.Width 
         4.3          1.0          0.1          7.7          6.9          2.5 
Sepal.Length  Sepal.Width  Petal.Width Sepal.Length  Sepal.Width  Petal.Width 
         4.3          2.0          0.1          7.7          4.4          2.5 
Sepal.Length  Sepal.Width Petal.Length Sepal.Length  Sepal.Width Petal.Length 
         4.3          2.0          1.0          7.7          4.4          6.9 
# Resumen del dataset imputado.
summary(data_knn_imputation)
  Sepal.Length    Sepal.Width     Petal.Length    Petal.Width   
 Min.   :4.300   Min.   :2.000   Min.   :1.000   Min.   :0.100  
 1st Qu.:5.100   1st Qu.:2.800   1st Qu.:1.525   1st Qu.:0.300  
 Median :5.800   Median :3.000   Median :4.400   Median :1.300  
 Mean   :5.825   Mean   :3.059   Mean   :3.735   Mean   :1.191  
 3rd Qu.:6.400   3rd Qu.:3.300   3rd Qu.:5.100   3rd Qu.:1.800  
 Max.   :7.700   Max.   :4.400   Max.   :6.900   Max.   :2.500  
 Sepal.Length_imp Sepal.Width_imp Petal.Length_imp Petal.Width_imp
 Mode :logical    Mode :logical   Mode :logical    Mode :logical  
 FALSE:139        FALSE:133       FALSE:137        FALSE:135      
 TRUE :11         TRUE :17        TRUE :13         TRUE :15       
                                                                  
                                                                  
                                                                  

10.1 Comparación visual con los datos reales

# Comparamos la variable Sepal.Length real frente a la imputada por KNN.
newBD <- data.frame(
  real = iris[, "Sepal.Length"],
  imputed = data_knn_imputation[, "Sepal.Length"]
)

# Reorganizamos a formato largo para representar densidades.
df_long <- newBD %>%
  pivot_longer(cols = everything(), names_to = "Variable", values_to = "Valor")

# Dibujamos la comparación de distribuciones.
ggplot(df_long, aes(x = Valor, fill = Variable)) +
  geom_density(alpha = 0.3) +
  labs(
    title = "Densidad de las dos variables",
    x = "Valor",
    y = "Densidad"
  ) +
  theme_minimal() +
  scale_fill_manual(values = c("blue", "red"))

10.2 Ejercicio propuesto

Desplegar una visualización múltiple que compare la imputación y los datos originales en todas las variables numéricas del dataframe.


11 1.4 missForest

missForest es un método robusto y muy utilizado cuando hay relaciones no lineales y combinaciones de variables numéricas y categóricas. Se basa en bosques aleatorios.

# 1.4 missForest ===============================================================

# Imputamos los valores ausentes usando los parámetros por defecto.
# variablewise = TRUE devuelve el error por variable.
# verbose = TRUE muestra el progreso por consola.
iris.imp <- missForest(iris.mis, variablewise = TRUE, verbose = TRUE)
  missForest iteration 1 in progress...done!
    estimated error(s): 0.1556513 0.08288479 0.173488 0.03617465 0.06870229 
    difference(s): 0.005367935 0.09333333 
    time: 0.03 seconds

  missForest iteration 2 in progress...done!
    estimated error(s): 0.1271514 0.08253977 0.06552404 0.02796956 0.0610687 
    difference(s): 6.003229e-05 0 
    time: 0.02 seconds

  missForest iteration 3 in progress...done!
    estimated error(s): 0.1248679 0.08148458 0.07318228 0.02940426 0.0610687 
    difference(s): 1.339835e-05 0 
    time: 0.03 seconds

  missForest iteration 4 in progress...done!
    estimated error(s): 0.1271934 0.08397521 0.06050999 0.02713873 0.04580153 
    difference(s): 6.503742e-06 0 
    time: 0.02 seconds

  missForest iteration 5 in progress...done!
    estimated error(s): 0.1250966 0.08541677 0.06685214 0.02747478 0.05343511 
    difference(s): 3.884594e-06 0 
    time: 0.03 seconds

  missForest iteration 6 in progress...done!
    estimated error(s): 0.1275682 0.08405009 0.06867867 0.02764291 0.06870229 
    difference(s): 4.397572e-06 0 
    time: 0.01 seconds
# Consultamos el dataframe imputado final.
iris.imp$ximp
# Revisamos el error de imputación out-of-bag.
iris.imp$OOBerror
       MSE        MSE        MSE        MSE        PFC 
0.12509656 0.08541677 0.06685214 0.02747478 0.05343511 
# Interpretación docente:
# - NRMSE (Normalized Root Mean Squared Error) se usa para variables continuas.
# - PFC (Proportion of Falsely Classified) se usa para variables categóricas.

11.1 Comparación contra los datos reales

# Calculamos el error comparando:
# - los datos imputados,
# - el dataset con missing,
# - y los datos originales completos.
iris.err <- mixError(iris.imp$ximp, iris.mis, iris)
iris.err
    NRMSE       PFC 
0.1758295 0.0000000 
# Comparamos visualmente Sepal.Length real frente a la imputada por missForest.
newBD <- data.frame(
  real = iris[, "Sepal.Length"],
  imputed = iris.imp$ximp[, "Sepal.Length"]
)

# Formato largo para la representación.
df_long <- newBD %>%
  pivot_longer(cols = everything(), names_to = "Variable", values_to = "Valor")

# Gráfico de densidades comparadas.
ggplot(df_long, aes(x = Valor, fill = Variable)) +
  geom_density(alpha = 0.3) +
  labs(
    title = "Densidad de las dos variables",
    x = "Valor",
    y = "Densidad"
  ) +
  theme_minimal() +
  scale_fill_manual(values = c("blue", "red"))

11.2 Ejercicio propuesto

Desplegar una visualización múltiple que compare la imputación y los datos originales en todas las variables numéricas del dataframe.


12 Extras

Cuando se trabaja con valores faltantes, en muchas ocasiones el problema no es solamente imputar, sino también detectar valores especiales que en realidad deberían considerarse NA.

Por ejemplo, en algunas bases de datos aparecen códigos como:

  • -99
  • -1
  • "N/A"
  • "Not Available"

que semánticamente representan ausencia de información.

# ==============================================================================
# Extras:
# When you are dealing with missing values, you might want to replace values with
# a missing values (NA). This is useful in cases when you know the origin of the
# data and can be certain which values should be missing. For example, you might
# know that all values of “N/A”, “N A”, and “Not Available”, or -99, or -1 are
# supposed to be missing.
#
# naniar provides functions to specifically work on this type of problem using
# the function replace_with_na. This function is the compliment to tidyr::replace_na,
# which replaces an NA value with a specified value, whereas naniar::replace_with_na
# replaces a value with an NA:
#
# tidyr::replace_na: Missing values turns into a value (NA -> -99)
# naniar::replace_with_na: Value becomes a missing value (-99 -> NA)

12.1 Ejemplo conceptual

# Ejemplo opcional de uso:
# Supongamos que en una variable el valor -99 significa realmente "dato no disponible".

df_example <- data.frame(x = c(1, 2, -99, 4, -99, 6))

# Reemplazamos el código -99 por NA real.
df_example_na <- naniar::replace_with_na(df_example, replace = list(x = -99))

df_example
df_example_na

13 Conclusiones docentes

A nivel pedagógico, este guion permite mostrar una progresión natural:

  1. Generar y detectar missingness.
  2. Visualizar patrones de ausencia.
  3. Aplicar imputaciones simples como referencia.
  4. Evolucionar hacia métodos más robustos como MICE, KNN y missForest.
  5. Comparar distribuciones para evaluar si la imputación distorsiona o preserva la estructura de los datos.

En términos metodológicos:

  • las imputaciones por media son fáciles de explicar pero pueden sesgar la varianza,
  • las imputaciones condicionadas por grupo introducen estructura supervisada útil,
  • MICE ofrece una aproximación estadísticamente sólida,
  • KNN aprovecha vecindad local,
  • missForest suele rendir muy bien cuando hay no linealidades y mixtura de tipos.

14 Bibliografía

Aquesta web està creada por Dante Conti y Sergi Ramírez, (c) 2026