¿Cómo manejar los valores extremos (outliers) en nuestros datos?

En este post les comparto una explicación detallada de qué son, cómo detectar y cómo manejar los valores extremos en un proyecto de Ciencia de Datos o Machine Learning.

¡Así que listo, comencemos!

Video

Como siempre, en el canal de YouTube se encuentra el video de este post:

Introducción

El manejo de valores extremos (outliers) es una de las fases fundamentales en prácticamente cualquier proyecto de Ciencia de Datos o Machine Learning.

Así que en este artículo veremos inicialmente una definición de lo que es un valor extremo; posteriormente veremos qué factores dan origen a este tipo de valores, así como las técnicas más usadas para su detección y su manejo.

Al final del artículo encontrarás las instrucciones para descargar el código fuente y el set de datos usados en este artículo.

¿Qué es un valor extremo?

Según el libro Applied Predictive Modeling (2013) los valores extremos “son muestras en nuestro set de datos que se encuentran excepcionalmente alejadas de la mayor parte de los datos.”

Entendamos este significado con un par de ejemplos.

Analicemos primero el set de datos correspondientes a las alturas de un grupo de 25.000 sujetos. Podemos dibujar este set de datos con las siguientes líneas de código:

fig = px.scatter(df_alturas, y='Altura (cm)', width=1200, height=350)
fig.show()

En este primer caso vemos que la mayor parte de los sujetos tiene alturas que oscilan en el rango de 160 a 185 cm aproximadamente. Sin embargo, vemos además que algunos sujetos tienen alturas bastante alejadas de dicho rango, con valores de 135 cm en algunos casos y de 198 cm en otros. Estos son precisamente ejemplos de valores extremos.

Ahora analicemos un segundo ejemplo: el set de datos correspondiente al monto de 1.300 transacciones realizadas por diferentes clientes de una entidad bancaria:

fig = px.scatter(df_transacc, y='Transacciones (USD)', width=1200, height=350)
fig.show()

En este caso observamos que el rango de valores para la mayoría de las transacciones está entre aproximadamente 10 y 330 dólares. Sin embargo encontramos algunos valores atípicos (los outliers) de por ejemplo 1.923 y 213 dólares, e incluso una transacción con un monto negativo de -217 dólares.

En este último ejemplo vemos que un valor extremo puede estar por encima o por debajo del rango de valor típico encontrado en nuestro set de datos.

¿Qué da origen a los outliers?

Esencialmente existen dos motivos que explican la aparición de valores extremos en un set de datos: porque hay errores en la captura de los datos o porque realmente el dato observado es un valor extremo.

Veamos en detalle cada uno de estos motivos.

Errores en la captura

El primero es debido a un error en la captura de los datos, error que puede ser humano o debido al dispositivo usado durante la adquisición.

Por ejemplo, si al momento de recolectar los datos se requiere que estos sean registrados por un ser humano es probable que al momento de hacerlo se hayan cometido errores involuntarios y los valores de ciertos datos serán incorrectos.

Por otra parte también es posible que si usamos un dispositivo para adquirir los datos (como por ejemplo un sensor) este pueda tener un fallo que hará que momentáneamente los datos registrados tengan valores extremos.

Porque realmente el dato observado es un valor extremo

También es probable que en lugar de errores en la adquisición encontremos valores extremos simplemente porque se trata realmente del comportamiento que tienen nuestros datos.

Un ejemplo es el set de datos de transacciones bancarias que analizamos hace un momento: si por ejemplo a la entidad bancaria le interesa detectar transacciones fraudulentas realizadas con tarjetas de crédito, es posible que en ocasiones los montos de dichas transacciones se alejen de los rangos típicos y tengan valores más altos.

En este tipo de situaciones nos interesa preservar estos valores atípicos pues se trata de anomalías que contienen información relevante del comportamiento de nuestros datos.

