Cómo reemplazar valores faltantes (NA) en R: na.omit y na.rm

⚡ Resumen inteligente

El tutorial "Reemplazar valores faltantes en R" explica cómo detectar valores NA, eliminar filas incompletas con `na.omit()` e imputarlas con la media o la mediana mediante `mutate()`. Este tutorial utiliza el conjunto de datos del Titanic, donde tanto la edad como la tarifa presentan datos faltantes.

  • 🔎 Primero la detección: colSums(is.na(df)) cuenta los valores faltantes por columna antes de tomar cualquier decisión sobre cómo manejarlos.
  • 🗑️ Eliminación de filas: La función na.omit() elimina todas las filas que contienen un valor NA, reduciendo los datos del Titanic de 1,309 filas a 1,045.
  • 📐 Imputación media: apply() con na.rm = TRUE calcula la media de la columna, y mutate() con ifelse() la escribe en una nueva columna.
  • 📊 Alternativa mediana: La mediana resiste los valores atípicos, lo que la convierte en la opción más segura para variables asimétricas como la tarifa.
  • Imputación masiva: Las funciones sapply() o across() aplican la misma regla a todas las columnas numéricas en una misma instrucción.
  • ⚠️ Compromiso conocido: La imputación de la media reduce la varianza y debilita las correlaciones, por lo que nunca debe aplicarse a ciegas.

Reemplazar valores faltantes NA en R

¿Qué son los valores faltantes en R?

Los valores faltantes aparecen cuando una observación no tiene un valor registrado en una columna, o cuando un marcador de posición no numérico ocupa el lugar de un número. Deben eliminarse o reemplazarse antes de cualquier cálculo, ya que la mayoría de las funciones de R devuelven NA en cuanto aparece uno.

Este tutorial muestra cómo manejar valores faltantes con la biblioteca dplyr, que forma parte del ecosistema tidyverse para el análisis de datos.

Reemplazar valores faltantes en R

El primer paso siempre es averiguar cuántos valores faltan y dónde.

Cómo detectar y contar valores faltantes en R

Antes de decidir qué hacer con los valores faltantes, es necesario saber cuántos hay y dónde se encuentran. R ofrece cuatro comprobaciones, desde una simple respuesta de sí o no hasta un recuento completo por columna.

# 1. Is there any NA at all?
anyNA(df_titanic)

# 2. How many in total?
sum(is.na(df_titanic))

# 3. How many per column, the most useful view
colSums(is.na(df_titanic))

# 4. Percentage missing per column
round(colMeans(is.na(df_titanic)) * 100, 1)

En la práctica, la función colSums() es la más recomendable. Devuelve un vector con nombre que muestra un valor por columna, lo que indica inmediatamente si a una variable le faltan algunos valores o si está prácticamente vacía.

Contando filas completas. complete.cases() devuelve TRUE para las filas sin ningún valor faltante, lo que le indica de antemano cuántos datos descartaría na.omit():

sum(complete.cases(df_titanic))       # rows that would survive
sum(!complete.cases(df_titanic))      # rows that would be dropped

Una advertencia sobre los marcadores de posición. R solo reconoce NA. Los conjuntos de datos suelen codificar los valores faltantes como una cadena vacía, un espacio, “N/A”, “-” o un número centinela como -99 o 999. Estos pasan desapercibidos en todas las comprobaciones anteriores. Conviértalos al importarlos para que el resto del flujo de trabajo funcione correctamente:

df <- read.csv(PATH, na.strings = c("", " ", "NA", "N/A", "-", "-99"))

Siempre revise el rango de cada columna numérica después de la importación. Una edad de -99 o una tarifa de 9999 son mucho más peligrosas que un valor NA honesto, porque ninguna función le avisará al respecto.

Tipos de datos faltantes: MCAR, MAR y MNAR

