Series temporales

Librerías de Python para forecasting clásico, ML unificado y deep learning

python
time-series
forecasting
statsmodels
prophet
deep-learning
ml
Referencia comentada de las librerías Python que estructuran el flujo de análisis y predicción de series temporales: estadística clásica, wrappers de ML, modelos profundos y extracción de features.

Sobre series temporales en Python

El ecosistema Python para series temporales se entiende mejor leyéndolo como tres eras superpuestas, cada una con su propia filosofía y su propia trampa principal:

  • Estadística clásica (años 70-2000). ARIMA, ETS, VAR, descomposición STL. La referencia operativa en Python es statsmodels, con pmdarima como envoltorio para la selección automática estilo auto.arima. La virtud es la interpretabilidad, coeficientes, intervalos de confianza, tests de raíz unitaria, y el coste es el trabajo manual de diagnóstico (estacionariedad, autocorrelación residual, diferenciación).
  • Wrappers de ML (2017-2022). prophet (Meta) populariza el enfoque aditivo trend + seasonality + holidays para usuarios no estadísticos. sktime propone una API unificada estilo scikit-learn para forecasting, clasificación y anomaly detection sobre series. tsfresh automatiza la extracción de cientos de features estadísticas para empujar el problema a un pipeline tabular clásico.
  • Deep learning y forecasting probabilístico (2020-). darts integra modelos clásicos y neuronales (N-BEATS, TFT, RNN, TCN) con un objeto TimeSeries propio y soporte nativo de cuantiles. neuralforecast (Nixtla) ofrece SOTA neural baselines (NHITS, TFT, PatchTST) con foco en rendimiento y multi-series. Son la opción razonable cuando tienes muchas series correlacionadas o necesitas distribuciones predictivas, no solo puntuales.

Convivencias prácticas:

# Versiones de referencia para esta página
# pip install statsmodels==0.14 prophet==1.1 sktime==0.32 darts==0.31 \
#             neuralforecast==1.7 pmdarima==2.0 tsfresh==0.20

Tres principios que conviene tener interiorizados antes de elegir librería:

  • El índice manda. Un DatetimeIndex con frecuencia explícita (asfreq("D"), "H", "MS"…) y sin duplicados es prerrequisito para casi todo. La mayoría de errores opacos en producción nacen de timestamps irregulares, gaps silenciosos o zonas horarias inconsistentes.
  • Validación temporal, no aleatoria. Cualquier split que no respete el orden cronológico es data leakage. Usa TimeSeriesSplit (sklearn) o ExpandingWindowSplitter / SlidingWindowSplitter (sktime). Nunca train_test_split aleatorio sobre series temporales.
  • Multi-horizonte vs recursivo. Predecir 24 pasos puede hacerse con un modelo direct (un modelo por horizonte o un output multi-step) o iterando un modelo one-step ahead. La elección afecta la propagación del error y el coste de entrenamiento. Conviene decidirla antes de tocar código.

Esta página cataloga siete librerías que cubren los flujos más habituales. El orden refleja jerarquía conceptual: primero la estadística clásica (statsmodels, pmdarima), después los wrappers (prophet, sktime), luego deep learning (darts, neuralforecast) y finalmente la extracción de features (tsfresh).


statsmodels

statsmodels es la referencia operativa de la estadística clásica de series temporales en Python. Incluye ARIMA / SARIMA / SARIMAX, suavizado exponencial (ETS), VAR / VECM, modelos de espacio de estados, descomposición STL, tests de raíz unitaria (ADF, KPSS) y herramientas de diagnóstico (ACF, PACF, Ljung-Box, Jarque-Bera).

Es la pieza más baja del stack. Muchas otras librerías (pmdarima, sktime, darts en su rama clásica) envuelven o reimplementan partes de statsmodels. Internalizar su API ahorra tiempo a lo largo de cualquier proyecto serio.

