(+351) 21 24 10006  ·  info@bconcepts.pt
Carnaxide, Lisboa

Cómo detectar drift de características en Machine Learning: paso a paso

João Barros 20 de August de 2026 5 min de lectura

Detectar drift de características en Machine Learning es útil para entender cuándo los datos en producción cambian respecto a los datos de entrenamiento, evitando pérdida de rendimiento. Este tutorial muestra, paso a paso, cómo calcular medidas simples de drift por característica y señalar cambios relevantes con Python.

Requisitos previos

  • Python 3.8+ instalado
  • Pandas y scipy instalados (pip install pandas scipy)
  • Conjunto de entrenamiento y de producción como archivos CSV
  • Conocimientos básicos de Python y estadística descriptiva

Paso 1: ¿Por qué detectar drift de características?

El drift de características (feature drift) ocurre cuando la distribución de una columna cambia a lo largo del tiempo. Identificar drift ayuda a decidir si es necesario retrain, ajustar el preprocesamiento o señalizar alertas. Vamos a usar pruebas estadísticas y medidas prácticas para cada característica.

Paso 2: Preparar datos y cargar librerías

Cargue los CSV del conjunto de entrenamiento y del conjunto de producción y seleccione las columnas que quiere monitorizar. Mantenemos solo columnas numéricas en este ejemplo; para categóricas usamos comparaciones por frecuencias.

import pandas as pd
from scipy.stats import ks_2samp, chi2_contingency

train = pd.read_csv('train.csv')
prod = pd.read_csv('prod.csv')

# Lista de columnas a monitorizar (numéricas y categóricas)
num_cols = ['age','salary']
cat_cols = ['region','product_type']

Paso 3: Calcular drift para características numéricas (Kolmogorov-Smirnov)

La prueba de Kolmogorov-Smirnov compara dos muestras para ver si provienen de la misma distribución. No asume normalidad. Usamos p-value y una medida de efecto (D) para señalar drift.

def detect_numeric_drift(train_ser, prod_ser, alpha=0.05):
    # Remove NaNs
    a = train_ser.dropna()
    b = prod_ser.dropna()
    if len(a) < 20 or len(b) < 20:
        return {'p_value': None, 'D': None, 'drift': 'insufficient_data'}
    stat, p = ks_2samp(a, b)
    drift = 'drift' if p < alpha else 'no_drift'
    return {'p_value': float(p), 'D': float(stat), 'drift': drift}

results_num = {}
for col in num_cols:
    results_num[col] = detect_numeric_drift(train[col], prod[col])

print(results_num)

Paso 4: Calcular drift para características categóricas (prueba chi-cuadrado)

Para categóricas con conteos, comparamos tablas de frecuencia. Si hay categorías nuevas o eliminadas, esto indica drift cualitativo.

def detect_categorical_drift(train_ser, prod_ser, alpha=0.05):
    a = train_ser.fillna('NULL')
    b = prod_ser.fillna('NULL')
    # Frecuencias alineadas por categoría
    freq = pd.concat([a.value_counts(), b.value_counts()], axis=1, sort=False).fillna(0)
    freq.columns = ['train', 'prod']
    chi2, p, _, _ = chi2_contingency(freq.values)
    different_categories = set(b.unique()) - set(a.unique())
    drift = 'drift' if p < alpha or len(different_categories) > 0 else 'no_drift'
    return {'p_value': float(p), 'different_categories': list(different_categories), 'drift': drift}

results_cat = {}
for col in cat_cols:
    results_cat[col] = detect_categorical_drift(train[col], prod[col])

print(results_cat)

Paso 5: Medidas complementarias y umbrales prácticos

Además de pruebas estadísticas, es útil calcular diferencias de media, mediana y proporciones para priorizar features. Defina umbrales prácticos (ej.: diferencia de media > 10% o D > 0.1).

def numeric_statistics(train_ser, prod_ser):
    t = train_ser.dropna()
    p = prod_ser.dropna()
    stats = {
        'train_mean': float(t.mean()),
        'prod_mean': float(p.mean()),
        'mean_rel_diff': abs(t.mean() - p.mean()) / (abs(t.mean()) + 1e-9)
    }
    return stats

summary = {}
for col in num_cols:
    summary[col] = {**results_num[col], **numeric_statistics(train[col], prod[col])}

print(summary)

Paso 6: Automatizar y generar informe

Junte todo en una función que itere por columnas, aplique pruebas y escriba un CSV de alerta con columnas señaladas por prioridad.

def drift_report(train_df, prod_df, num_cols, cat_cols):
    rows = []
    for col in num_cols:
        r = detect_numeric_drift(train_df[col], prod_df[col])
        stats = numeric_statistics(train_df[col], prod_df[col])
        priority = 'high' if r['drift']=='drift' and stats['mean_rel_diff'] > 0.1 else 'low'
        rows.append({'feature': col, 'type': 'numeric', **r, **stats, 'priority': priority})
    for col in cat_cols:
        r = detect_categorical_drift(train_df[col], prod_df[col])
        priority = 'high' if r['drift']=='drift' else 'low'
        rows.append({'feature': col, 'type': 'categorical', **r, 'priority': priority})
    return pd.DataFrame(rows)

report = drift_report(train, prod, num_cols, cat_cols)
report.to_csv('drift_report.csv', index=False)
print(report)

Verificar el resultado

Abra drift_report.csv: debe listar cada característica con p_value, indicador de drift y priority. Revise las columnas marcadas como high: inspeccione histogramas o boxplots para confirmar visualmente. Si varios features están en high, considere retrain, recalibrar umbrales o ajustar ETL.

Conclusión

Este método simple combina pruebas estadísticas (KS y chi-cuadrado) con medidas prácticas para detectar drift de características en Machine Learning. Siguientes pasos: integrar esto en un pipeline de monitorización en producción, añadir pruebas por ventana temporal y usar técnicas de explicabilidad para entender causas. Consejo: empiece a monitorizar gradualmente y ajuste umbrales según el coste de falso positivo/negativo en su escenario.