El motivo por el que falta un valor determina si es seguro imputarlo. Los estadísticos reconocen tres mecanismos.

  • MCAR, desaparecido completamente al azar. La probabilidad de que falte no está relacionada con nada, observado o no, por ejemplo, un sensor que falla en momentos aleatorios.ping o bien, la imputación de estas filas no introduce ningún sesgo, solo una pérdida de precisión.
  • MAR, falta al azar. La probabilidad depende de otras variables observadas, pero no del valor faltante en sí. Si los pasajeros de mayor edad tenían menos probabilidades de que se registrara su edad, esta es MAR dadas las demás columnas. La imputación que utiliza esas columnas maneja bien este problema.
  • MNAR, falta no aleatoriamente. La probabilidad depende del valor no observado en sí, por ejemplo, que las personas con altos ingresos se nieguen a declarar sus ingresos. Ningún método de imputación puede solucionar esto basándose únicamente en los datos, y la falta de datos en sí misma contiene información valiosa que se puede registrar en una columna de indicadores.

La imputación por la media, tal como se utiliza en este tutorial, solo es válida bajo MCAR y MAR simple. Incluso en esos casos, tiene un coste conocido: rellenar cada hueco con el mismo número reduce la varianza de la columna y debilita su correlación con las demás. Para trabajos serios, utilice un método basado en modelos, como el paquete mice, y mantenga siempre una columna indicadora que señale qué valores se imputaron.

mudar()

mutate() es el dplyr verbo que crea una nueva variable o sobrescribe una existente, lo que lo convierte en la herramienta natural para crear una copia limpia de una columna.

Procederemos en dos partes. Aprenderemos a:

  • excluir valores faltantes de un marco de datos
  • imputar los valores faltantes con la media y la mediana

El verbo mutate() es muy fácil de usar. Podemos crear una nueva variable siguiendo esta sintaxis:

mutate(df, name_variable_1 = condition, ...)
arguments:
-df: Data frame used to create a new variable
-name_variable_1: Name and the formula to create the new variable
-...: No limit constraint. Possibility to create more than one variable inside mutate()

Excluir valores faltantes (NA)

`na.omit()` es una función base de R, no un verbo de dplyr, pero funciona perfectamente con tuberías. Elimina todas las filas que contienen al menos un valor NA. Esta es la opción más rápida, pero rara vez la mejor, ya que un solo valor faltante descarta toda la observación.

Para abordar el problema de las observaciones faltantes, utilizaremos el conjunto de datos del Titanic. En este conjunto de datos, tenemos acceso a la información de los pasajeros a bordo durante la tragedia. Este conjunto de datos tiene muchas NA que deben tenerse en cuenta.

Cargue el archivo CSV desde Internet y luego enumere las columnas que contienen NA:

PATH <- "https://raw.githubusercontent.com/guru99-edu/R-Programming/master/test.csv"
df_titanic <- read.csv(PATH, sep = ",")
# Return the column names containing missing observations
list_na <- colnames(df_titanic)[ apply(df_titanic, 2, anyNA) ]
list_na

Salida:

## [1] "age"  "fare"

Aquí,

colnames(df_titanic)[apply(df_titanic, 2, anyNA)]

Devuelve los nombres de las columnas que contienen al menos un valor faltante.

A las columnas edad y tarifa les faltan valores.

Podemos eliminarlos con na.omit().

library(dplyr)
# Exclude the missing observations
df_titanic_drop <-df_titanic %>%
na.omit()		
dim(df_titanic_drop)

Salida:

## [1] 1045   13

El nuevo conjunto de datos contiene 1045 filas en comparación con las 1309 del conjunto de datos original.

Excluir valores faltantes

Imputar los datos faltantes con la media y la mediana.

También se pueden imputar, es decir, rellenar, los valores faltantes con la media o la mediana. Una buena práctica es crear dos variables separadas para la media y la mediana. Una vez creadas, podemos reemplazar los valores faltantes con las nuevas variables.

Usaremos el método de aplicación para calcular la media de la columna con NA. Veamos un ejemplo

