Mar 3 / Sebastián Garrido de Sierra

?R | #6 - NAs: Guía básica, parte 3

Al final del número de la semana pasada mencioné que la mayor parte de las estrategias que existen para lidiar con NAs pueden ser agrupadas en cuatro grandes categorías:

  1. Omitir temporalmente observaciones con valores faltantes
  2. Eliminar permanentemente renglones o columnas con uno o más NAs
  3. Reemplazar NAs por otro valor (0s, el promedio de la variable, etc.)
  4. Obtener más información

En esta entrega exploro las primeras dos y la próxima semana concluiré esta guía cubriendo las dos restantes (lo sé, lo sé, mi plan era terminar esta semana, pero... ¡hay tanto que decir!).

Antes de arrancar, quiero enfatizar tres puntos.

Primero, todos los métodos que existen para trabajar con valores faltantes tienen ventajas, desventajas y son falibles. Debes usarlos con conocimiento de causa y precaución.

Segundo, el uso de estos métodos está condicionado en buena medida por la naturaleza del tipo de valores faltantes que tienes enfrente (por eso me clavé tanto en el tema la semana pasada).

Tercero, sea cual sea el método que elijas, es una buena práctica que se lo comuniques explícita y transparentemente a tu audiencia. Esto le permitirá estar al tanto del reto que enfrentaste, así como tener elementos para valorar tus decisiones.

A darle.

💡 Tips

1)  ⚗️ OMITIR TEMPORALMENTE OBSERVACIONES CON VALORES FALTANTES

Hace un par de semanas, al iniciar esta guía básica, mencioné que los valores faltantes son contagiosos en R.

Para entender mejor a qué me refiero, utilizaré la siguiente mini base de datos:
Puedes construirla con los siguientes bloques de código:
Si después de construir el objeto bd_nas intentas calcular la media de cualquiera de sus primeras cinco variables usando la función mean(), obtendrás como resultado el valor NA.

Puedes confirmarlo copiando y ejecutando las siguientes líneas de código:
Si lo haces, deberías ver algo como esto en tu consola:
Esto no se debe ni se limita a la función mean().

Puedes comprobarlo copiando y ejecutando estos otros ejemplos de código:
En todo los casos, el "problema" radica en que R encuentra al menos un valor faltante en la o las columnas que le indicamos que usara, y como no sabe cuál es su verdadero valor, entonces concluye que el resultado debe ser NA.

Dada lo común que es este "problema", todas las funciones que usé hasta ahora, y varias más que enlisto en los recursos de esta semana, incluyen un argumento para indicarle a R que deseamos omitir los valores faltantes...
  • De forma temporal; y,
  • Exclusivamente para el cálculo en cuestión.
En el caso de mean(), tenemos que modificar el valor del argumento na.rm para que sea TRUE en lugar de FALSE (el default).

Los siguientes chunks de código ilustran cómo hacerlo:
Si los copias, pegas y ejecutas, verás los siguientes resultados:
En términos formales, esta solución se conoce como eliminación por pares o pairwise deletion y consiste en calcular lo que te interesa (p. ej., la media, varianza o coeficiente de correlación) usando únicamente los valores que sí están disponibles.

El siguiente bloque ofrece otros ejemplos de código de funciones que ofrecen la opción de pairwise deletion:
Esta solución es sencilla y práctica, pero tiene al menos dos riesgos.

Primero, puede generarnos una falsa sensación de que "resolvimos" el problema.

Para entender mejor por qué, echa un ojo a var_4 en el tibble bd_nas:
Si calculamos su media usando na.rm = TRUE obtendremos como resultado el número 10.

¡Pero ocho de los nueve valores de esta variable son faltantes!

¿De qué nos sirve calcular la media de una variable a partir de un solo valor?

El problema está lejos de ser "resuelto".

Y si bien construí esta variable para ilustrar un caso extremo, esto nos lleva a un terreno complicado: ¿qué tanto es tantito?

Es decir, ¿a partir de qué porcentaje de valores faltantes en una variable debemos preocuparnos al usar na.rm = TRUE o equivalentes?

De acuerdo con Little y Rubin, no hay una regla clara, pues la decisión no sólo depende del porcentaje de valores faltantes, sino también de su naturaleza.

Ya sé... No es lo que esperabas leer.


El
segundo riesgo potencial de usar la eliminación por pares es que no siempre es adecuada.

Concentrémonos en la var_5 del tibble bd_nas:
Ésta tiene tres valores faltantes, cifra parecida a las dos en var_1, var_2 y var_3.

Sin embargo, hay una diferencia importante.

Como ilustra el recuadro rojo en la imagen de arriba, los tres valores faltantes de var_5 están correlacionados con el valor Grupo B de la var_7, lo que sugiere al menos dos cosas:
  • La naturaleza de los valores faltantes de var_5 es de tipo aleatorio (acá puedes repasar por qué)
  • Y si calculamos su media usando na.rm = TRUE, lo haremos sin considerar los valores de las observaciones correspondientes al Grupo B
