Práctica 11
Práctica 11: Temporal Feature Engineering
- Autores: Joaquín Batista, Milagros Cancela, Valentín Rodríguez, Alexia Aurrecoechea, Nahuel López (G1)
- Unidad temática: UT3 · Feature Engineering
- Tipo: Práctica guiada – Assignment UT3-11
- Entorno: Python · Pandas · Scikit-learn · Matplotlib · Seaborn · NumPy
- Dataset: Online Retail (Kaggle) · 397 884 transacciones · 4 338 usuarios · 18 562 órdenes
- Fecha: Octubre 2025
- Notebook: Práctica 11 - Temporal Feature Engineering
- Tiempo estimado: 120–150 minutos
🎯 Objetivos de Aprendizaje
- Implementar lag features usando
groupby().shift()para capturar valores históricos sin leakage. - Aplicar rolling y expanding windows para modelar tendencias a corto y largo plazo.
- Calcular métricas RFM para entender el comportamiento de recompra.
- Construir agregaciones por ventanas temporales (7d, 30d, 90d) que midan actividad reciente.
- Codificar variables calendáricas con encoding cíclico (sin/cos).
- Evaluar modelos con validación temporal (
TimeSeriesSplit) previniendo leakage. - Comparar desempeño con y sin temporal features.
📊 Dataset y Contexto de Negocio
Online Retail Dataset
- Target:
will_purchase_again(1 si el cliente compra nuevamente). - Distribución: 85.8 % sí · 14.2 % no.
- Período: 2010-12-01 a 2011-12-09 (373 días).
- Promedio de órdenes: 4.27 por usuario (5.99 para recurrentes).
Escenario: una empresa de e-commerce necesita anticipar si un cliente realizará otra compra. El reto es capturar patrones temporales fieles a la causalidad y evitar usar información futura.

🔬 Metodologías Implementadas
1. Preparación y agregación a nivel orden
df = (
df_raw.dropna(subset=["CustomerID"])
.loc[~df_raw["InvoiceNo"].astype(str).str.startswith("C")]
)
df = df[(df["Quantity"] > 0) & (df["UnitPrice"] > 0)]
df["total_amount"] = df["Quantity"] * df["UnitPrice"]
df = df.sort_values(["user_id", "order_date"]).reset_index(drop=True)- Se consolidaron transacciones al nivel
order_id, generandocart_sizeyorder_total. - Se añadieron columnas básicas:
order_number,days_since_prior_order.
2. Lag features
orders_df["days_since_prior_lag_1"] = (
orders_df.groupby("user_id")["days_since_prior_order"].shift(1)
)groupby().shift()garantiza historial por usuario y elimina leakage.- Se generaron lags de 1, 2 y 3 órdenes previas.
3. Rolling y expanding windows
orders_df["rolling_cart_mean_3"] = (
orders_df.groupby("user_id")["cart_size"]
.shift(1) # excluye orden actual
.rolling(window=3, min_periods=1)
.mean()
.reset_index(level=0, drop=True)
)- Rolling (3 órdenes) captura tendencias recientes.
- Expanding acumula desde la primera compra para entender el histórico.

4. RFM (Recency, Frequency, Monetary)
recency_days,frequency_total_orders,monetary_avgymonetary_total.monetary_avgse calculó con acumulados expandibles para evitar divisiones por cero.

5. Ventanas temporales (7d, 30d, 90d)
- Conteo y gasto en distintos horizontes (
orders_7d,spend_30d,orders_90d, etc.). - Permiten detectar activación o dormancia de clientes.

6. Product diversity
unique_products,product_diversity_ratioy métricas derivadas.- Capturan si el cliente explora o repite productos.

7. Calendar features y encoding cíclico
orders_df["hour_sin"] = np.sin(2 * np.pi * orders_df["order_hour_of_day"] / 24)
orders_df["hour_cos"] = np.cos(2 * np.pi * orders_df["order_hour_of_day"] / 24)- Variables binarias (
is_weekend,is_month_start,is_holiday) y codificación sin/cos para hora, día y mes.

8. Variables externas (indicadores económicos)
gdp_growth,unemployment_rate,consumer_confidenceagregadas por mes.- Solo
ffillpara mantener causalidad.

9. Validación temporal y evaluación de modelos
- Se utilizó
TimeSeriesSplit(n_splits=3)verificando quetrain_max < val_min. - Comparación:
- Modelo base (sin temporal): AUC 0.6625 ± 0.0254.
- Modelo con temporal: AUC 0.7204 ± 0.0623.
- Mejora absoluta: +0.0580 AUC (8.7 %).

10. Feature importance
- Feature importance de un Gradient Boosting demostró que:
product_diversity_ratio,recency_days,unique_products,spend_90d,days_since_prior_lag_3son top 5.- Lag/Window features suman 28.8 % de importancia total.

🛡️ Data Leakage Detection
Checklist aplicado:
groupby().shift(1)en todas las agregaciones temporales.- Sin
bfill, soloffill. TimeSeriesSplitgarantizando orden cronológico.- Verificación de brecha razonable entre train y CV.
- Revisión manual de top features para descartar columnas filtradas.
Resultado: no se detectó leakage en el pipeline.
🎓 Conclusiones e Insights
- Temporal features aportaron +8.7 % de mejora en AUC.
- Categorías más influyentes:
- Lag/Window (28.8 %)
- Diversity (17.1 %)
- RFM (15.0 %)
- Los features de ventanas temporales permiten identificar cambios recientes.
- La diversidad de productos es un predictor clave de recompra.
Reglas de oro anti-leakage
df.groupby("user")["feature"].shift(1)antes de cualquierrolling/expanding.- No usar
shiftglobal sin agrupar. - Validar siempre con splits temporales.
- Documentar los pasos y versionar el pipeline.
🔍 Preguntas de Reflexión
- Ventanas temporales más relevantes: 30 días ofreció mejor balance señal/ruido frente a 7 (ruidoso) y 90 (diluido).
- Variables económicas: aportaron poco al ser simuladas y de baja resolución temporal; su valor crecería con datos reales y más frecuentes.
- RFM más predictivo:
recency_days, seguido defrequency_total_orders;monetaryaporta pero con menor peso. - Señales de leakage: ninguna; el monitoreo de gaps, feature importance y revisiones manuales confirmó integridad.
- Deploy diario: mantener feature store incremental, cálculos online de lags/ventanas, versionado de features/modelos, monitoreo continuo y estrategias para cold-start.
📚 Referencias y Recursos
- Kaggle – Online Retail Dataset.
- Feature Engineering for Machine Learning – capítulo de temporal features.
- Documentación de Pandas (operaciones temporales).
- Scikit-learn –
TimeSeriesSplit. - Framework RFM para e-commerce.