Paso 1) Anteriormente en el tutorial, almacenamos el nombre de las columnas con los valores faltantes en la lista llamada list_na. Usaremos esta lista

Paso 2) Calcula la media con el argumento na.rm = TRUE. Este argumento es obligatorio porque las columnas tienen datos faltantes, y esto le indica a R que los ignore.

# Create mean
average_missing <- apply(df_titanic[,colnames(df_titanic) %in% list_na],
      2,
      mean,
      na.rm =  TRUE)
average_missing

Code Explicación:

Pasamos 4 argumentos en el método de aplicación.

  • df: df_titanic[,colnames(df_titanic) %in% list_na]. Este código devolverá el nombre de las columnas del objeto list_na (es decir, "edad" y "tarifa")
  • 2: Calcular la función en las columnas.
  • media: Calcular la media
  • na.rm = VERDADERO: ignorar los valores faltantes

Salida:

##      age     fare 
## 29.88113 33.29548

Creamos con éxito la media de las columnas que contienen observaciones faltantes. Estos dos valores se utilizarán para reemplazar las observaciones faltantes.

Paso 3) Reemplazar los valores de NA

El verbo mutar de la biblioteca dplyr es útil para crear una nueva variable. No necesariamente queremos cambiar la columna original para poder crear una nueva variable sin NA. mutate es fácil de usar, simplemente elegimos un nombre de variable y definimos cómo crear esta variable. Aquí está el código completo.

# Create a new variable with the mean and median
df_titanic_replace <- df_titanic %>%
   mutate(replace_mean_age  = ifelse(is.na(age), average_missing[1], age),
   replace_mean_fare = ifelse(is.na(fare), average_missing[2], fare))

Code Explicación:

Creamos dos variables, replace_mean_age y replace_mean_fare de la siguiente manera:

  • reemplazar_edad_media = ifelse(is.na(edad), falta_promedio[1], edad)
  • reemplazar_tarifa_media = ifelse(is.na(tarifa), promedio_faltante[2],tarifa)

Si a la columna edad le faltan valores, reemplácela con el primer elemento de Average_missing (media de edad); de lo contrario, mantenga los valores originales. Misma lógica para la tarifa.

sum(is.na(df_titanic_replace$age))

Salida:

## [1] 263

Tras la sustitución, la nueva columna no contiene ningún valor faltante:

sum(is.na(df_titanic_replace$replace_mean_age))

Salida:

## [1] 0

La columna de edad original contiene 263 valores faltantes, mientras que la nueva columna replace_mean_age los ha rellenado todos con la edad media.

Paso 4) También podemos reemplazar las observaciones faltantes con la mediana.

median_missing <- apply(df_titanic[,colnames(df_titanic) %in% list_na],
      2,
      median,
      na.rm =  TRUE)
df_titanic_replace <- df_titanic %>%
            mutate(replace_median_age  = ifelse(is.na(age), median_missing[1], age), 
            replace_median_fare = ifelse(is.na(fare), median_missing[2], fare))
head(df_titanic_replace)

Salida:

Imputar datos faltantes con la media y la mediana

Paso 5) En conjuntos de datos extensos, el método paso a paso se vuelve tedioso. La función sapply() simplifica todo el procedimiento a una sola instrucción, aunque a costa de no visualizar nunca los valores imputados.

sapply no crea un marco de datos, por lo que podemos envolver la función sapply() dentro de data.frame() para crear un objeto de marco de datos.

# Quick code to replace missing values with the mean
df_titanic_impute_mean <- data.frame(
    sapply(
        df_titanic,
        function(x) ifelse(is.na(x),
            mean(x, na.rm = TRUE),
            x)))

Imputación moderna con replace_na() y across()

Los métodos apply() y sapply() descritos anteriormente siguen funcionando, pero tidyr y dplyr ahora expresan las mismas operaciones de forma más segura y legible.