Esto último puede introducir un sesgo importante en nuestras estimaciones 😒.

La próxima semana incluiré algunas recomendaciones respecto al tipo de solución que es recomendable dada la naturaleza de los valores faltantes.

Por ahora te adelanto que la eliminación por pares es una alternativa adecuada si los valores faltantes en tu base de datos son completamente aleatorios... cosa que, como comenté la semana pasada, no es muy común.

2) 🔪 ELIMINAR RENGLONES CON UNO O MÁS NAs

El segundo conjunto de alternativas para lidiar con NAs es más drástico (y potencialmente costoso) que omitir temporalmente los valores faltantes de una o más variables.

Implica eliminar por completo y de forma permanente, los renglones que contienen al menos un valor faltante.

Esta solución suele llamarse eliminación por lista o listwise deletion.

Mientras que ésta es la solución que adoptan por default otros programas (les estoy hablando a ustedes, SPSS y Stata) cuando se topan con uno o más NAs en un renglón, en R tenemos que implementarla explícitamente en la mayoría de las ocasiones.

Partiendo del tibble bd_nas, los siguientes bloques de código ofrecen dos opciones para eliminar todos los renglones con al menos un NA:
Si ejecutas cualquiera de estas líneas obtendrás el siguiente resultado:
Pasamos de un tibble con nueve observaciones a otro con un sólo renglón 🤯.

Cierto, diseñé el tibble bd_nas para que al llegar a este punto viéramos un ejemplo extremo de lo que puede ocurrir si usamos la eliminación por lista de renglones.

Pero este resultado es útil para ilustrar al menos tres desventajas de usar esta alternativa:

  1. Trata a todos los renglones de la misma forma, sin importar si contienen uno, cinco o 10 valores faltantes;
  2. Si la base de datos registra información de períodos consecutivos de tiempo, eliminar uno o más de sus renglones provocará que se interrumpa la serie; y,
  3. Es probable que en el proceso perdamos muchas observaciones (en este famoso paper, King & Co. sugieren que la cifra puede oscilar entre 30% y 90%)


Detengámonos un momento en esto último. ¿Cuáles podrían ser las implicaciones?

Si calculamos la media de var_1 con la versión original de bd_nas omitiendo temporalmente los valores faltantes, R usará siete observaciones (78% del total) y arrojará el valor 47.28571.

Pero si lo hacemos con la versión de bd_nas que obtenemos después de eliminar todos los renglones con al menos un valor faltante, R sólo tendrá una observación para trabajar (11% del total) y el valor de la misma, 34, será el de la media.

Puedes comprobando ejecutando los siguientes bloques de código:
En este caso en particular, la eliminación por lista de renglones provoca que perdamos mucha información valiosa que sí tenemos y deberíamos aprovechar.

Este problema se mitiga al trabajar con bases de datos más grandes.

Pero incluso si nuestra base original tuviera 1 millón de observaciones y después de eliminar los renglones con NAs nos quedamos con la mitad, si la naturaleza de los valores faltantes no es completamente aleatoria, la eliminación por lista puede sesgar las estimaciones que hagamos con la información disponible.

3) 🗡️ ELIMINAR COLUMNAS CON UNO O MÁS NAs

Como probablemente estás pensando, así como podemos eliminar los renglones que contienen uno o más valores faltantes, podemos hacer lo mismo con las columnas.

A continuación encontrarás dos opciones para realizar este procedimiento:
Sin importar cuál uses, el tibble resultante se verá así:
En este ejemplo pasamos de un tibble con siete columnas a uno de dos.

De nuevo, si bien es un caso extremo, ilustra que la primera y tercera desventajas que mencioné antes para la eliminación por lista de renglones, también podrían presentarse cuando usamos este método para las columnas.

Y a estos dos problemas, se suma uno más.

Las columnas representan variables.

Éstas suelen ser clasificadas como de respuesta (o dependientes) y explicativas (o independientes).

Si usamos este método para lidiar con los valores faltantes en nuestras columnas, y nuestra variable de respuesta (lo que queremos explicar) o nuestra principal variable explicativa contienen al menos un valor faltante, terminaríamos borrándolas.

Lo cual, por decir lo menos, es un resultado subóptimo 🫠.

-----

Si la omisión temporal de valores faltantes y la eliminación permanente de renglones y/o columnas presentan todas estas limitaciones y riesgos...

¿Qué hacemos entonces?

Mi punto no es que nunca debas optar por estas soluciones, sino que cuando la uses, lo hagas con precaución y pleno conocimiento de sus posibles implicaciones.

Y que si concluyes que estas alternativas no son adecuadas dada la naturaleza de tu análisis y los valores faltantes que tiene tu base de datos, explores si la solución radica en reemplazar los NAs por otros valores.

¿Juuuuat? 😮