Cuándo usarlo

  • ARIMA / SARIMA / SARIMAX con variables exógenas, cuando necesitas interpretabilidad de coeficientes y diagnóstico residual.
  • ETS y modelos de espacio de estados (Kalman) para series con estructura clara de nivel / tendencia / estacionalidad.
  • VAR / VECM para sistemas multivariantes con interés en relaciones de cointegración.
  • Tests formales de estacionariedad (ADF, KPSS, Phillips-Perron) y changepoints básicos.

Cuándo NO usarlo

  • Muchas series en paralelo. statsmodels ajusta cada modelo individualmente. Para miles de series, neuralforecast o statsforecast (Nixtla) son órdenes de magnitud más rápidos.
  • Modelos profundos (RNN, TCN, Transformers). Quedan fuera del alcance, usa darts o neuralforecast.
  • Forecasting probabilístico complejo (cuantiles condicionales, distribuciones flexibles). Los intervalos de confianza de statsmodels son válidos pero asumen normalidad de residuos.

Conceptos clave

  • SARIMAX(endog, exog, order=(p,d,q), seasonal_order=(P,D,Q,s)) es la API moderna. Reemplaza a la antigua ARIMA y soporta variables exógenas de serie.
  • model.fit() devuelve un SARIMAXResults. El forecast(steps) produce predicciones puntuales y get_forecast(steps).conf_int() los intervalos.
  • Diferenciación. El parámetro d (y D estacional) aplica diferenciación interna. Si diferenciaste tú la serie antes de pasarla al modelo, no la vuelvas a diferenciar dentro, un error clásico.
  • seasonal_decompose y STL (más robusto) separan tendencia, estacionalidad y residuo. STL admite estacionalidad no entera y es preferible para series irregulares.
  • Diagnóstico residual: plot_diagnostics() cubre ACF, histograma, Q-Q y test de Ljung-Box. Si los residuos siguen autocorrelacionados, el modelo está mal especificado.

Patrón mínimo

import pandas as pd
import statsmodels.api as sm

# y: pd.Series con DatetimeIndex y frecuencia regular (p. ej. "MS" mensual)
y = pd.read_csv("ventas.csv", parse_dates=["fecha"], index_col="fecha")["y"].asfreq("MS")

train, test = y.iloc[:-12], y.iloc[-12:]

# SARIMA con estacionalidad anual
model = sm.tsa.SARIMAX(
    train,
    order=(1, 1, 1),
    seasonal_order=(0, 1, 1, 12),
    enforce_stationarity=False,
    enforce_invertibility=False,
)
res = model.fit(disp=False)

fcst = res.get_forecast(steps=12)
mean = fcst.predicted_mean
ci   = fcst.conf_int(alpha=0.05)

Trampas habituales

  • freq faltante. Si el índice no tiene frecuencia (y.index.freq is None), statsmodels emite warnings y los horizontes pueden no respetar el calendario. Forzar .asfreq("MS") o equivalente al cargar es disciplina mínima.
  • Doble diferenciación. Pasar una serie ya diferenciada con d=1 la diferencia otra vez. Si la has diferenciado a mano, usa d=0 o pasa la serie original.
  • Predicción en niveles vs diferencias. Si entrenaste sobre una transformación (log, diff), las predicciones están en la misma escala. Hay que invertir la transformación con cuidado para obtener intervalos en escala original, no es la simple exp(...) en general.
  • enforce_stationarity=True por defecto puede fallar con datos reales. Suele ser razonable desactivarlo durante el ajuste y comprobar después.

Enlaces

Relacionados en esta página

  • pmdarima, selección automática de órdenes ARIMA estilo auto.arima.
  • sktime, envuelve statsmodels bajo una API unificada.

pmdarima

pmdarima es el port directo a Python de auto.arima (Hyndman, R forecast). Selecciona automáticamente los órdenes (p, d, q) y (P, D, Q, s) minimizando AIC / BIC y validando con tests de raíz unitaria (ADF, KPSS). El motor de ajuste es statsmodels. pmdarima aporta la búsqueda y una API estilo scikit-learn (.fit(), .predict()).

