--- name: stl_decompose kind: function lang: py domain: datascience version: "1.0.0" purity: pure signature: "def stl_decompose(values: list, period: int = None, robust: bool = True) -> dict" description: "Descomposicion STL (Seasonal-Trend using Loess, statsmodels) de una serie temporal en tendencia, estacional y resto. Si period es None lo infiere por autocorrelacion. Devuelve las 3 componentes (o estadisticos si son largas), mas la fuerza de tendencia y de estacionalidad de Hyndman (1 - Var(resto)/Var(resto+componente)). Descarta None/NaN; serie corta (<2*period) -> nota." tags: [statistics, timeseries, decomposition, stl, seasonality, trend, eda, forecasting, python] uses_functions: [] uses_types: [] returns: [] returns_optional: false error_type: "" imports: [math, numpy, statsmodels] params: - name: values desc: "serie temporal de valores numericos en orden cronologico. None/NaN/infinitos/no-numericos se descartan antes de descomponer." - name: period desc: "periodo estacional (observaciones por ciclo, p.ej. 12 para mensual con estacionalidad anual). Si None se infiere por autocorrelacion; si no hay periodo claro devuelve nota." - name: robust desc: "si True (default) usa el ajuste robusto de STL, que reduce el efecto de outliers sobre tendencia y estacionalidad." output: "dict con 'period' usado, 'period_inferred' (bool), 'trend'/'seasonal'/'resid' (cada uno min/max/mean/std + values si la serie es corta, si no None), 'trend_strength' y 'seasonal_strength' (medidas de Hyndman en [0,1]). Serie insuficiente o sin periodo inferible: dict con 'note' y strengths en None. Nunca lanza excepcion." tested: true tests: ["test_serie_con_tendencia_y_estacionalidad", "test_fuerza_estacional_alta_con_estacionalidad_fuerte", "test_infiere_periodo_si_none", "test_serie_corta_devuelve_nota", "test_muestra_insuficiente_devuelve_nota", "test_descarta_none_y_nan", "test_serie_larga_resume_sin_values"] test_file_path: "python/functions/datascience/stl_decompose_test.py" file_path: "python/functions/datascience/stl_decompose.py" --- ## Ejemplo ```python from datascience import stl_decompose import numpy as np # Serie mensual = tendencia lineal + ciclo estacional anual (periodo 12) + ruido rng = np.random.default_rng(0) n = 120 serie = [0.3 * i + 10 * np.sin(2 * np.pi * i / 12) + rng.normal(0, 1) for i in range(n)] res = stl_decompose(serie, period=12) res["trend_strength"] # -> ~0.99 (tendencia clara) res["seasonal_strength"] # -> ~0.98 (estacionalidad clara) res["seasonal"]["values"][:3] # primeras 3 muestras de la componente estacional # Sin pasar periodo: lo infiere por autocorrelacion stl_decompose(serie)["period_inferred"] # -> True ``` ## Cuando usarla Cuando quieres separar una serie temporal en sus partes para entenderla o prepararla para modelar: cuanta de su variacion es tendencia de fondo, cuanta es ciclo estacional repetitivo y cuanta es ruido. Util en EDA para decidir si merece la pena desestacionalizar antes de comparar periodos, para detectar un cambio de tendencia, o para extraer features (las fuerzas de tendencia/estacionalidad de Hyndman resumen la serie en dos numeros comparables entre series). ## Gotchas - Es pura pero importa `statsmodels.tsa.seasonal.STL` y `numpy` (en `python/.venv`). - STL exige al menos **dos ciclos completos**: con `n < 2*period` devuelve una nota en vez de descomponer. Para datos mensuales con estacionalidad anual (period=12) necesitas >= 24 meses. - La inferencia automatica de `period` busca el pico de autocorrelacion; es heuristica. Si conoces el periodo real (12 mensual, 7 diario-semanal, 24 horario-diario), pasalo explicito: es mas fiable. - Las componentes largas (> 200 puntos) se resumen en estadisticos y `values` queda en `None` para no inflar el payload; las cortas vienen completas. - Las fuerzas estan en `[0,1]` por construccion (se recortan a 0 si la varianza del resto supera la de resto+componente, lo que indica componente inexistente).