2. Características: retardos y ventanas móviles
Nivel 8: Series Temporales - Estacionariedad
Una serie temporal debe reformularse como un problema de aprendizaje supervisado antes de poder aplicarle algoritmos de machine learning. En una serie temporal no existe el concepto de variables de entrada y de salida: solo hay una columna de valores y un índice de tiempo. Tenemos que elegir la variable a predecir y construir mediante ingeniería de características todas las entradas que usaremos para predecir pasos futuros.
Lo que hay que transformar es esto:
- tiempo 1, valor 1
- tiempo 2, valor 2
- tiempo 3, valor 3
en esto:
- entrada 1, salida 1
- entrada 2, salida 2
- entrada 3, salida 3
Características de retardo (lag features). Son la forma clásica de convertir una predicción temporal en aprendizaje supervisado. El enfoque más simple es predecir el valor en el instante siguiente (t+1) dado el valor actual (t). En pandas, shift(k) desplaza la serie k posiciones hacia abajo, de modo que en la fila de hoy aparece el valor de hace k días:
values = pd.DataFrame(df.values)
df2 = pd.concat([values.shift(2), values.shift(1), values], axis=1)
df2.columns = ['t-2', 't-1', 't']
df2.head()Las dos primeras filas contendrán valores nulos: en la primera observación no existe un t-1 ni un t-2. Es el precio inevitable de mirar hacia atrás y la razón por la que un modelo con muchos retardos pierde el arranque de la muestra.
Estadísticos de ventana móvil (rolling window). Un paso más allá de añadir valores retardados en bruto es añadir un resumen de los valores de los instantes previos. Podemos calcular estadísticos sobre los valores que caen dentro de una ventana deslizante e incluirlos como características. El más útil suele ser la media de los valores anteriores, la media móvil:
values = pd.DataFrame(df.values)
shifted = values.shift(1)
window = shifted.rolling(window=2)
means = window.mean()
df3 = pd.concat([means, values], axis=1)
df3.columns = ['mean(t-2,t-1)', 't']
df3.head()Fíjate en el detalle crítico: primero se desplaza y después se aplica la ventana. Ese shift(1) es lo que garantiza que la media de la fila t se calcula con t-2 y t-1, sin incluir el propio valor de t. Si aplicaras values.rolling(window=2).mean() directamente, la característica de la fila t contendría información del instante t, es decir, del propio valor que quieres predecir. Eso es sesgo de anticipación (lookahead bias), el error que más backtests espectaculares y falsos ha producido en la historia del trading cuantitativo.
Estadísticos de ventana expansiva (expanding window). Otro tipo de ventana útil incluye todos los datos previos de la serie. Se llama ventana expansiva y ayuda a llevar la cuenta de los límites de lo observado hasta la fecha:
values = pd.DataFrame(df.values)
window = values.expanding()
df4 = pd.concat([window.mean(), values], axis=1)
df4.columns = ['mean', 't']
df4.head()La diferencia práctica entre ambas: la ventana móvil tiene memoria finita y olvida el pasado lejano, por lo que se adapta a cambios de régimen; la expansiva nunca olvida, así que se vuelve cada vez más estable y cada vez más lenta en reaccionar. Para máximos históricos, drawdowns o normalizaciones acumuladas, la expansiva es la correcta. Para una media que deba reflejar el mercado actual, la móvil.