ScikitLearn
Tutorial ScikitLearn: Ingeniería de características para series temporales
Aprende ingeniería de características en fechas para crear variables de retraso en Python para mejorar modelos de series temporales con pandas y Scikit-Learn.
Aprende ScikitLearn GRATIS y certifícateCreación de variables de retraso (lags)
En el análisis de series temporales, la creación de variables de retraso o lags es esencial para capturar la dependencia temporal en los datos. Estas variables representan los valores anteriores de una serie y permiten que los modelos aprendan patrones históricos para mejorar las predicciones.
La técnica consiste en agregar al conjunto de datos nuevas columnas que contienen los valores retrasados de la serie original. Por ejemplo, un lag de 1 período refleja el valor inmediatamente anterior, mientras que un lag de 2 períodos refleja el valor de dos pasos atrás. Esto es especialmente útil en modelos de aprendizaje automático que requieren entradas estáticas.
Para ilustrar cómo crear variables de retraso en Python, utilizaremos la biblioteca pandas junto con Scikit-Learn. A continuación, se muestra un ejemplo completo utilizando datos de ventas diarias:
import pandas as pd
# Crear un DataFrame con una serie temporal de ventas diarias
data = {
'fecha': pd.date_range(start='2025-01-01', periods=10, freq='D'),
'ventas': [200, 220, 215, 230, 225, 240, 235, 250, 245, 260]
}
df = pd.DataFrame(data)
# Establecer la columna 'fecha' como índice
df.set_index('fecha', inplace=True)
# Crear variables de retraso (lags)
df['ventas_lag1'] = df['ventas'].shift(1)
df['ventas_lag2'] = df['ventas'].shift(2)
print(df)
Al ejecutar el código anterior, se añadirán dos nuevas columnas al DataFrame: ventas_lag1 y ventas_lag2, que representan los valores de ventas con un retraso de uno y dos días, respectivamente. Observa que las primeras filas de estas nuevas columnas contienen valores faltantes debido a la falta de datos anteriores.
Es importante manejar los valores faltantes resultantes de los lags. Una práctica común es eliminar las filas que contienen datos incompletos:
# Eliminar filas con valores faltantes
df.dropna(inplace=True)
Una vez creadas las variables de retraso y limpiados los datos, podemos preparar las características (features) y la variable objetivo (target) para el modelado:
# Definir las características (X) y la variable objetivo (y)
X = df[['ventas_lag1', 'ventas_lag2']]
y = df['ventas']
En problemas de series temporales, es fundamental mantener el orden cronológico al dividir los datos en conjuntos de entrenamiento y prueba para evitar fugas de información. A continuación, dividimos los datos respetando la secuencia temporal:
# Dividir los datos en conjuntos de entrenamiento y prueba
train_size = int(len(df) * 0.7) # 70% para entrenamiento
X_train, X_test = X.iloc[:train_size], X.iloc[train_size:]
y_train, y_test = y.iloc[:train_size], y.iloc[train_size:]
Con los datos preparados, entrenamos un modelo de regresión, como RandomForestRegressor, para predecir las ventas futuras:
from sklearn.ensemble import RandomForestRegressor
# Crear y entrenar el modelo
modelo = RandomForestRegressor(n_estimators=100, random_state=42)
modelo.fit(X_train, y_train)
# Realizar predicciones
y_pred = modelo.predict(X_test)
Para evaluar el rendimiento del modelo, utilizamos métricas como el Error Medio Absoluto (MAE) y la Raíz del Error Cuadrático Medio (RMSE):
from sklearn.metrics import mean_absolute_error, mean_squared_error
import numpy as np
mae = mean_absolute_error(y_test, y_pred)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
print(f'Error Medio Absoluto (MAE): {mae}')
print(f'Raíz del Error Cuadrático Medio (RMSE): {rmse}')
Salida:
La incorporación de variables de retraso permite al modelo reconocer patrones temporales y mejorar la precisión de las predicciones. Sin embargo, es importante seleccionar el número adecuado de lags. Utilizar demasiados puede aumentar la complejidad del modelo y el riesgo de sobreajuste.
Es aconsejable experimentar con diferentes cantidades de variables de retraso y utilizar técnicas de validación cruzada específicas para series temporales. Además, combinar los lags con otras técnicas de ingeniería de características puede optimizar aún más el rendimiento del modelo.
Uso de ventanas móviles y características acumulativas
En el análisis de series temporales, las ventanas móviles y las características acumulativas son herramientas fundamentales para capturar patrones y tendencias en los datos. Estas técnicas permiten extraer información estadística que refleja el comportamiento histórico de la serie, enriqueciendo el conjunto de características para el modelado.
Las ventanas móviles, también conocidas como rolling windows, se utilizan para calcular estadísticas sobre un intervalo de tiempo fijo que se desplaza a lo largo de la serie temporal. Esto es útil para detectar cambios locales y variaciones en el comportamiento de la serie. Por otro lado, las características acumulativas proporcionan información agregada desde el inicio hasta un punto específico en el tiempo, ayudando a captar tendencias a largo plazo.
A continuación, veremos cómo implementar estas técnicas utilizando Python 3.13, pandas y Scikit-Learn 1.5.2. Partiremos de un conjunto de datos de temperaturas diarias:
import pandas as pd
# Crear un DataFrame con una serie temporal de temperaturas diarias
data = {
'fecha': pd.date_range(start='2025-01-01', periods=15, freq='D'),
'temperatura': [15, 16, 15, 17, 16, 18, 19, 17, 16, 18, 20, 21, 19, 18, 20]
}
df = pd.DataFrame(data)
# Establecer la columna 'fecha' como índice
df.set_index('fecha', inplace=True)
print(df)
Con el DataFrame preparado, podemos calcular la media móvil utilizando la función rolling()
de pandas. Por ejemplo, para obtener la media móvil de una ventana de 3 días:
# Calcular la media móvil de 3 días
df['media_movil_3'] = df['temperatura'].rolling(window=3).mean()
print(df)
La nueva columna media_movil_3 contiene la media de las temperaturas de los últimos tres días. Esto ayuda a suavizar las fluctuaciones diarias y a destacar tendencias subyacentes. También es posible calcular otras estadísticas móviles, como la desviación estándar y el máximo:
# Calcular la desviación estándar móvil de 3 días
df['std_movil_3'] = df['temperatura'].rolling(window=3).std()
# Calcular el máximo móvil de 3 días
df['max_movil_3'] = df['temperatura'].rolling(window=3).max()
Las características acumulativas se calculan utilizando la función expanding()
de pandas, que acumula las estadísticas desde el inicio de la serie hasta el punto actual:
# Calcular la suma acumulativa
df['suma_acumulativa'] = df['temperatura'].expanding().sum()
# Calcular el promedio acumulativo
df['promedio_acumulativo'] = df['temperatura'].expanding().mean()
Estas características proporcionan información sobre la evolución global de la serie temporal. Es importante manejar los valores faltantes que surgen al calcular ventanas móviles, especialmente al inicio donde la ventana no está completa. Podemos eliminar estos valores para preparar los datos para el modelado:
# Eliminar filas con valores faltantes
df_limpio = df.dropna()
print(df_limpio)
Una vez creadas las nuevas características, definimos las variables predictoras y la variable objetivo:
# Definir las características (X) y la variable objetivo (y)
X = df_limpio[['media_movil_3', 'std_movil_3', 'max_movil_3', 'suma_acumulativa', 'promedio_acumulativo']]
y = df_limpio['temperatura']
Para evitar fugas de información y respetar el orden temporal, dividimos los datos en conjuntos de entrenamiento y prueba sin mezclar las observaciones:
# Dividir los datos en conjuntos de entrenamiento y prueba
train_size = int(len(df_limpio) * 0.7) # 70% para entrenamiento
X_train, X_test = X.iloc[:train_size], X.iloc[train_size:]
y_train, y_test = y.iloc[:train_size], y.iloc[train_size:]
Entrenamos un modelo de regresión, como GradientBoostingRegressor, para predecir la temperatura en función de las características generadas:
from sklearn.ensemble import GradientBoostingRegressor
# Crear y entrenar el modelo
modelo = GradientBoostingRegressor(n_estimators=100, learning_rate=0.1, random_state=42)
modelo.fit(X_train, y_train)
# Realizar predicciones
y_pred = modelo.predict(X_test)
Para evaluar el rendimiento del modelo, utilizamos métricas como el Error Medio Absoluto (MAE) y la Raíz del Error Cuadrático Medio (RMSE):
from sklearn.metrics import mean_absolute_error, mean_squared_error
import numpy as np
mae = mean_absolute_error(y_test, y_pred)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
print(f'Error Medio Absoluto (MAE): {mae}')
print(f'Raíz del Error Cuadrático Medio (RMSE): {rmse}')
Salida:
El uso de ventanas móviles y características acumulativas enriquece el conjunto de datos al proporcionar perspectivas tanto locales como globales de la serie temporal. Estas técnicas permiten a los modelos de aprendizaje automático capturar de manera más efectiva las dinámicas temporales, mejorando la precisión de las predicciones.
Es aconsejable experimentar con diferentes tamaños de ventana y tipos de estadísticas para identificar las características que aporten mayor valor al modelo. Además, combinar estas técnicas con otras estrategias de ingeniería de características puede optimizar aún más el rendimiento en tareas de predicción de series temporales.
Generación de características basadas en fechas (día de la semana, mes, etc.)
En el análisis de series temporales, la generación de características basadas en fechas es fundamental para capturar patrones estacionales y tendencias asociadas al tiempo. Al extraer información relevante de las fechas, como el día de la semana o el mes, enriquecemos el conjunto de variables predictoras, permitiendo que los modelos de aprendizaje automático identifiquen comportamientos específicos en determinados periodos.
A continuación, se presenta un ejemplo completo utilizando Python, pandas y Scikit-Learn, que ilustra cómo generar y utilizar características basadas en fechas para mejorar las predicciones en series temporales.
Ejemplo práctico: Predicción de ventas diarias
Supongamos que disponemos de un conjunto de datos con las ventas diarias de una tienda a lo largo de varios meses y queremos predecir las ventas futuras. Primero, creamos un DataFrame con los datos simulados:
import pandas as pd
import numpy as np
# Crear un generador de números aleatorios
rng = np.random.default_rng(42) # Semilla para reproducibilidad
# Generar fechas diarias para el año 2025
fechas = pd.date_range(start='2025-01-01', end='2025-03-31', freq='D')
# Generar datos de ventas simuladas con el nuevo generador
ventas = rng.poisson(lam=50, size=len(fechas))
# Crear el DataFrame
df = pd.DataFrame({'fecha': fechas, 'ventas': ventas})
print(df.head())
Establecemos la columna fecha como índice para facilitar la manipulación temporal:
# Establecer 'fecha' como índice
df.set_index('fecha', inplace=True)
# Visualizar las primeras filas
print(df.head())
Extracción de características basadas en fechas
Utilizamos las funciones de pandas para extraer información temporal de la columna fecha y crear nuevas características:
# Crear características basadas en fechas
df['dia_semana'] = df.index.weekday # 0: Lunes, 6: Domingo
df['fin_semana'] = df['dia_semana'].isin([5, 6]).astype(int) # 1 si es sábado o domingo
df['dia_mes'] = df.index.day
df['semana_anio'] = df.index.isocalendar().week
df['mes'] = df.index.month
df['trimestre'] = df.index.quarter
print(df.head())
Las nuevas columnas contienen información detallada sobre cada fecha:
- dia_semana: día de la semana.
- fin_semana: indicador binario de fin de semana.
- dia_mes: día del mes.
- semana_anio: número de semana en el año.
- mes: mes del año.
- trimestre: trimestre del año.
Codificación de variables categóricas
Algunas de las características extraídas son variables categóricas y necesitan ser transformadas para ser utilizadas en modelos de aprendizaje automático. Empleamos el OneHotEncoder de Scikit-Learn para realizar esta codificación:
from sklearn.preprocessing import OneHotEncoder
# Identificar columnas categóricas
columnas_categoricas = ['dia_semana', 'mes', 'trimestre']
# Inicializar el OneHotEncoder
encoder = OneHotEncoder(sparse_output=False, drop='first')
# Aplicar el encoder a las columnas categóricas
encoded = encoder.fit_transform(df[columnas_categoricas])
# Obtener nombres de las nuevas columnas
nombres_columnas = encoder.get_feature_names_out(columnas_categoricas)
# Crear un DataFrame con las nuevas columnas codificadas
df_encoded = pd.DataFrame(encoded, index=df.index, columns=nombres_columnas)
# Concatenar las nuevas columnas al DataFrame original
df = pd.concat([df, df_encoded], axis=1)
# Eliminar las columnas categóricas originales
df.drop(columns=columnas_categoricas, inplace=True)
print(df.head())
La codificación One-Hot transforma las variables categóricas en una matriz de variables binarias, permitiendo al modelo interpretar correctamente estas características.
Preparación de los datos para el modelado
Definimos las características (X) y la variable objetivo (y):
# Definir X y y
X = df.drop(columns=['ventas'])
y = df['ventas']
Dividimos los datos en conjuntos de entrenamiento y prueba, respetando el orden temporal para evitar fugas de información:
# Determinar el tamaño del conjunto de entrenamiento (80% de los datos)
train_size = int(len(df) * 0.8)
# Dividir en conjuntos de entrenamiento y prueba
X_train, X_test = X.iloc[:train_size], X.iloc[train_size:]
y_train, y_test = y.iloc[:train_size], y.iloc[train_size:]
Entrenamiento y evaluación del modelo
Utilizamos un modelo de RandomForestRegressor para predecir las ventas:
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_absolute_error, mean_squared_error
# Crear y entrenar el modelo
modelo = RandomForestRegressor(n_estimators=100, random_state=42)
modelo.fit(X_train, y_train)
# Realizar predicciones
y_pred = modelo.predict(X_test)
# Evaluar el modelo
mae = mean_absolute_error(y_test, y_pred)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
print(f'Error Medio Absoluto (MAE): {mae:.2f}')
print(f'Raíz del Error Cuadrático Medio (RMSE): {rmse:.2f}')
Salida:
La incorporación de características basadas en fechas ha permitido capturar la estacionalidad y los patrones temporales de las ventas, mejorando la precisión del modelo.
Representación cíclica de variables temporales
Para variables temporales cíclicas, como dia_semana o mes, es útil aplicar una transformación que refleje su naturaleza circular. Utilizamos funciones seno y coseno para esta representación:
# Añadir de nuevo las variables temporales necesarias
df['dia_semana'] = df.index.weekday
df['mes'] = df.index.month
# Representación cíclica del día de la semana
df['dia_semana_sin'] = np.sin(2 * np.pi * df['dia_semana'] / 7)
df['dia_semana_cos'] = np.cos(2 * np.pi * df['dia_semana'] / 7)
# Representación cíclica del mes
df['mes_sin'] = np.sin(2 * np.pi * df['mes'] / 12)
df['mes_cos'] = np.cos(2 * np.pi * df['mes'] / 12)
# Eliminar las columnas originales
df.drop(columns=['dia_semana', 'mes'], inplace=True)
print(df.head())
Estas transformaciones permiten al modelo captar mejor la continuidad cíclica de estas variables, evitando artificiales discontinuidades entre el final y el inicio del ciclo.
Reentrenamiento y evaluación con variables cíclicas
Actualizamos las características y reentrenamos el modelo:
# Definir X y y con las nuevas características
X = df.drop(columns=['ventas'])
y = df['ventas']
# Dividir en conjuntos de entrenamiento y prueba
X_train, X_test = X.iloc[:train_size], X.iloc[train_size:]
y_train, y_test = y.iloc[:train_size], y.iloc[train_size:]
# Entrenar el modelo
modelo.fit(X_train, y_train)
# Realizar predicciones
y_pred = modelo.predict(X_test)
# Evaluar el modelo
mae = mean_absolute_error(y_test, y_pred)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
print(f'Nuevo Error Medio Absoluto (MAE): {mae:.2f}')
print(f'Nuevo Raíz del Error Cuadrático Medio (RMSE): {rmse:.2f}')
Salida:
La utilización de la representación cíclica puede mejorar aún más el rendimiento del modelo al reflejar adecuadamente la naturaleza periódica de las variables temporales.
Agregación y combinación de características basadas en fechas
Podemos también crear características adicionales combinando información temporal, como indicadores de días festivos o temporadas específicas:
# Crear un indicador para periodos promocionales (ejemplo)
df['promocion'] = ((df.index.month == 1) & (df.index.day <= 15)).astype(int)
# Crear una columna que indique si es principio de mes
df['inicio_mes'] = (df.index.day <= 5).astype(int)
print(df.head())
Estas nuevas características pueden ser especialmente útiles si existen comportamientos particulares en ciertos periodos, como incrementos de ventas durante promociones o variaciones al inicio de cada mes.
Implementación en un Pipeline de Scikit-Learn
Para facilitar el proceso y evitar errores, es recomendable utilizar un Pipeline que incluya todos los pasos de preprocesamiento y modelado:
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import FunctionTransformer
# Crear el DataFrame
data = pd.DataFrame({'fecha': fechas, 'ventas': ventas})
data.set_index('fecha', inplace=True)
# Definir funciones para extraer características temporales
def extraer_caracteristicas_fecha(df):
df_temp = df.copy()
df_temp['dia_semana'] = df_temp.index.weekday
df_temp['mes'] = df_temp.index.month
df_temp['dia_semana_sin'] = np.sin(2 * np.pi * df_temp['dia_semana'] / 7)
df_temp['dia_semana_cos'] = np.cos(2 * np.pi * df_temp['dia_semana'] / 7)
df_temp['mes_sin'] = np.sin(2 * np.pi * df_temp['mes'] / 12)
df_temp['mes_cos'] = np.cos(2 * np.pi * df_temp['mes'] / 12)
df_temp['promocion'] = ((df_temp.index.month == 1) & (df_temp.index.day <= 15)).astype(int)
df_temp['inicio_mes'] = (df_temp.index.day <= 5).astype(int)
return df_temp.drop(columns=['dia_semana', 'mes'])
# Definir X y y
X = data.drop(columns=['ventas'])
y = data['ventas']
# Determinar el tamaño del conjunto de entrenamiento
train_size = int(len(data) * 0.8)
X_train, X_test = X.iloc[:train_size], X.iloc[train_size:]
y_train, y_test = y.iloc[:train_size], y.iloc[train_size:]
# Crear el Pipeline con memoria
pipeline = Pipeline(
steps=[
('caracteristicas_fecha', FunctionTransformer(extraer_caracteristicas_fecha, validate=False)),
('modelo', RandomForestRegressor(n_estimators=100, random_state=42))
],
memory=None # Puedes especificar una ruta para almacenar en caché si es necesario
)
# Entrenar el Pipeline
pipeline.fit(X_train, y_train)
# Realizar predicciones
y_pred = pipeline.predict(X_test)
# Evaluar el modelo
mae = mean_absolute_error(y_test, y_pred)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
# Imprimir métricas
print(f'Error Medio Absoluto con Pipeline (MAE): {mae:.2f}')
print(f'Raíz del Error Cuadrático Medio con Pipeline (RMSE): {rmse:.2f}')
El uso de un Pipeline integrado asegura que todos los pasos se realicen de manera consistente y facilita la reproducibilidad del modelo.
Extracción de características estacionales y de tendencia
En el análisis de series temporales, es fundamental capturar los componentes estacionales y de tendencia para mejorar las predicciones. La estacionalidad refleja patrones que se repiten en intervalos regulares, mientras que la tendencia muestra el comportamiento a largo plazo de la serie. Extraer estos componentes y utilizarlos como características adicionales en los modelos de aprendizaje automático permite captar mejor las dinámicas subyacentes de los datos.
Una técnica común para extraer estos componentes es la descomposición de series temporales, que separa la serie en tendencia, estacionalidad y residuo. Utilizaremos la función seasonal_decompose
de la biblioteca statsmodels para llevar a cabo esta descomposición. A continuación, se presenta un ejemplo completo utilizando datos mensuales de ventas:
import pandas as pd
import numpy as np
from statsmodels.tsa.seasonal import seasonal_decompose
# Generar fechas mensuales
fechas = pd.date_range(start='2020-01-01', end='2024-12-01', freq='MS')
# Crear datos simulados de ventas con tendencia y estacionalidad
np.random.seed(42)
ventas = 500 + (fechas.year - 2020) * 50 + np.sin(2 * np.pi * fechas.month / 12) * 100 + np.random.normal(0, 20, len(fechas))
# Crear un DataFrame
df = pd.DataFrame({'fecha': fechas, 'ventas': ventas})
df.set_index('fecha', inplace=True)
print(df.head())
Al aplicar la descomposición, obtenemos los componentes que añadiremos al conjunto de datos:
# Realizar la descomposición aditiva
descomp = seasonal_decompose(df['ventas'], model='additive', period=12)
# Extraer los componentes
df['tendencia'] = descomp.trend
df['estacionalidad'] = descomp.seasonal
df['residuo'] = descomp.resid
# Eliminar filas con valores faltantes
df.dropna(inplace=True)
print(df.head())
Ahora, utilizamos las características de tendencia y estacionalidad para entrenar un modelo de regresión. Definimos las variables predictoras y la variable objetivo:
# Definir las características (X) y la variable objetivo (y)
X = df[['tendencia', 'estacionalidad']]
y = df['ventas']
Dividimos los datos en conjuntos de entrenamiento y prueba, respetando el orden temporal para evitar fugas de información:
# Dividir en entrenamiento y prueba
train_size = int(len(df) * 0.8)
X_train, X_test = X.iloc[:train_size], X.iloc[train_size:]
y_train, y_test = y.iloc[:train_size], y.iloc[train_size:]
Entrenamos un modelo usando RandomForestRegressor de Scikit-Learn:
from sklearn.ensemble import RandomForestRegressor
# Crear y entrenar el modelo
modelo = RandomForestRegressor(n_estimators=100, random_state=42)
modelo.fit(X_train, y_train)
# Realizar predicciones
y_pred = modelo.predict(X_test)
Evaluamos el rendimiento del modelo utilizando métricas como el Error Medio Absoluto (MAE) y la Raíz del Error Cuadrático Medio (RMSE):
from sklearn.metrics import mean_absolute_error, mean_squared_error
mae = mean_absolute_error(y_test, y_pred)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
print(f'Error Medio Absoluto (MAE): {mae:.2f}')
print(f'Raíz del Error Cuadrático Medio (RMSE): {rmse:.2f}')
Salida:
Para capturar patrones más complejos en la estacionalidad, podemos utilizar la Transformada de Fourier y añadir componentes sinusoidales como características adicionales. Esto es especialmente útil cuando existen múltiples ciclos estacionales o no están alineados perfectamente.
# Función para generar componentes de Fourier
def generar_componentes_fourier(df, periodo, n_harm):
N = len(df)
t = np.arange(N)
componentes_fourier_df = pd.DataFrame(index=df.index)
for i in range(1, n_harm + 1):
componentes_fourier_df[f'sin_{i}'] = np.sin(2 * np.pi * i * t / periodo)
componentes_fourier_df[f'cos_{i}'] = np.cos(2 * np.pi * i * t / periodo)
return componentes_fourier_df
# Generar componentes de Fourier
componentes_fourier_df = generar_componentes_fourier(df, periodo=12, n_harm=3)
# Añadir las nuevas características
X = pd.concat([X, componentes_fourier_df], axis=1)
print(X.head())
Actualizamos los conjuntos de entrenamiento y prueba:
# Actualizar conjuntos de datos
X_train, X_test = X.iloc[:train_size], X.iloc[train_size:]
# Reentrenar el modelo
modelo.fit(X_train, y_train)
# Predecir y evaluar
y_pred = modelo.predict(X_test)
mae = mean_absolute_error(y_test, y_pred)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
print(f'Nuevo MAE con componentes de Fourier: {mae:.2f}')
print(f'Nuevo RMSE con componentes de Fourier: {rmse:.2f}')
Salida:
Otra técnica para extraer la tendencia es mediante suavizados, como el Filtro de Hodrick-Prescott. Este filtro permite separar la serie en una tendencia de largo plazo y un componente cíclico.
from statsmodels.tsa.filters.hp_filter import hpfilter
# Aplicar el filtro de Hodrick-Prescott
tendencia_hp, ciclo_hp = hpfilter(df['ventas'], lamb=1600)
# Añadir la tendencia al DataFrame
df['tendencia_hp'] = tendencia_hp
# Añadir al conjunto de características
X['tendencia_hp'] = df['tendencia_hp']
# Actualizar conjuntos de entrenamiento y prueba
X_train, X_test = X.iloc[:train_size], X.iloc[train_size:]
# Reentrenar y evaluar el modelo
modelo.fit(X_train, y_train)
y_pred = modelo.predict(X_test)
mae = mean_absolute_error(y_test, y_pred)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
print(f'MAE con Filtro HP: {mae:.2f}')
print(f'RMSE con Filtro HP: {rmse:.2f}')
Salida:
El uso del Descompositor STL (Seasonal-Trend decomposition using Loess) es otra opción para extraer componentes de tendencia y estacionalidad de manera robusta.
from statsmodels.tsa.seasonal import STL
# Aplicar el descompositor STL
stl = STL(df['ventas'], period=12)
resultado = stl.fit()
# Añadir los componentes al DataFrame
df['tendencia_stl'] = resultado.trend
df['estacionalidad_stl'] = resultado.seasonal
# Añadir al conjunto de características
X['tendencia_stl'] = df['tendencia_stl']
X['estacionalidad_stl'] = df['estacionalidad_stl']
# Actualizar conjuntos de entrenamiento y prueba
X_train, X_test = X.iloc[:train_size], X.iloc[train_size:]
# Reentrenar y evaluar
modelo.fit(X_train, y_train)
y_pred = modelo.predict(X_test)
mae = mean_absolute_error(y_test, y_pred)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
print(f'MAE con STL: {mae:.2f}')
print(f'RMSE con STL: {rmse:.2f}')
Salida:
La incorporación de componentes estacionales y de tendencia como características en los modelos de aprendizaje automático mejora significativamente su capacidad para predecir eventos futuros. Es recomendable experimentar con diferentes métodos de extracción y combinarlos según la naturaleza de los datos. Además, es importante asegurar que todas las variables estén alineadas temporalmente y manejar adecuadamente los valores faltantes resultantes de los procesos de descomposición.
Incorporación de variables externas y eventos especiales
En el análisis de series temporales, la incorporación de variables externas y eventos especiales es esencial para capturar factores que influyen en la serie pero que no están representados por ella misma. Estas variables, también conocidas como exógenas, pueden mejorar significativamente el rendimiento de los modelos de aprendizaje automático al proporcionar información adicional que explica variaciones en los datos.
Las variables externas son factores externos que afectan a la serie temporal, como indicadores económicos, condiciones meteorológicas, campañas publicitarias o precios de materias primas. Por otro lado, los eventos especiales se refieren a fechas o sucesos particulares que pueden causar cambios abruptos en la serie, como festivos, promociones o eventos excepcionales.
A continuación, se presenta un ejemplo completo de cómo incorporar variables externas y eventos especiales en un modelo de predicción de demanda utilizando Python 3.13 y Scikit-Learn 1.5.2.
Ejemplo práctico: Predicción de la demanda energética con variables exógenas
Supongamos que queremos predecir la demanda energética diaria de una ciudad, y sabemos que esta demanda está influenciada por variables externas como la temperatura ambiental y eventos especiales como días festivos.
Primero, cargamos los datos de demanda y las variables externas:
import pandas as pd
import numpy as np
# Cargar datos de demanda energética
# Supongamos que tenemos un archivo 'demanda_energetica.csv' con columnas 'fecha' y 'demanda_mwh'
df_demanda = pd.read_csv('demanda_energetica.csv', parse_dates=['fecha'])
# Cargar datos de temperatura
# Archivo 'temperatura.csv' con columnas 'fecha' y 'temperatura_celsius'
df_temperatura = pd.read_csv('temperatura.csv', parse_dates=['fecha'])
# Cargar datos de festivos
# Archivo 'festivos.csv' con columna 'fecha' que indica días festivos
df_festivos = pd.read_csv('festivos.csv', parse_dates=['fecha'])
Unimos los datasets en un único DataFrame:
# Unir demanda y temperatura por fecha
df = pd.merge(df_demanda, df_temperatura, on='fecha', how='left')
# Crear una variable binaria para indicar si el día es festivo
df['es_festivo'] = df['fecha'].isin(df_festivos['fecha']).astype(int)
# Establecer la columna 'fecha' como índice
df.set_index('fecha', inplace=True)
print(df.head())
Creación de variables adicionales y preprocesamiento
Además de las variables externas incorporadas, podemos generar características adicionales, como el día de la semana, para ayudar al modelo a capturar patrones temporales:
# Crear variable 'dia_semana'
df['dia_semana'] = df.index.weekday # 0: Lunes, 6: Domingo
# Crear variables dummies para 'dia_semana'
df = pd.get_dummies(df, columns=['dia_semana'], drop_first=True)
# Manejo de valores faltantes en 'temperatura_celsius'
df['temperatura_celsius'] = df['temperatura_celsius'].ffill()
print(df.head())
División de los datos en entrenamiento y prueba
Es importante mantener el orden cronológico al dividir los datos para evitar la fuga de información:
# Dividir en conjuntos de entrenamiento y prueba (80% entrenamiento)
train_size = int(len(df) * 0.8)
df_train = df.iloc[:train_size]
df_test = df.iloc[train_size:]
# Separar características (X) y variable objetivo (y)
X_train = df_train.drop(columns=['demanda_mwh'])
y_train = df_train['demanda_mwh']
X_test = df_test.drop(columns=['demanda_mwh'])
y_test = df_test['demanda_mwh']
Entrenamiento del modelo
Utilizaremos RandomForestRegressor para predecir la demanda energética:
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_absolute_error, mean_squared_error
# Crear y entrenar el modelo
modelo = RandomForestRegressor(n_estimators=100, random_state=42)
modelo.fit(X_train, y_train)
# Realizar predicciones
y_pred = modelo.predict(X_test)
Evaluación del modelo
Evaluamos el rendimiento del modelo utilizando métricas apropiadas:
# Calcular métricas de evaluación
mae = mean_absolute_error(y_test, y_pred)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
print(f'Error Medio Absoluto (MAE): {mae:.2f} MWh')
print(f'Raíz del Error Cuadrático Medio (RMSE): {rmse:.2f} MWh')
Salida:
Importancia de las variables
Analizamos la importancia de las características para entender cuáles tienen mayor impacto en la predicción:
import matplotlib.pyplot as plt
# Obtener importancias de las características
importancias = modelo.feature_importances_
indices = np.argsort(importancias)[::-1]
nombres_caracteristicas = X_train.columns[indices]
# Crear gráfico de barras
plt.figure(figsize=(10,6))
plt.title('Importancia de las Características')
plt.bar(range(len(importancias)), importancias[indices])
plt.xticks(range(len(importancias)), nombres_caracteristicas, rotation=90)
plt.tight_layout()
plt.show()
Incorporación de eventos especiales
Además de los días festivos, podemos incluir otros eventos especiales como olas de calor, campañas publicitarias o eventos deportivos que puedan afectar a la demanda. Supongamos que tenemos un DataFrame df_eventos
con una columna 'fecha' y 'tipo_evento':
# Cargar datos de eventos especiales
# Archivo 'eventos_especiales.csv' con columnas 'fecha' y 'tipo_evento'
df_eventos = pd.read_csv('eventos_especiales.csv', parse_dates=['fecha'])
# Unir con el DataFrame principal
df = df.join(df_eventos.set_index('fecha'), on='fecha')
# Reemplazar valores faltantes en 'tipo_evento' por 'ninguno'
df['tipo_evento'] = df['tipo_evento'].fillna('ninguno')
# Crear variables dummies para 'tipo_evento'
df = pd.get_dummies(df, columns=['tipo_evento'], drop_first=True)
print(df.head())
Actualización del modelo con variables de eventos
Repetimos el proceso de división y reentrenamiento del modelo con las nuevas características:
# Actualizar conjuntos de entrenamiento y prueba
X_train = df.iloc[:train_size].drop(columns=['demanda_mwh'])
y_train = df.iloc[:train_size]['demanda_mwh']
X_test = df.iloc[train_size:].drop(columns=['demanda_mwh'])
y_test = df.iloc[train_size:]['demanda_mwh']
# Reentrenar el modelo
modelo.fit(X_train, y_train)
# Realizar nuevas predicciones
y_pred = modelo.predict(X_test)
# Recalcular métricas de evaluación
mae = mean_absolute_error(y_test, y_pred)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
print(f'Nuevo MAE: {mae:.2f} MWh')
print(f'Nuevo RMSE: {rmse:.2f} MWh')
Salida:
Uso de variables retrasadas y ventanas móviles con variables externas
Podemos combinar las variables externas con técnicas de variables de retraso y ventanas móviles para capturar efectos retardados y acumulativos:
# Crear variable de temperatura retrasada un día
df['temp_lag1'] = df['temperatura_celsius'].shift(1)
# Crear media móvil de temperatura de 3 días
df['temp_media_movil_3'] = df['temperatura_celsius'].rolling(window=3).mean()
# Eliminar valores faltantes resultantes
df.dropna(inplace=True)
# Actualizar conjuntos de datos
train_size = int(len(df) * 0.8)
X_train = df.iloc[:train_size].drop(columns=['demanda_mwh'])
y_train = df.iloc[:train_size]['demanda_mwh']
X_test = df.iloc[train_size:].drop(columns=['demanda_mwh'])
y_test = df.iloc[train_size:]['demanda_mwh']
# Reentrenar el modelo
modelo.fit(X_train, y_train)
# Realizar predicciones
y_pred = modelo.predict(X_test)
# Evaluar el modelo
mae = mean_absolute_error(y_test, y_pred)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
print(f'MAE con variables retrasadas y medias móviles: {mae:.2f} MWh')
print(f'RMSE con variables retrasadas y medias móviles: {rmse:.2f} MWh')
Salida:
Consideraciones importantes
Es crucial asegurar la alineación temporal entre las variables externas y la serie temporal principal. Las variables externas deben estar disponibles en el mismo intervalo de tiempo y frecuencia que los datos que se desean predecir. Si alguna variable externa tiene una frecuencia diferente (por ejemplo, mensual en lugar de diaria), es necesario ajustar esta periodicidad mediante técnicas de resampling o interpolación.
También es importante evitar la fuga de información; al crear variables retrasadas o medias móviles, se debe tener cuidado de no usar información futura en el modelado.
Además, al incorporar variables categóricas con muchos niveles (como tipos de eventos), es recomendable considerar técnicas de codificación apropiadas para evitar la alta dimensionalidad, como la codificación por frecuencia o el Target Encoding.
Ejercicios de esta lección Ingeniería de características para series temporales
Evalúa tus conocimientos de esta lección Ingeniería de características para series temporales con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.
Todas las lecciones de ScikitLearn
Accede a todas las lecciones de ScikitLearn y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Aprendizaje Automático
Introducción Y Entorno
Introducción E Instalación
Introducción Y Entorno
Introducción Al Preprocesamiento De Datos
Preprocesamiento De Datos
Identificación Y Tratamiento De Valores Faltantes
Preprocesamiento De Datos
Escalado De Datos
Preprocesamiento De Datos
Normalización De Datos
Preprocesamiento De Datos
Codificación De Variables Categóricas
Preprocesamiento De Datos
Ingeniería De Características
Preprocesamiento De Datos
Selección De Características
Preprocesamiento De Datos
Extracción De Características
Preprocesamiento De Datos
Particionamiento De Datos
Preprocesamiento De Datos
Preprocesamiento De Datos Desbalanceados
Preprocesamiento De Datos
Introducción A La Regresión
Regresión
Regresión Lineal
Regresión
Regresión Knn Kneighborsregressor
Regresión
Regresión Svm Con Svr
Regresión
Regresión Con Árboles Decisiontreeregressor
Regresión
Regresión Con Algoritmos De Conjunto
Regresión
Introducción A La Clasificación
Clasificación
Clasificación Con Regresión Logística
Clasificación
Clasificación Knn Kneighborsclassifier
Clasificación
Clasificación Svm Con Svc
Clasificación
Clasificación Con Árboles Decisiontreeclassifier
Clasificación
Clasificación Con Algoritmos De Conjunto
Clasificación
Reducción De La Dimensionalidad Con Pca
Aprendizaje No Supervisado
Clustering Con Kmeans
Aprendizaje No Supervisado
Clustering Jerárquico
Aprendizaje No Supervisado
Clustering De Densidad Con Dbscan
Aprendizaje No Supervisado
Preprocesamiento De Textos Para Nlp
Nlp
Representación De Texto Y Extracción De Características
Nlp
Clasificación De Texto Con Scikit Learn
Nlp
Análisis De Sentimiento
Nlp
Técnicas Avanzadas De Extracción De Características
Nlp
Introducción Al Análisis De Series Temporales
Series Temporales
Preprocesamiento De Datos De Series Temporales
Series Temporales
Ingeniería De Características Para Series Temporales
Series Temporales
Transformación Y Escalado De Series Temporales
Series Temporales
Validación Y Evaluación De Modelos En Series Temporales
Series Temporales
Validación Y Evaluación De Modelos
Validación De Modelos
Técnicas De Validación Cruzada
Validación De Modelos
Métricas De Regresión
Validación De Modelos
Métricas De Clasificación
Validación De Modelos
Ajuste De Hiperparámetros
Validación De Modelos
Introducción A Pipelines
Pipelines Y Despliegue
Creación De Pipelines Básicos
Pipelines Y Despliegue
Preprocesamiento De Datos Con Pipelines
Pipelines Y Despliegue
Pipelines Y Validación Cruzada
Pipelines Y Despliegue
Pipelines Con Columntransformer
Pipelines Y Despliegue
Exportar E Importar Pipelines
Pipelines Y Despliegue
Objetivos de aprendizaje de esta lección
- Comprender la importancia de las variables de retraso en el análisis de series temporales.
- Aprender a utilizar Python y pandas para crear lags en series temporales.
- Conocer el proceso de eliminación de valores faltantes generados por los lags.
- Desarrollar habilidades para dividir datos en conjuntos de entrenamiento y prueba respetando la secuencia temporal.
- Entrenar modelos de regresión con lags para predecir series temporales.
- Evaluar el rendimiento de modelos usando métricas como MAE y RMSE.
- Experimentar con diferentes cantidades de lags para optimizar modelos de predicción.