Otro ejemplo es por ejemplo la detección de anomalías en sistemas de diagnóstico, en el área de la salud. En estos casos nos interesa precisamente capturar los datos asociados a variables fisiológicas que se encuentran por fuera del rango normal, pues esto puede ser un indicador de un paciente con una enfermedad determinada.

En el tutorial para la detección de anomalías cardiacas con autoencoders podemos ver precisamente cómo estas anomalías son valores extremos que permiten clasificar a un paciente como “sano” o “enfermo”.

¿Cómo detectar los valores extremos?

Muy bien teniendo claro qué son y las situaciones que dan origen a valores extremos en nuestro set de datos, en esta sección nos enfocaremos en las técnicas más usadas para su detección.

Esencialmente existen tres métodos: visualización, uso de la desviación estándar y uso del rango intercuartiles. Veamos en detalle cada una de estas técnicas.

Detección de valores extremos usando visualización de los datos

Es la técnica más sencilla de todas aunque no siempre es la más efectiva. Consiste simplemente en generar una gráfica de nuestro set de datos y detectar posteriormente los outliers por simple inspección visual.

Esto lo vimos en los ejemplos analizados anteriormente para los sets de datos de alturas y de transacciones bancarias.

Sin embargo esta técnica no siempre funciona. Si por ejemplo hacemos una gráfica del set de datos correspondiente a los salarios de un grupo de empleados de una compañía en Estados Unidos:

fig = px.scatter(df_salarios, y='Salarios (USD)', width=1200, height=350)
fig.show()

observamos que debido a la distribución de los datos no resulta sencillo definir claramente cuáles son los valores extremos.

Cuando las herramientas visuales no son suficientes, se pueden usar herramientas estadísticas que combinadas con visualización permiten ver la distribución de los datos.

Detección de valores extremos usando el método de la desviación estándar

Este método se puede usar cuando los datos tienen una distribución normal (forma de campana Gaussiana), para lo cual se pueden aplicar diferentes pruebas estadísticas que permiten realizar esta comprobación.

La distribución Gaussiana se caracteriza por la media (𝜇) y la desviación estándar (𝜎). Conociendo los parámetros de esta distribución podemos establecer una serie de umbrales dentro de los cuales se encontrarán distriubidos nuestros datos, así:

Así que si, por ejemplo, tomamos como criterio el rango 𝜇±3𝜎 (donde se encuentra la gran mayoría de los datos), podemos etiquetar a un dato como valor extremo si se encuentra por fuera de este rango.

Por ejemplo, si dibujamos el histograma del set de datos de edades:

fig = px.histogram(df_alturas, x='Altura (cm)', width=800, height=400)
fig.show()

podemos comprobar que tiene una distribución normal. Si a esta gráfica superponemos la media y los límites inferior (-3𝜎) y superior (+3𝜎):

# Calcular media y límites
mean_alturas = df_alturas.mean()[0]
std_alturas = df_alturas.std()[0]
lim_inf = mean_alturas - 3*std_alturas
lim_sup = mean_alturas + 3*std_alturas

# Dibujar distribución y superponer los límites
fig = px.histogram(df_alturas, x='Altura (cm)', width=800, height=400)
fig.add_vline(x=mean_alturas, line_color='black', annotation_text='$\mu$')
fig.add_vline(x=lim_inf, line_color='red', annotation_text='$\mu-3\sigma$')
fig.add_vline(x=lim_sup, line_color='red', annotation_text='$\mu+3\sigma$')
fig.show()

podemos verificar que efectivamente datos correspondientes a alturas por debajo de 158 cm (límite inferior) o superiores a 186 cm (límite superior) pueden ser catalogados como outliers.

Detección de valores extremos usando el método del rango intercuartiles

Este método se puede usar cuando los datos no tienen una distribución normal, como por ejemplo es el caso del set de datos con los salarios.

Aunque de este método ya hablamos cuando discutimos el análisis exploratorio de datos, vale la pena recordar las ideas más importantes.

