Saltar a contenido

Detección y tratamiento de valores atípicos

1 ¿Qué son los valores atípicos?

Un valor atípico (outlier) es un dato que se desvía significativamente del resto de observaciones. No son necesariamente errores: pueden ser variaciones legítimas, pero distorsionan estadísticas, escalado y el rendimiento de los modelos.

1.1 ¿Por qué importan?

  • Sesgan la media y la desviación estándar, afectando la estandarización.
  • Empeoran la regresión lineal, que minimiza errores cuadráticos (un outlier lejano tiene un efecto desproporcionado).
  • Confunden KNN y SVM, que usan distancias.
  • Pueden ser ruido que el modelo memoriza en lugar de patrones reales.

Nota

No siempre hay que eliminar outliers. En fraude bancario, los outliers son la señal. En detección de anomalías, los outliers son exactamente lo que buscas. El contexto manda.

2 Detección de outliers

2.1 Método 1: Regla de las 3 desviaciones estándar

Si los datos siguen una distribución aproximadamente normal, el 99.7% de los valores están dentro de \(\mu \pm 3\sigma\). Los valores fuera de ese rango son atípicos.

\[ \text{Outlier si } |x - \mu| > 3\sigma \]
import numpy as np
import pandas as pd

np.random.seed(42)
datos = np.concatenate([np.random.normal(50, 10, 100), [150, -30, 200]])
df = pd.DataFrame({'valor': datos})

media = df['valor'].mean()
std = df['valor'].std()

outliers_zscore = df[(np.abs(df['valor'] - media) > 3 * std)]
print(f"Outliers detectados: {len(outliers_zscore)}")
print(outliers_zscore)

Advertencia

Suposición fuerte: Asume distribución normal. Si los datos no son normales, este método detecta falsos positivos o misses outliers reales.

2.2 Método 2: Rango intercuartílico (IQR)

El IQR es la distancia entre el tercer cuartil (\(Q_3\)) y el primero (\(Q_1\)). Es robusto a outliers porque usa percentiles en lugar de media/desviación estándar.

\[ IQR = Q_3 - Q_1 \]
\[ \text{Límite inferior} = Q_1 - 1.5 \times IQR \]
\[ \text{Límite superior} = Q_3 + 1.5 \times IQR \]
Q1 = df['valor'].quantile(0.25)
Q3 = df['valor'].quantile(0.75)
IQR = Q3 - Q1

limite_inferior = Q1 - 1.5 * IQR
limite_superior = Q3 + 1.5 * IQR

outliers_iqr = df[(df['valor'] < limite_inferior) | (df['valor'] > limite_superior)]
print(f"Outliers detectados: {len(outliers_iqr)}")
print(f"Límites: [{limite_inferior:.1f}, {limite_superior:.1f}]")

2.3 Método 3: Isolation Forest

El Isolation Forest es un algoritmo de machine learning que aísla observaciones aleatoriamente. Los outliers necesitan menos aleatorios para ser aislados (están “más solos”):

from sklearn.ensemble import IsolationForest

iso_forest = IsolationForest(contamination=0.05, random_state=42)
df['outlier'] = iso_forest.fit_predict(df[['valor']])

print(f"Outliers: {(df['outlier'] == -1).sum()}")
print(df[df['outlier'] == -1])

Ventaja: No asume distribución y funciona con múltiples dimensiones.

2.4 Método 4: Gráficos (visual)

import matplotlib.pyplot as plt

fig, axes = plt.subplots(1, 3, figsize=(14, 4))

axes[0].boxplot(df['valor'])
axes[0].set_title('Boxplot')

axes[1].hist(df['valor'], bins=30, edgecolor='black')
axes[1].axvline(limite_inferior, color='r', linestyle='--', label='Límite IQR')
axes[1].axvline(limite_superior, color='r', linestyle='--')
axes[1].legend()
axes[1].set_title('Histograma')

axes[2].scatter(range(len(df)), df['valor'], c=df['outlier'].map({1: 'blue', -1: 'red'}), s=10)
axes[2].set_title('Scatter (rojo = outlier)')

plt.tight_layout()

2.5 Comparación de métodos

Método Ventaja Desventaja Ideal para
3σ Simple Asume normalidad Distribuciones simétricas
IQR Robusto, no paramétrico Unidimensional Exploración rápida
Isolation Forest Multidimensional, no paramétrico Más complejo Datasets con múltiples features

3 Tratamiento de outliers

Una vez detectados, hay varias estrategias para manejarlos:

3.1 1. Eliminar registros

df_limpio = df[df['outlier'] == 1]
print(f"Filas antes: {len(df)}, después: {len(df_limpio)}")

Cuándo usar: Cuando el outlier es claramente un error (sensor defectuoso, dato corrupto).

3.2 2. Capping (limitar)

Reemplaza los valores extremos por el límite del IQR:

df_capped = df.copy()
df_capped['valor'] = df_capped['valor'].clip(lower=limite_inferior, upper=limite_superior)

print(f"Original - media: {df['valor'].mean():.2f}, std: {df['valor'].std():.2f}")
print(f"Capped   - media: {df_capped['valor'].mean():.2f}, std: {df_capped['valor'].std():.2f}")

Cuándo usar: Cuando quieres conservar la fila pero reducir el impacto del outlier.

3.3 3. Transformación logarítmica

La transformación log comprime valores extremos y es especialmente útil para distribuciones con cola pesada (ingresos, precios, población):

import numpy as np

df['valor_log'] = np.log1p(df['valor'])

print(f"Original - rango: [{df['valor'].min():.1f}, {df['valor'].max():.1f}]")
print(f"Log       - rango: [{df['valor_log'].min():.2f}, {df['valor_log'].max():.2f}]")

Tip

np.log1p calcula \(\log(1 + x)\), lo que evita el problema de \(\log(0)\). Es la versión segura de np.log para datos que incluyen ceros.

3.4 4. Winsorización

Similar al capping pero usando percentiles (por ejemplo, reemplazar todo lo que esté por debajo del 1% o por encima del 99% por esos percentiles):

from scipy.stats import mstats

df['valor_winsorized'] = mstats.winsorize(df['valor'], limits=[0.01, 0.01])

3.5 5. Imputación

Tratar el outlier como un valor faltante y reemplazarlo con la mediana:

df_imputed = df.copy()
mask_outlier = df_imputed['outlier'] == -1
df_imputed.loc[mask_outlier, 'valor'] = df_imputed.loc[~mask_outlier, 'valor'].median()

Advertencia

Cuidado con la imputación de outliers. Si imputas con la media/mediana, estás asumiendo que el outlier es un error. Si es un dato legítimo pero extremo, la imputación introduce sesgo.

4 Resumen

Estrategia Cuándo usar Efecto
Eliminar Error claro Reduce tamaño del dataset
Capping Conservar fila, reducir impacto Trunca valores extremos
Transformación log Distribución con cola pesada Comprime colas
Winsorización Percentiles extremos Reemplaza por percentil
Imputación Outlier = error Reemplaza con central

Regla práctica: Primero identifica (IQR o Isolation Forest). Luego diagnostica (¿es error o dato legítimo?). Solo entonces trata (eliminar, cap, transformar o imputar).