Sesgo del colisionador: cómo el filtrado de datos crea correlaciones falsas
Un colisionador es una variable C influenciada por dos variables A y B mediante rutas causales: A → C ← B. Condicionar sobre C—ya sea mediante filtrado o estratificación—genera una correlación espuria entre A y B.
Ejemplos del mundo real:
- Edad → Uso → Calidad (los artículos que sobreviven parecen tener mejor calidad).
- IQ → Admisión universitaria → Riqueza familiar (entre estudiantes, los más ricos parecen menos inteligentes).
- Talento → Éxito → Ética laboral (los individuos talentosos exitosos parecen más perezosos).
En ciencia de datos, esto conduce a modelos engañosos: los modelos de boosting de gradiente sobre datos filtrados heredan este sesgo.
Simulación con datos uniformes
Generamos 1.000 puntos independientes para A y B desde una distribución uniforme(0,100). La bandera del colisionador se activa si A > 50 o B > 50.
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
from statsmodels.nonparametric.smoothers_lowess import lowess
NUMBER_OF_VALUES = 1000
A_RANGE = (0, 100)
B_RANGE = (0, 100)
A_THRESHOLD = 50
B_THRESHOLD = 50
a = np.random.uniform(*A_RANGE, NUMBER_OF_VALUES)
b = np.random.uniform(*B_RANGE, NUMBER_OF_VALUES)
data = pd.DataFrame({'a': a, 'b': b})
data['flag'] = (data['a'] > A_THRESHOLD) | (data['b'] > B_THRESHOLD)
print(f"Correlación entre A y B: {data['a'].corr(data['b']):.2f}")
print(f"Correlación donde flag es verdadero: {data[data['flag']]['a'].corr(data[data['flag']]['b']):.2f}")
Resultados:
- Conjunto completo: corr ≈ 0.00
- Subconjunto filtrado: corr ≈ -0.34
La visualización con gráfico de dispersión muestra cómo eliminar los puntos no supervivientes (azules) desplaza la tendencia hacia abajo.
sns.scatterplot(data=data, x='a', y='b', hue='flag', palette={True: 'red', False: 'blue'}, alpha=0.5)
sns.regplot(data=data, x='a', y='b', scatter=False, color='black')
plt.show()
Después del filtrado, el gráfico lmplot muestra una pendiente negativa: el cuadrante superior derecho queda vacío, rompiendo el equilibrio.
Escenario realista: Distribución normal
Ahora usamos np.random.normal: A ~ N(50,5), B ~ N(30,5). El colisionador es probabilístico: normalizamos los valores, calculamos la probabilidad de selección como max(a_norm, b_norm)*1.5, recortamos en [0,1], y asignamos la bandera según una tirada aleatoria.
a = np.random.normal(loc=50, scale=5, size=NUMBER_OF_VALUES)
b = np.random.normal(loc=30, scale=5, size=NUMBER_OF_VALUES)
normal_data = pd.DataFrame({'a': a, 'b': b})
a_norm = (normal_data['a'] - normal_data['a'].min()) / (normal_data['a'].max() - normal_data['a'].min() + 1e-6)
b_norm = (normal_data['b'] - normal_data['b'].min()) / (normal_data['b'].max() - normal_data['b'].min() + 1e-6)
selection_prob = np.maximum(a_norm, b_norm) * 1.5
selection_prob = np.clip(selection_prob, 0, 1)
normal_data['flag'] = np.random.random(len(normal_data)) < selection_prob
print(f"Correlación en conjunto completo: {normal_data['a'].corr(normal_data['b']):.2f}")
print(f"Correlación cuando flag es verdadero: {normal_data[normal_data['flag']]['a'].corr(normal_data[normal_data['flag']]['b']):.2f}")
Resultados:
- Conjunto completo: corr ≈ 0.03
- Subconjunto filtrado: corr ≈ -0.09
El efecto es más débil pero aún significativo en pruebas A/B, estudios de campo o flujos de aprendizaje automático.
Cómo evitar el sesgo del colisionador en el análisis
- Dibuja DAGs: Usa grafos acíclicos dirigidos para identificar colisionadores antes del análisis.
- Evita el filtrado basado en resultados: Analiza la población completa o aplica ponderación IPCW (ponderación inversa por censura probabilística).
- Verifica correlaciones antes y después de la estratificación: Compara las tendencias entre grupos.
- Usa variables instrumentales: Para inferencia causal cuando se sospecha sesgo.
- Aprovecha redes bayesianas: Modela explícitamente dependencias y relaciones condicionales.
Conclusiones clave
- El sesgo del colisionador ocurre al condicionar sobre una causa común, generando correlaciones negativas artificiales.
- La simulación con datos uniformes da corr ≈ -0.34; con datos normales, ≈ -0.09—el efecto varía según la estructura de los datos.
- Relacionado con el sesgo de supervivencia, el paradoja de Berkson y el sesgo de selección.
- Siempre dibuja un DAG antes de filtrar datos.
- En ML: datos filtrados generan modelos sesgados, resultando en subestimación o sobreestimación de la importancia de características.
— Editorial Team
Aún no hay comentarios.