Cómo crear muestras temporales para entrenamiento/prueba en Python para datos
Aprende a crear muestras temporales para entrenamiento/prueba en Python para datos — una tarea esencial para modelado de series temporales que evita data leakage y garantiza validación realista. La técnica es útil para predecir ventas, métricas web o cualquier serie en la que el orden cronológico sea importante.
Pre-requisitos
- Python 3.8+ instalado
- Paquetes: pandas, scikit-learn, numpy (instalar con pip si es necesario)
- Noiones básicas de pandas y de entrenamiento/prueba en machine learning
Paso 1: Por qué hacer la división temporal en vez de shuffle
En datos temporales, barajar (shuffle) puede causar data leakage: información del futuro entra en el entrenamiento e infla la métrica. La división temporal preserva el orden cronológico — entrenamiento con datos más antiguos y prueba con datos más recientes.
Paso 2: Estructura mínima de datos
Comenzamos con un DataFrame simple que tiene una columna de fechas y una variable objetivo. Aseguramos que la columna de fechas está en datetime y ordenamos por ella.
import pandas as pd
import numpy as np
# Exemplo mínimo
np.random.seed(42)
dates = pd.date_range('2022-01-01', periods=100, freq='D')
value = np.random.randn(100).cumsum() + 50
df = pd.DataFrame({'date': dates, 'value': value})
# Converter e ordenar
df['date'] = pd.to_datetime(df['date'])
df = df.sort_values('date').reset_index(drop=True)
print(df.head())
Paso 3: División simple entrenamiento/prueba temporales (holdout)
Un enfoque común es usar un porcentaje final como prueba (por ejemplo, últimos 20%). Esto es simple y evita mezcla temporal.
# Proporción de prueba
test_size = 0.2
n = len(df)
split = int(n * (1 - test_size))
train = df.iloc[:split].reset_index(drop=True)
test = df.iloc[split:].reset_index(drop=True)
print('Train:', train['date'].min(), '->', train['date'].max())
print('Test:', test['date'].min(), '->', test['date'].max())
Paso 4: Validación temporal con rolling windows
Para evaluar la estabilidad del modelo, usamos cross-validation temporal (rolling/expanding window). Aquí creamos splits secuenciales en los que cada fold entrena con datos hasta un punto t y prueba en un bloque futuro.
from sklearn.model_selection import TimeSeriesSplit
# Exemplo com TimeSeriesSplit (k folds)
X = df[['value']].copy() # features fictícias
y = df['value']
tscv = TimeSeriesSplit(n_splits=5)
for i, (train_idx, test_idx) in enumerate(tscv.split(X)):
print(f'Fold {i+1}: train {train_idx[0]}-{train_idx[-1]}, test {test_idx[0]}-{test_idx[-1]}')
Paso 5: Ventana con avance (walk-forward) y bloques de prueba fijos
Si necesitas predecir un horizonte fijo (p.ej. 7 días), crea un generador que devuelva ventanas en las que la prueba tiene longitud fija y el entrenamiento se expande o desplaza.
def rolling_windows(df, train_size, test_size, step=1):
n = len(df)
start = 0
while True:
train_end = start + train_size
test_end = train_end + test_size
if test_end > n:
break
yield df.iloc[start:train_end].reset_index(drop=True), df.iloc[train_end:test_end].reset_index(drop=True)
start += step
# Exemplo
for i, (tr, te) in enumerate(rolling_windows(df, train_size=60, test_size=7, step=7)):
print(f'Window {i+1}: train {tr.date.min().date()}->{tr.date.max().date()}, test {te.date.min().date()}->{te.date.max().date()}')
Paso 6: Evitar leaks con características basadas en el futuro
Al crear características (lags, rolling means) asegura que solo uses información hasta t-1 para predecir t. Usa shift() en pandas para crear lags y evita funciones que agreguen valores del futuro.
# Exemplo de lags e média móvel sem data leakage
df['lag_1'] = df['value'].shift(1)
df['rolling_7'] = df['value'].shift(1).rolling(window=7, min_periods=1).mean()
# Remover primeiras linhas com NaN resultantes
df_feat = df.dropna().reset_index(drop=True)
print(df_feat.head())
Verificar el resultado
Confirma que las fechas del entrenamiento son siempre anteriores a las de la prueba y que las características del entrenamiento no contienen valores del futuro. Verifica el tamaño de los conjuntos e imprime una muestra de las primeras filas después de crear lags/rolling.
# Verificações rápidas
assert train['date'].max() < test['date'].min(), 'Data leakage: sobreposição temporal!'
print('Tamanhos: ', len(train), len(test))
print(df_feat[['date','value','lag_1','rolling_7']].head())
Conclusión
Dividir series temporales correctamente es crítico para evitar data leakage y obtener métricas realistas. Usa holdout temporal para pruebas simples, TimeSeriesSplit para validación más robusta y rolling windows para evaluar la estabilidad a lo largo del tiempo. Paso siguiente: entrenar un modelo simple (ex.: RandomForest, XGBoost) usando los lags y validar con los splits temporales. Consejo: visualiza siempre las fechas y las ventanas para garantizar que no hubo solapamiento.