En primer lugar recodemos el concepto de percentil que es una cantidad que divide nuestro set de datos en 100 partes iguales de acuerdo a su rango de valores.

Sin embargo, para la detección de valores extremos, nos interesa tener en cuenta sólo algunos percentiles:

Si por ejemplo calculamos y dibujamos estos tres cuartiles para el set de datos de salarios:

# Calcular los cuartiles 1 (25%), 2 (50%) y 3 (75%) para el set de datos de salarios
qs = df_salarios.quantile([0.25,0.5,0.75]).values
q1 = qs[0][0]
q2 = qs[1][0]
q3 = qs[2][0]

# Y dibujarlos superpuestos a los datos
fig = px.scatter(df_salarios, y='Salarios (USD)', width=1200, height=350)
fig.add_hline(y=q2, line_color='black', annotation_text='$q_{2}$')
fig.add_hline(y=q1, line_color='red', annotation_text='$q_{1}$')
fig.add_hline(y=q3, line_color='red', annotation_text='$q_{3}$')
fig.show()

veremos que alcanzan valores de 65.800 (cuartil 1), 87.100 (cuartil 2) y 115.200 dólares (cuartil 3).

Teniendo claro el concepto de cuartiles, ahora sí podemos definir el rango intercuartiles (IQR). Este rango es simplemente la resta entre el cuartil 3 y el cuartil 1.

Por ejemplo, para el ejemplo del set de datos de salarios, este rango intercuartiles será igual a 115.200 - 65.800 = 49.400.

Por cubrir el rango que va del cuartil 1 al cuartil 3, este rango intercuartiles define los límites dentro de los cuales se encuentra el 50% de los datos.

Finalmente, habiendo definido qué es el IQR podemos usarlo como criterio para determinar si un dato es o no un valor extremo.

De forma similar a lo mencionado en el método de la desviación estándar, en este caso se definen también dos límites (inferior y superior):

donde k es un valor que usualmente es igual a 1.5.

Gráficamente podemos visualizar los cuartiles y los límites superior e inferior usando lo que se conoce como un box plot (o gráfico de caja):

fig = px.box(df_salarios, y="Salarios (USD)", width = 600, height=400)
fig.show()

Generalmente este tipo de gráficos, además de mostrar dichos cuartiles y los límites nos permite visualizar los datos extremos, que son simplemente los que se encuentran por fuera de los límites superior e inferior.

¿Cómo manejar los valores extremos?

En este punto ya tenemos claro el concepto de valores extremos y los métodos más usados para su detección.

Pero entonces ¿qué debemos hacer con estos valores cuando desarrollamos un proyecto en Ciencia de Datos o Machine Learning?

Antes de responder a esta pregunta debemos tener en cuenta una condición previa: debemos tener experticia en el dominio.

Esto quiere decir que antes de tomar cualquier decisión acerca del manejo que daremos a los outliers, es fundamental que entendamos claramente cómo fueron adquiridos los datos, los objetivos de nuestro proyecto y el campo de aplicación del mismo, pues todos estos elementos nos ayudarán en el proceso de toma de decisiones para de esta forma dar el manejo más adecuado a estos valores extremos.

Una vez cumplido este requisito, pgeneralmente podemos probar uno de los cuatro enfoques descritos a continuación.

Método de eliminación (trimming)

Como su nombre lo indica, con este método simplemente se elimina el valor extremo de nuestro set de datos, dependiendo del criterio de detección usado.

Por ejemplo, podemos usar el criterio de la desviación estándar para eliminar valores extremos del set de datos correspondiente a las alturas:

# Eliminar datos del set "Alturas" usando el criterio de desviación estándar
df_alturas_trimmed = df_alturas[
    (df_alturas['Altura (cm)']>=lim_inf) & (df_alturas['Altura (cm)']<=lim_sup)
]

print(f'Tamaño dataset original: {df_alturas.shape}')
print(f'Tamaño dataset "trimmed": {df_alturas_trimmed.shape}')

