El método de Holt y la tendencia amortiguada
SES asume un nivel fijo. En cuanto una serie tiene tendencia real, ese supuesto se rompe — ya lo viste en el capítulo principal con el ejemplo de la librería online. El método de Holt resuelve esto añadiendo una segunda ecuación que rastrea la pendiente de la serie junto con su nivel.
Dos ecuaciones en vez de una
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.tsa.holtwinters import Holt
from sklearn.metrics import mean_absolute_error
# Serie con tendencia clara y ruido moderado — 30 observaciones
np.random.seed(11)
n = 30
t = np.arange(n)
serie = pd.Series(50 + 3.2 * t + np.random.normal(0, 6, n), name="serie_con_tendencia")
train = serie.iloc[:-6]
test = serie.iloc[-6:]
modelo_holt = Holt(train, initialization_method="estimated").fit()
print(f"alpha (nivel) : {modelo_holt.params['smoothing_level']:.3f}")
print(f"beta (tendencia) : {modelo_holt.params['smoothing_trend']:.3f}")
Holt mantiene dos estados internos que se actualizan en cada paso:
- Nivel (): igual que en SES, pero ahora sumando la tendencia estimada del paso anterior antes de mezclar con el nuevo dato.
- Tendencia (): la diferencia entre el nivel actual y el anterior, suavizada con su propio parámetro β.
El pronóstico a h pasos es simplemente extrapolar en línea recta desde el último nivel y la última tendencia estimados:
prediccion_holt = modelo_holt.forecast(6)
mae_holt = mean_absolute_error(test, prediccion_holt)
# Comparamos contra SES puro, que ignoraría la tendencia
from statsmodels.tsa.holtwinters import SimpleExpSmoothing
modelo_ses = SimpleExpSmoothing(train, initialization_method="estimated").fit()
mae_ses = mean_absolute_error(test, modelo_ses.forecast(6))
print(f"MAE Holt: {mae_holt:.2f}")
print(f"MAE SES : {mae_ses:.2f}")
Sobre una serie con tendencia real, Holt gana con claridad: SES se queda anclado al nivel pasado mientras la serie sigue subiendo, y su error crece con cada paso hacia adelante.
El problema de extrapolar en línea recta
La fórmula tiene una consecuencia incómoda: cuanto más lejos pronosticas, más se aleja el pronóstico, sin ningún límite. Es razonable a 1 o 2 pasos, pero a 24 pasos vista, pocas series reales sostienen la misma pendiente indefinidamente.
pronostico_largo = modelo_holt.forecast(24)
plt.figure(figsize=(10, 4))
plt.plot(train, color="#1a1a1a", label="Histórico")
plt.plot(pronostico_largo, color="#ffcc00", linestyle="--", label="Pronóstico Holt (24 pasos)")
plt.legend()
plt.title("La línea recta se aleja del histórico sin límite")
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
Observa la pendiente del histórico frente a la del pronóstico: son idénticas por construcción. Si la serie real se está desacelerando —muchas series de crecimiento lo hacen, tarde o temprano— Holt sin amortiguar sobreestimará sistemáticamente el futuro lejano.
Tendencia amortiguada: frenar la extrapolación
La solución es introducir un parámetro φ (phi), entre 0 y 1, que reduce el peso de la tendencia en cada paso adicional hacia el futuro:
Con φ = 1 recuperas a Holt sin amortiguar (la tendencia no pierde fuerza nunca). Con φ más bajo, la suma geométrica converge a un valor máximo, y el pronóstico se aplana en vez de seguir creciendo sin fin.
from statsmodels.tsa.holtwinters import Holt
# Fijamos distintos valores de phi para ver el efecto directamente
for phi in [1.0, 0.9, 0.8, 0.6]:
if phi == 1.0:
modelo = Holt(train, initialization_method="estimated").fit()
etiqueta = "sin amortiguar"
else:
modelo = Holt(train, damped_trend=True, initialization_method="estimated").fit(
damping_trend=phi
)
etiqueta = f"phi = {phi}"
pronostico = modelo.forecast(24)
print(f"{etiqueta:16s} -> t+1: {pronostico.iloc[0]:6.1f} "
f"t+12: {pronostico.iloc[11]:6.1f} t+24: {pronostico.iloc[-1]:6.1f}")
sin amortiguar -> t+1: 147.8 t+12: 184.4 t+24: 224.4
phi = 0.9 -> t+1: 140.9 t+12: 149.1 t+24: 151.8
phi = 0.8 -> t+1: 139.7 t+12: 143.4 t+24: 143.7
phi = 0.6 -> t+1: 139.5 t+12: 140.8 t+24: 140.8
La diferencia es contundente. Sin amortiguar, el pronóstico sigue subiendo durante los 24 pasos (147.8 → 224.4). Con φ = 0.8, el pronóstico prácticamente se estabiliza a partir del mes 12 (143.4 → 143.7): la tendencia “se queda sin fuerza” de forma controlada, en lugar de crecer para siempre.
Cuidado con sobre-amortiguar: un φ demasiado bajo (0.6 en el ejemplo) aplana el pronóstico casi de inmediato, prácticamente igual que si no hubiera tendencia. Igual que con
den ARIMA, no hay un valor universal — se elige comparando AIC o validando contra un conjunto de test, nunca a ojo.
# Dejar que statsmodels optimice phi en vez de fijarlo a mano
modelo_amortiguado = Holt(train, damped_trend=True, initialization_method="estimated").fit()
print(f"phi optimizado: {modelo_amortiguado.params['damping_trend']:.3f}")
Cuando el φ que el propio optimizador encuentra sale muy cercano a 1, es una señal honesta: la tendencia observada ya es razonablemente lineal en el horizonte que te interesa, y amortiguar aporta poco. No fuerces la amortiguación si los datos no la piden.
Ejercicio de práctica
Ajusta Holt con y sin amortiguar sobre esta serie, que se desacelera de forma visible hacia el final, y compara el pronóstico a 12 pasos contra los últimos valores reales.
import numpy as np
import pandas as pd
np.random.seed(55)
n = 40
t = np.arange(n)
# Crecimiento que se desacelera (raíz cuadrada, no lineal)
nivel_desacelerado = 20 * np.sqrt(t + 1)
serie_desacelerada = pd.Series(nivel_desacelerado + np.random.normal(0, 3, n))
train_d = serie_desacelerada.iloc[:-12]
test_d = serie_desacelerada.iloc[-12:]
# Paso 1: ajusta Holt sin amortiguar y con amortiguar (damped_trend=True)
# Paso 2: pronostica 12 pasos con cada uno
# Paso 3: calcula el MAE de ambos contra test_d y compara
Ver solución
from statsmodels.tsa.holtwinters import Holt
from sklearn.metrics import mean_absolute_error
modelo_sin = Holt(train_d, initialization_method="estimated").fit()
modelo_con = Holt(train_d, damped_trend=True, initialization_method="estimated").fit()
pred_sin = modelo_sin.forecast(12)
pred_con = modelo_con.forecast(12)
print(f"MAE sin amortiguar: {mean_absolute_error(test_d, pred_sin):.2f}")
print(f"MAE amortiguado : {mean_absolute_error(test_d, pred_con):.2f}")
Como la serie real se desacelera (crece como raíz cuadrada, no en línea recta), la versión amortiguada debería acercarse más a los valores reales del test que la versión sin amortiguar, que sigue extrapolando con la pendiente más pronunciada de los últimos períodos observados.
Resumen
- Holt añade una ecuación de tendencia (β) a SES, permitiendo capturar crecimiento o caída sostenidos.
- El pronóstico de Holt es una extrapolación en línea recta: , que se aleja sin límite cuanto mayor es el horizonte.
- La tendencia amortiguada introduce φ para frenar esa extrapolación, haciendo que el pronóstico se estabilice en vez de crecer indefinidamente.
- Un φ optimizado cercano a 1 es información honesta: la tendencia observada ya era razonablemente lineal, y amortiguar aporta poco valor en ese caso.
El siguiente subcapítulo cierra el capítulo con el caso completo: Holt-Winters con estacionalidad, selección de modelo por AIC, diagnóstico de residuos y pronóstico con intervalos.