Es la opción razonable cuando necesitas un baseline ARIMA decente sin invertir tiempo en diagnóstico manual. No sustituye al análisis cuidadoso, pero produce un punto de partida muy aceptable.

Cuándo usarlo

  • Baseline ARIMA / SARIMA automático sobre series individuales.
  • Comparaciones rápidas en benchmarks contra modelos más sofisticados.
  • Pipelines donde no hay tiempo (ni necesidad) de tunear el modelo a mano.

Cuándo NO usarlo

  • Series muy largas o muy numerosas. La búsqueda stepwise es lenta. Para miles de series, statsforecast (Nixtla) ejecuta AutoARIMA mucho más rápido y con paralelización.
  • Estructuras no ARIMA. Si la serie es claramente no lineal o tiene cambios de régimen, ningún auto.arima lo resolverá. Pasa a Prophet, modelos de espacio de estados o deep learning.
  • Producción crítica. Por dependencia de numpy.distutils y compatibilidad ocasionalmente frágil con versiones recientes de Python / numpy, fija versiones explícitas si lo usas en pipelines productivos.

Conceptos clave

  • auto_arima(y, seasonal=True, m=12, ...) ejecuta la búsqueda. m es la longitud del ciclo estacional (12 = mensual con anual, 7 = diario con semanal, 24 = horario con diario).
  • Por defecto usa búsqueda stepwise (Hyndman-Khandakar). Pasar stepwise=False activa la búsqueda exhaustiva, más exacta y mucho más lenta.
  • exogenous (renombrado X en versiones recientes) acepta variables exógenas, replicando SARIMAX.
  • El objeto devuelto es una ARIMA (envoltorio sobre SARIMAXResults) que expone .predict(n_periods) y .predict(..., return_conf_int=True).

Patrón mínimo

import pmdarima as pm

# y: pd.Series con DatetimeIndex y frecuencia "MS"
train, test = y.iloc[:-12], y.iloc[-12:]

model = pm.auto_arima(
    train,
    seasonal=True, m=12,
    stepwise=True,
    suppress_warnings=True,
    error_action="ignore",
)

print(model.summary())

pred, ci = model.predict(n_periods=12, return_conf_int=True, alpha=0.05)

Trampas habituales

  • m incorrecto. El parámetro estacional m debe corresponderse con la frecuencia real del ciclo, no con la frecuencia muestral. Para datos semanales con estacionalidad anual, m=52. Para datos diarios con estacionalidad semanal, m=7. Equivocarlo invalida la selección.
  • stepwise=True no garantiza el óptimo. Es una búsqueda greedy. En series exigentes, comparar con stepwise=False puede revelar modelos mejores.
  • Compatibilidad numpy / Python. Fija versiones en requirements.txt. pmdarima ha sufrido roturas históricas con releases de numpy.

Enlaces

Relacionados en esta página

  • statsmodels, motor subyacente.
  • sktime, alternativa con API unificada y wrapper a AutoARIMA.

Prophet

Prophet (Meta) es un modelo aditivo de descomposición que separa la serie en tendencia (lineal o logística), estacionalidad (Fourier), efectos de festivos y eventos especiales, todo dentro de un marco bayesiano ajustado con Stan. Está diseñado para producir baselines decentes con poca intervención, tolerar outliers y gaps, y permitir incorporar conocimiento de dominio (festivos, changepoints) de forma explícita.

Su público objetivo natural es el analista de negocio que necesita predicciones razonables sobre series con estacionalidad fuerte y festivos relevantes, sin invertir en diagnóstico ARIMA.

Cuándo usarlo

  • Series con estacionalidades múltiples (diaria + semanal + anual) y / o festivos importantes, la API para inyectar calendarios de festivos por país es la mejor del ecosistema.
  • Tendencias con cambios suaves y changepoints que quieres modelar explícitamente.
  • Cuando necesitas un baseline rápido y comunicable a stakeholders no técnicos (descomposición gráfica clara).