La próxima semana te contaré de esta tercera familia de soluciones.

Por ahora, paso a los recursos de la semana.

¿Te gustaría recibir nuestro newsletter cada semana en tu correo?

Compártenos tu info y te lo mandamos 🤓.

Nos importa tu privacidad.
Averigua
aquí qué hacemos para protegerla.
Muchas gracias 🙂

En los próximos minutos recibirás un correo de Sebastián de Data Crunchers

Si no lo encuentras en la bandeja de entrada, por favor revisa las carpetas de Spam y Social (o equivalentes).

⚡️ Recursos

1) FUNCIONES QUE INCLUYEN ARGUMENTOS PARA OMITIR LOS NAs

A continuación encontrarás las funciones que sé que incluyen un argumento para omitir temporalmente los valores faltantes:
Seguramente la lista está incompleta.

Si conoces alguna otra, ¿me la compartes, por fa?

2) 📦 {finalfit}

Como seguramente habrás notado por mi insistencia en el tema (*basta, Segasi, basta 😒*), un paso fundamental para determinar cómo debemos lidiar con los valores faltantes en nuestras bases de datos consiste en descifrar su naturaleza.

La función missing_pairs() del paquete {finalfit} (página) está diseñada para ayudarnos a entender si los valores faltantes en nuestra base son completamente aleatorios o aleatorios.

La siguiente imagen...
... se genera al ejecutar estas líneas de código:
La interpretación de esta gráfica no es trivial.

Pero dado que este número de ?R ya es larguísimo, moví mi explicación de la misma a la posdata 👇.

3) PRUEBAS PARA ENTENDER MEJOR LA NATURALEZA DE LOS NAs

Además de visualizaciones, existen diversas pruebas estadísticas que nos permiten analizar la naturaleza de los valores faltantes incluidos en nuestra base de datos.

Aquí te cuento de dos.

Mientras que el paquete {naniar} ofrece la función mcar_test() (página) para realizar la prueba MCAR de Little, la función mcar() del paquete {mice} (página) nos permite realizar la prueba propuesta por Jamshidian y Jalal.


Termino aquí, colega.

Gracias por leerme.

Saludos y prometo que, ahora sí, la próxima semana terminamos con esta guía básica de valores faltantes.

~ Sebastián

Empty space, drag to resize
Postdata

Van algunas ideas para interpretar la visualización que genera la función finalfit::missing_pairs().

Primero, el nombre en la parte superior de cada columna corresponde a la variable visualizada en la misma. Todas las gráficas en una columna son de la misma variable. En este ejemplo, la primera columna contiene las gráficas correspondientes a var_1, la siguiente las de var_2 y así sucesivamente.

Segundo, si la variable es numérica, entonces la columna incluirá boxplots o diagramas de caja para representar su distribución. Si la variable es categórica, como en var_7, la columna incluirá gráficas de barra.

Tercero, en el eje vertical aparecen de nueva cuenta los nombres de las variables con el término missing entre paréntesis. El primer renglón corresponde a cuando faltan valores en var_1, el siguiente a cuando ocurre en var_2 y así sucesivamente.

Cuarto, si una de las variables enlistadas en el eje vertical tiene uno o más valores faltantes, aparecen dos boxplots (uno azul y otro gris) y las barras tienen dos regiones.

Quinto, si la variable en el eje vertical no tiene valores faltantes (p. ej., var_6 y var_7), sólo habrá un boxplot en cada celda y las barras serán completamente azules.

Paso ahora a la interpretación.

Me concentro primero en la celda que está en el primer renglón y la segunda columna. Ahí vemos dos boxplots; ambos fueron construidos a partir de los valores de var_2.

La diferencia es que mientras que el boxplot azul representa la distribución de esta variable para las observaciones que sí tienen un valor definido en var_1 (todas excepto las que están en los renglones 5 y 9 de bd_nas), el boxplot gris muestra la distribución de var_2 para las observaciones que tienen valores faltantes en var_1 (renglones 5 y 9).

La comparación de estos dos boxplots nos ofrece información valiosa para entender si existe alguna relación entre los valores faltantes de var_1 y los valores de var_2. En este caso parece que es así, pues el boxplot gris se concentra en valores más bajos que el boxplot azul.

Me muevo ahora a la gráfica de barras que está en el renglón 5 y la columna 7. La altura de cada una de las barras corresponde al número de veces que se repiten los valores  Grupo A, Grupo B y Grupo C  en var_71 (tres en cada caso).

La columna de en medio, correspondiente a Grupo B, está completamente gris, porque las tres observaciones que tienen este valor en var_7 (renglones 4, 5 y 6 en bd_nas), tienen valores faltantes en var_5.

Ojo: al interpretar estos resultados para bd_nas, vale la pena hacerlo con un kilo de sal, pues cada variable en bd_nas sólo tiene nueve valores, y muchos de los boxplots se generaron a partir de la información de uno o dos de ellos.