# Dibujar las dos distribuciones
fig = px.scatter(df_alturas, y='Altura (cm)', width=900, height=300,
                 title = 'Dataset original')
fig.show()

fig = px.scatter(df_alturas_trimmed, y='Altura (cm)', width=900, height=300,
                 title = 'Dataset "trimmed"')
fig.show()

Sin embargo, debemos tener en cuenta que este método presenta dos desventajas:

Método de recorte (capping)

En este método lo que se hace es cambiar el valor de los outliers a los límites inferior y superior encontrados en la etapa de detección.

Por ejemplo, podemos usar este método de recorte en el set de datos de salarios y usando los límites encontrados usando el método del rango intercuartiles:

# Definir límites inferior y superior
iqr = q3-q1
iqr_lim_inf = q1 - 1.5*iqr
iqr_lim_sup = q3 + 1.5*iqr

# Y realizar el recorte
df_salarios['Capped'] = df_salarios['Salarios (USD)']
df_salarios['Capped'] = np.where(df_salarios['Capped']>iqr_lim_sup,
                                 iqr_lim_sup, df_salarios['Capped'])
df_salarios['Capped'] = np.where(df_salarios['Capped']<iqr_lim_inf,
                                 iqr_lim_inf, df_salarios['Capped'])

# Dibujar boxplots original y "capped"
fig = px.box(df_salarios, width = 600, height=400)
fig.show()

En este caso los límites superior e inferior calculados son iguales a 189.130 y -8.127 dólares. Como el límite inferior es negativo y el menor valor observado en el salario es cercano a los 50.000 dólares, el recorte se realiza únicamente para valores por encima del límite superior.

Este recorte implica en este caso que los valores extremos por encima del límite superior tendrán ahora valores iguales a 189.130 (columna 'Capped' en el set de datos).

La ventaja del recorte en comparación con la eliminación es que se preserva el tamaño del set de datos.

Sin embargo, la modificación del valor de los outliers implica que en este caso también estaremos modificando la distribución de los datos y de nuevo es importante sopesar las implicaciones que esto puede tener en las etapas posteriores de nuestro proyecto.

Probar con y sin valores extremos

Si estamos realizando un análisis exploratorio de datos o implementando un modelo de Machine Learning podemos intentar analizar el impacto que tendrían los valores extremos en esta fase de desarrollo.

Es decir que podemos optar por realizar dos implementaciones:

En ocasiones resulta útil llevar a cabo esta comparación para determinar si los valores extremos tienen o no un impacto significativo en las etapas posteriores de desarrollo del proyecto.

Conociendo todos los detalles del proyecto y teniendo la experticia en el dominio mencionada anteriormente, podremos de esta forma tomar una decisión de si es mejor preservar o eliminar dichos outliers.

¡No hacer nada!

Como lo mencionamos anteriormente, en ocasiones los valores extremos contienen información importante y resultan esenciales para entender el comportamiento de los datos y para fases posteriores del proyecto (como por ejemplo en el caso de la detección de anomalías).

Así que en estos casos a veces lo mejor es no hacer nada con estos datos: es mejor preservarlos y usarlos como herramienta en la toma de decisiones en el proyecto que estemos desarrollando.

De nuevo, para poder tomar esta decisión el requisito fundamental es nuestra experticia en el dominio.

Resumen: aspectos a tener en cuenta

Para finalizar, es importante que al abordar el manejo de valores extremos en nuestro proyecto tengamos en cuenta estos elementos:

Enlace de descarga del código fuente y del set de datos

Suscríbete a la newsletter mensual de Codificando Bits y recibe el enlace de descarga en tu e-mail:

Conclusión

Muy bien, con lo que acabamos de ver ya tenemos un panorama completo acerca de lo que son los valores extremos y de las diferentes técnicas para su detección y manejo.

Así que espero que estas herramientas resulten útiles en el desarrollo de sus propios proyectos.