Cuándo NO usarlo

  • Series cortas o sin estacionalidad clara. El modelo asume estructura aditiva. Si la serie es ruido sobre una media plana, ARIMA / ETS clásicos suelen ser mejores.
  • Forecasting probabilístico riguroso. Los intervalos de Prophet son intervalos de incertidumbre simulados sobre la tendencia, no calibrados por validación. Para cuantiles bien calibrados, mejor darts o neuralforecast con quantile loss.
  • Multi-series correlacionadas. Prophet entrena una serie a la vez. Para global models sobre muchas series, pasa a neuralforecast o darts.
  • Dependencias autorregresivas fuertes. Prophet no modela y_{t-1} directamente. Si la serie depende fuertemente de su propio pasado a corto plazo, un ARIMA captará eso mejor.

Conceptos clave

  • DataFrame de entrada con dos columnas exactas: ds (timestamp) e y (valor). La rigidez del esquema es deliberada.
  • growth="linear" (por defecto) o growth="logistic" (con cap y floor) para tendencias acotadas.
  • add_seasonality(name, period, fourier_order) añade estacionalidades a medida (p. ej. mensual con period=30.5).
  • add_country_holidays(country_name="ES") inyecta el calendario nacional. holidays (DataFrame con holiday, ds, opcionalmente lower_window / upper_window) permite festivos personalizados.
  • add_regressor(name) admite variables exógenas (precio, promociones). Deben estar disponibles también en el horizonte de predicción.

Patrón mínimo

import pandas as pd
from prophet import Prophet

df = pd.read_csv("ventas.csv", parse_dates=["fecha"]).rename(
    columns={"fecha": "ds", "valor": "y"}
)

m = Prophet(
    yearly_seasonality=True,
    weekly_seasonality=True,
    daily_seasonality=False,
)
m.add_country_holidays(country_name="ES")
m.fit(df)

future = m.make_future_dataframe(periods=90, freq="D")
fcst   = m.predict(future)

# fcst[["ds", "yhat", "yhat_lower", "yhat_upper"]]
fig = m.plot_components(fcst)

Trampas habituales

  • Doble conteo de festivos. Si add_country_holidays() ya incluye Navidad y tú añades manualmente “Navidad” en holidays=, el efecto se duplica. Comprueba m.train_holiday_names después del fit.
  • Festivos en el horizonte. El calendario debe cubrir también el futuro a predecir. Si tu DataFrame de festivos personalizados solo contiene el pasado, Prophet asumirá cero efecto en el horizonte.
  • cap y floor en crecimiento logístico. Olvidarlos lleva a errores opacos. Si usas growth="logistic", debes incluir cap (y opcionalmente floor) tanto en el training set como en el future.
  • Frecuencia del make_future_dataframe. El parámetro freq debe coincidir con la frecuencia de los datos. Mezclar freq="D" con datos semanales produce predicciones rotas.
  • Variables exógenas en el horizonte. add_regressor exige valores futuros del regresor para predecir. Si no los tienes, no es la herramienta.

Enlaces

Relacionados en esta página

  • statsmodels, alternativa clásica para series con dependencia autorregresiva fuerte.
  • darts, incluye una implementación de Prophet entre sus modelos.

sktime

sktime es la API unificada estilo scikit-learn para series temporales: forecasting, clasificación, regresión, clustering y anomaly detection. Su valor real no son los algoritmos propios sino la interfaz consistente sobre statsmodels, pmdarima, prophet, xgboost, tbats, darts y otros, con cross-validation temporal correcta y pipelines compatibles con sklearn.

Es la elección razonable cuando quieres comparar familias de modelos en igualdad de condiciones, o cuando construyes pipelines reproducibles que combinan transformaciones (detrending, deseasonalizing) con modelos finales.

Cuándo usarlo

  • Benchmarking de varias familias de modelos sobre la misma serie con métricas y validación homogéneas.
  • Pipelines que combinan detrending / deseasonalizing + modelo final como en sklearn.
  • Reduction: convertir un problema de forecasting en uno de regresión tabular usando make_reduction(estimator, window_length, strategy) con un regressor sklearn.
  • Forecasting jerárquico (HierarchicalForecaster) y reconciliación de niveles.

Cuándo NO usarlo

  • Cuando solo necesitas un modelo concreto. Si vas a usar SARIMAX y nada más, ir directo a statsmodels es más simple. La abstracción de sktime se paga en complejidad.
  • Deep learning serio. sktime integra algunas redes pero el centro de gravedad de DL para series está en darts y neuralforecast.
  • Producción de baja latencia. Las capas de envoltura añaden overhead. Para inference a milisegundos, baja al motor subyacente.

Conceptos clave

  • ForecastingHorizon(fh, is_relative=True) define el horizonte como offsets relativos (p. ej. [1, 2, 3]) o timestamps absolutos.
  • Cross-validation: ExpandingWindowSplitter (ventana creciente) y SlidingWindowSplitter (ventana fija). Ambos respetan el orden temporal.
  • ForecastingPipeline encadena transformadores + forecaster como sklearn.Pipeline.
  • make_reduction(estimator, strategy="recursive" | "direct" | "multioutput") adapta un regressor sklearn al problema de forecasting con la estrategia elegida.
  • Métricas dedicadas en sktime.performance_metrics.forecasting: MeanAbsolutePercentageError, MeanAbsoluteScaledError, MeanSquaredError.

Patrón mínimo

from sktime.forecasting.model_selection import ExpandingWindowSplitter
from sktime.forecasting.compose import make_reduction
from sktime.forecasting.naive import NaiveForecaster
from sktime.forecasting.base import ForecastingHorizon
from sktime.performance_metrics.forecasting import MeanAbsoluteScaledError
from sklearn.ensemble import RandomForestRegressor

# y: pd.Series con PeriodIndex o DatetimeIndex regular
fh = ForecastingHorizon(list(range(1, 13)), is_relative=True)

# Baseline naive estacional
naive = NaiveForecaster(strategy="last", sp=12)

# Forecaster por reducción: window de 24 -> RandomForest, estrategia recursive
rf = make_reduction(
    RandomForestRegressor(n_estimators=200, random_state=0),
    window_length=24,
    strategy="recursive",
)

# Validación walk-forward
cv = ExpandingWindowSplitter(initial_window=60, step_length=12, fh=fh)

from sktime.forecasting.model_evaluation import evaluate
results = evaluate(
    forecaster=rf, y=y, cv=cv,
    scoring=MeanAbsoluteScaledError(sp=12),
    return_data=False,
)
print(results)

Trampas habituales

  • PeriodIndex vs DatetimeIndex. sktime prefiere PeriodIndex para muchas operaciones de forecasting. Si trabajas con DatetimeIndex, fija frecuencia explícita y prepárate a convertir.
  • Mezclar APIs. No combines evaluate() (alto nivel) con bucles manuales sobre cv.split() sin entender qué subconjunto de datos ve cada paso. Es la fuente número uno de data leakage en este ecosistema.
  • make_reduction(strategy="recursive") propaga error. Para horizontes largos, considera strategy="direct" (un modelo por horizonte) o "multioutput" si el regressor lo soporta.
  • Compatibilidad de versiones. sktime actualiza dependencias con frecuencia. Fija versiones y revisa el changelog antes de actualizar, la API ha tenido breaking changes notables.

Enlaces

Relacionados en esta página


darts

darts es una librería integrada de forecasting con un objeto propio (TimeSeries, basado en xarray) que unifica modelos clásicos (ARIMA, ExponentialSmoothing, Theta), de gradient boosting (LightGBMModel, XGBModel, CatBoostModel) y profundos (RNNModel, BlockRNNModel, TCNModel, NBEATSModel, TFTModel, TransformerModel, DLinearModel). Soporta global models sobre múltiples series, covariables pasadas y futuras, y forecasting probabilístico (cuantiles, distribuciones) de forma nativa.

Es probablemente la mejor opción cuando necesitas comparar familias muy distintas de modelos con la misma API y outputs probabilísticos coherentes.

Cuándo usarlo

  • Global models sobre muchas series correlacionadas (cientos a miles) con un único modelo neuronal compartido.
  • Forecasting probabilístico (cuantiles, intervalos calibrados) con likelihood=... o num_samples en inference.
  • Comparativa homogénea de ARIMA / ETS / LightGBM / N-BEATS / TFT sobre la misma serie.
  • Covariables pasadas (solo conocidas hasta t) y futuras (conocidas en el horizonte: día de la semana, festivo, promoción planificada).

Cuándo NO usarlo

  • Dataset minúsculo (< 100 puntos). Los modelos neuronales no aportan nada. Quédate en statsmodels o pmdarima.
  • Inference ultraligera. Los wrappers añaden capas. Para servir un ARIMA en milisegundos, baja a statsmodels.
  • Necesidad de inferencia estadística clásica (p-values, intervalos basados en likelihood, tests de hipótesis sobre coeficientes). darts no es esa herramienta.

Conceptos clave

  • TimeSeries es la unidad básica. Se construye con TimeSeries.from_dataframe(df, time_col, value_cols) o from_series. Es inmutable: las transformaciones devuelven nuevos objetos.
  • Covariables: past_covariates (conocidas solo hasta el presente, p. ej. otra serie observada) y future_covariates (conocidas en el horizonte, p. ej. calendario). La distinción es crítica: pasarlas en el slot equivocado es el error número uno.
  • Multi-series: fit([ts1, ts2, ts3], ...) entrena un único modelo global sobre múltiples series. El predict se hace serie a serie o vectorialmente.
  • Probabilístico: num_samples=500 en predict() genera muestras de la distribución predictiva. Con likelihood=QuantileRegression(...) el modelo aprende cuantiles directamente.
  • Scalers (Scaler, BoxCox) son objetos fittable compatibles con multi-series y reversibles automáticamente en predict.

Patrón mínimo

from darts import TimeSeries
from darts.models import NBEATSModel
from darts.dataprocessing.transformers import Scaler
from darts.metrics import mape

series = TimeSeries.from_dataframe(df, time_col="ds", value_cols="y", freq="D")
train, val = series[:-90], series[-90:]

scaler = Scaler()
train_s = scaler.fit_transform(train)
val_s   = scaler.transform(val)

model = NBEATSModel(
    input_chunk_length=60,
    output_chunk_length=14,
    n_epochs=50,
    random_state=0,
)
model.fit(train_s)

fcst_s = model.predict(n=90, num_samples=200)   # forecasting probabilístico
fcst   = scaler.inverse_transform(fcst_s)

print("MAPE:", mape(val, fcst))

Trampas habituales

  • Confundir past_covariates y future_covariates. Si pasas como future_covariates algo que no conoces en el horizonte, estás filtrando información del futuro al entrenamiento, data leakage puro.
  • Longitudes desalineadas. darts exige que las covariables cubran exactamente el rango requerido. Los errores son crípticos. Depura con series.time_index[0], series.time_index[-1].
  • input_chunk_length vs output_chunk_length. El primero es la ventana histórica, el segundo el horizonte interno del modelo. Para predicciones más largas que output_chunk_length, darts itera internamente (estrategia auto-recursiva), el error se propaga.
  • GPU silenciosa. Modelos grandes (TFT, Transformer) tardan mucho sin GPU. pl_trainer_kwargs={"accelerator": "gpu"} activa CUDA si está disponible.
  • Reproducibilidad. Fija random_state y, para PyTorch Lightning bajo el capó, torch.manual_seed. Sin ello, los resultados varían entre runs.

Enlaces

Relacionados en esta página

  • neuralforecast, alternativa centrada en SOTA neural baselines, más rápida en multi-series.
  • sktime, API unificada con menor foco en DL.

neuralforecast

neuralforecast (de Nixtla) es una librería de modelos neuronales SOTA para forecasting con énfasis en velocidad, soporte multi-series nativo y compatibilidad con el resto del stack Nixtla (statsforecast, mlforecast, hierarchicalforecast). Incluye NHITS, N-BEATS, TFT, PatchTST, iTransformer, TimesNet, NLinear, DLinear y más, todos sobre PyTorch Lightning con una API homogénea.

Su diferencial frente a darts es la eficiencia en multi-series (entrenamiento global sobre miles de series con un único modelo) y la integración con el ecosistema Nixtla, que cubre desde ARIMA distribuido (statsforecast) hasta reconciliación jerárquica.

Cuándo usarlo

  • Multi-series a escala (cientos a millones de series). El diseño global model es de primera clase y muy eficiente.
  • Cuando quieres baselines neuronales fuertes (NHITS, PatchTST, TFT) sin escribir PyTorch.
  • Pipelines integrados con statsforecast (clásico) y mlforecast (gradient boosting) para comparar familias.
  • Forecasting probabilístico vía loss=MQLoss(quantiles=[...]).

Cuándo NO usarlo

  • Una sola serie corta. El overhead de PyTorch Lightning no compensa. Usa statsforecast o statsmodels.
  • Interpretabilidad clásica. Los modelos neuronales son opacos. Si necesitas justificar coeficientes a un comité, no es la herramienta.
  • Inferencia bayesiana / intervalos basados en likelihood. Aquí los intervalos vienen de regresión cuantílica, no de un modelo generativo completo.

Conceptos clave

  • Esquema largo (long format): unique_id, ds, y. Cada serie se identifica por unique_id. Multi-series es el caso por defecto, no la excepción.
  • NeuralForecast(models=[...], freq="D") envuelve la lista de modelos. Cada modelo tiene h (horizonte) e input_size propios.
  • Exógenas estáticas (stat_exog_list, p. ej. categoría del producto) e históricas (hist_exog_list) y futuras (futr_exog_list). La distinción es estructuralmente la misma que en darts.
  • Cross-validation: nf.cross_validation(df, n_windows, step_size) ejecuta walk-forward sobre todas las series en paralelo.
  • loss=MQLoss(quantiles=[0.1, 0.5, 0.9]) produce predicciones cuantílicas directamente.

Patrón mínimo

from neuralforecast import NeuralForecast
from neuralforecast.models import NHITS
from neuralforecast.losses.pytorch import MQLoss

# df: columnas unique_id, ds, y (long format)
horizon = 14

models = [
    NHITS(
        h=horizon,
        input_size=2 * horizon,
        loss=MQLoss(quantiles=[0.1, 0.5, 0.9]),
        max_steps=500,
        random_seed=0,
    )
]

nf = NeuralForecast(models=models, freq="D")
nf.fit(df)

fcst = nf.predict()                       # incluye columnas NHITS-q-10, NHITS-q-50, NHITS-q-90
cv   = nf.cross_validation(df, n_windows=4, step_size=horizon)

Trampas habituales

  • input_size mal dimensionado. Si input_size es menor que la estacionalidad principal, el modelo no la captará. Una heurística sólida es input_size >= 2 * estacionalidad.
  • Frecuencia inconsistente. freq="D" exige ds regular diario en todas las series. gaps o duplicados rompen el fit con mensajes confusos. Prefiltrar con mlforecast’s utilities o pandas (asfreq, dropna) es disciplina mínima.
  • GPU silenciosa. Sin accelerator="gpu" en trainer_kwargs, entrena en CPU aunque haya CUDA disponible. Modelos grandes tardan órdenes de magnitud más.
  • Cross-validation con step_size mal elegido. Si step_size < h, los folds se solapan en el horizonte, está bien si lo entiendes, pero las métricas dejan de ser independientes.
  • Reproducibilidad. Fija random_seed por modelo. neuralforecast no propaga un seed global a todos los componentes automáticamente.

Enlaces

Relacionados en esta página

  • darts, alternativa con catálogo similar y TimeSeries propio.
  • sktime, API unificada que también puede orquestar modelos neuronales.

tsfresh

tsfresh es una librería que extrae automáticamente cientos de features estadísticas de cada serie temporal (o de cada ventana) y filtra las relevantes para una variable objetivo mediante tests de hipótesis con corrección por false discovery rate. Su utilidad principal es convertir un problema de series en uno de clasificación o regresión tabular, donde los pipelines clásicos de sklearn / XGBoost se aplican directamente.

No es una librería de forecasting. Es la herramienta cuando lo que necesitas es representación tabular de series para clasificación (anomalía vs normal, fallo vs no fallo, paciente A vs B), regresión sobre features (estimar un parámetro de un proceso a partir de su serie observada) o clustering de series.

Cuándo usarlo

  • Clasificación o regresión donde la unidad de entrada es una serie temporal completa o una ventana, no un punto.
  • Detección de anomalías reducida a clasificación binaria con features extraídas por ventana.
  • Generar features de entrada para un modelo downstream (gradient boosting, red neuronal tabular).
  • Feature engineering exploratorio cuando no sabes qué propiedades estadísticas son relevantes para tu problema.

Cuándo NO usarlo

  • Forecasting puro. tsfresh no predice valores futuros, extrae features del pasado.
  • Series muy largas con poca variación local. Calcular ~750 features sobre series de millones de puntos es prohibitivo en tiempo. Considera muestrear ventanas.
  • Cuando el modelo downstream aprende su propia representación. Si vas a entrenar un Transformer sobre la serie cruda, las features de tsfresh añaden ruido más que valor.

Conceptos clave

  • Entrada en formato largo: DataFrame con id (identifica cada serie / ventana), time, value. Un único DataFrame contiene todas las series.
  • extract_features(df, column_id="id", column_sort="time", column_value="value") produce una matriz tabular id × features.
  • select_features(X, y) aplica tests de hipótesis (Mann-Whitney, Kolmogorov-Smirnov, Kendall, según el tipo de target) con corrección Benjamini-Yekutieli para FDR.
  • Settings: ComprehensiveFCParameters() (default, ~750 features), EfficientFCParameters() (~600, sin las más costosas), MinimalFCParameters() (~10, para sanity check rápido).
  • Paralelización con n_jobs (multiprocessing local) o vía distributor (Dask).

Patrón mínimo

from tsfresh import extract_features, select_features
from tsfresh.feature_extraction import EfficientFCParameters
from tsfresh.utilities.dataframe_functions import impute

# df_long: columnas id, time, value
X = extract_features(
    df_long,
    column_id="id", column_sort="time", column_value="value",
    default_fc_parameters=EfficientFCParameters(),
    n_jobs=4,
)

impute(X)                              # in-place: NaN -> mediana / extremos finitos

# y: pd.Series con índice = id, target binario o continuo
X_filtered = select_features(X, y)

# Pipeline downstream estándar
from sklearn.ensemble import GradientBoostingClassifier
clf = GradientBoostingClassifier(random_state=0).fit(X_filtered, y)

Trampas habituales

  • Coste computacional. ComprehensiveFCParameters sobre miles de series largas puede tardar horas. Empieza con MinimalFCParameters() o EfficientFCParameters() para tener un primer baseline.
  • NaN en el output. Muchas features generan NaN o inf legítimamente. impute() es necesario antes de entregar a sklearn.
  • select_features requiere un y claro. Si tu target es ruidoso o muy desbalanceado, el filtrado pierde sentido. Inspecciona la distribución de y antes.
  • Data leakage por ventanas mal definidas. Si tus ventanas de entrenamiento incluyen información futura respecto al target (p. ej. extraes features de toda la serie y predices su clase), estás filtrando. Las ventanas deben terminar antes del instante a predecir.
  • Compatibilidad de versiones. tsfresh ha cambiado nombres de features entre versiones. Para reproducibilidad, fija versión y guarda la lista de features seleccionadas.

Enlaces

Relacionados en esta página

  • sktime, incluye un transformador TSFreshFeatureExtractor para integrar tsfresh en pipelines sktime.