replace_na() para valores fijos. tidyr::replace_na() toma una lista con nombre de columnas y sus reemplazos:

library(tidyr)

df_titanic %>%
    replace_na(list(age = 29.88, fare = 33.30))

a través() para cada columna numérica. Este es el reemplazo directo y seguro de tipos para la línea de código sapply(), y a diferencia de sapply(), nunca toca columnas de caracteres o factores:

library(dplyr)

# Mean-impute every numeric column
df_titanic %>%
    mutate(across(where(is.numeric),
                  ~ ifelse(is.na(.x), mean(.x, na.rm = TRUE), .x)))

# Median-impute instead
df_titanic %>%
    mutate(across(where(is.numeric),
                  ~ ifelse(is.na(.x), median(.x, na.rm = TRUE), .x)))

⚠️ Por qué el atajo sapply() es arriesgado: El ejemplo de una sola línea sapply() se ejecuta todas columna, incluyendo caracteres y factores. Llamar a mean() en esas columnas devuelve NA con una advertencia, y sapply() convierte todo el resultado a caracteres. La versión across(where(is.numeric), …) anterior evita esto por completo.

coalesce() para columnas de reserva. Cuando una segunda columna puede proporcionar el valor faltante, coalesce() devuelve la primera entrada no faltante entre sus argumentos:

df %>% mutate(age_final = coalesce(age, age_estimated, 0))

Lleva un registro de los cambios que hayas realizado. Agregue una bandera antes de la imputación, para que cualquier modelo posterior pueda aprender del hecho de que faltaba un valor:

df_titanic %>%
    mutate(age_was_missing = is.na(age),
           age = ifelse(is.na(age), mean(age, na.rm = TRUE), age))

Manejo de valores faltantes en R: Referencia del método

En este tutorial se abordan tres enfoques:

  • Excluir todas las observaciones faltantes.
  • Imputar con la media
  • Imputar con la mediana

La siguiente tabla resume la detección y eliminación:

Biblioteca Objetivo Code
bases Listar observaciones faltantes
colnames(df)[apply(df, 2, anyNA)]
bases Eliminar todas las filas que contengan NA
na.omit(df)

La imputación de media o mediana se puede realizar de dos formas.

  • Usando aplicar
  • usando sapply
Método Detalles Ventajas Desventajas
Paso a paso con aplicar Verifique las columnas que faltan, calcule la media/mediana, almacene el valor, reemplace con mutate() Puedes ver la media o mediana imputada. Más tiempo de ejecución. Puede ser lento con un gran conjunto de datos
Manera rápida con sapply Utilice sapply() y data.frame() para buscar y reemplazar automáticamente los valores faltantes con media/mediana Código corto y rápido. Los valores imputados nunca se muestran.

Preguntas Frecuentes

NA indica un valor faltante en un vector. NULL representa la ausencia de un objeto y tiene longitud cero. NaN es el resultado de una operación numérica no definida, como dividir cero entre cero.

Utilice la mediana para variables asimétricas como tarifas o ingresos, ya que los valores atípicos elevan la media. Utilice la media solo cuando la columna sea aproximadamente simétrica y no contenga valores extremos.

Rellenar todos los huecos con el mismo valor reduce la varianza de la columna y debilita su correlación con otras variables. Los métodos basados ​​en modelos, como el paquete mice, preservan mucho mejor esas relaciones.

La mayoría de los algoritmos no aceptan valores NA y generarán errores o descartarán filas silenciosamente. Una imputación descuidada filtra información entre los conjuntos de entrenamiento y prueba, por lo que siempre se deben calcular los valores de imputación únicamente en la división de entrenamiento.

Sí. Los asistentes de IA pueden sugerir métodos basados ​​en el patrón de datos faltantes y generar el código dplyr. Verifique la elección comparándola con el resultado de colSums(is.na()) y con su conocimiento del dominio sobre por qué faltan datos.

Resumir este post con: