ARIMA: el primer model que de debò prediu el futur
En el capítol anterior vas aprendre a veure una sèrie temporal com la suma de tendència, estacionalitat i soroll. Ara ve la pregunta incòmoda: ¿com uses això per predir?
La resposta curta és: primer converteixes la sèrie en alguna cosa més previsible, després tries un model que aprofiti la dependència entre valors passats i, finalment, projectes cap endavant. El model més clàssic per fer-ho és ARIMA. No és el més modern, però entendre’l bé és com aprendre a conduir amb canvi manual abans de pujar-te a un cotxe automàtic: tota la resta s’entén molt millor.
El supòsit que ho canvia tot: estacionarietat
Una sèrie és estacionària si les seves propietats estadístiques —mitjana, variància i autocorrelació— no canvien amb el temps. Visualment, és una sèrie que sembla “moure’s al voltant d’una mitjana constant” sense una pendent clara ni canvis d’amplitud.
¿Per què importa? Perquè la majoria de models clàssics assumeixen que el futur s’assemblarà estadísticament al passat. Si la mitjana puja sense parar, aquest supòsit es trenca.
Hi ha un test molt usat per comprovar-ho: el test de Dickey-Fuller augmentat (ADF). La hipòtesi nul·la és que la sèrie no és estacionària.
from statsmodels.tsa.stattools import adfuller
# La hipòtesi nul·la: la sèrie no és estacionària
resultat = adfuller(serie)
print(f"Estadístic ADF: {resultat[0]:.4f}")
print(f"p-valor: {resultat[1]:.4f}")
if resultat[1] < 0.05:
print("La sèrie és estacionària (rebutgem H0)")
else:
print("La sèrie NO és estacionària (no rebutgem H0)")
Si el teu p-valor és menor que 0.05, celebra: la sèrie és estacionària. Si no, toca transformar-la.
Diferenciació: traient la tendència
La tècnica més senzilla per fer estacionària una sèrie és la diferenciació. Consisteix a restar cada valor a l’anterior:
Si una diferència no n’hi ha prou, pots aplicar una segona diferència. En la pràctica, rarament necessites més de dues.
# Primera diferenciació
serie_diff = serie.diff().dropna()
# Comprovem de nou
resultat_diff = adfuller(serie_diff)
print(f"p-valor després de diferenciar: {resultat_diff[1]:.4f}")
Després de diferenciar, la sèrie hauria de moure’s al voltant de zero. Si encara té estacionalitat forta, també pots diferenciar estacionalment, restant el valor del mateix mes de l’any anterior.
# Diferenciació estacional: restem el valor del mateix mes de l'any anterior
serie_diff_seasonal = serie.diff(12).dropna()
# Combinem diferenciació regular i estacional
serie_diff_double = serie.diff().diff(12).dropna()
resultat_double = adfuller(serie_diff_double)
print(f"p-valor després de diferenciar regular + estacional: {resultat_double[1]:.4f}")
Quan parar: si després d’una o dues diferenciacions el p-valor de l’ADF és menor que 0.05, ja tens una sèrie estacionària. No segueixis diferenciant per inèrcia: cada diferenciació perd una observació i pot introduir soroll.
ACF i PACF: els ulls del modelador
Un cop estacionària, necessites decidir els paràmetres del model. Per això uses dues gràfiques:
- ACF (Autocorrelation Function): correlació de la sèrie amb els seus propis lags.
- PACF (Partial Autocorrelation Function): correlació amb un lag eliminant l’efecte dels lags intermedis.

En la imatge pots veure com l’ACF decreix lentament: és el senyal típic d’una sèrie no estacionària o amb tendència. La PACF t’ajuda a identificar l’ordre autorregressiu.
La regla mnemotècnica clàssica és:
| Gràfica | Patró | Indica |
|---|---|---|
| ACF | decreix gradualment | Component de mitjana mòbil (q) |
| PACF | talla bruscament després del lag p | Component autorregressiu (p) |
| ACF | pics en lags estacionals | Necessitat de diferenciació estacional |
No prenguis aquests talls com a dogma. Són una guia per començar a explorar, no un procediment automàtic. Sempre valida amb mètriques com AIC o BIC.
Com funciona ARIMA
ARIMA són les sigles d’AutoRegressive Integrated Moving Average. El model té tres paràmetres:
- p: ordre autorregressiu. Quants valors passats influeixen directament en l’actual.
- d: nombre de diferenciacions necessàries per fer la sèrie estacionària.
- q: ordre de mitjana mòbil. Quants errors passats uses per corregir la predicció.
Un ARIMA(1,1,1) diu: “Diferencio una vegada, i després modelitzo la sèrie diferenciada utilitzant un valor passat i un error passat.”
En codi, ajustar-lo és directe:
from statsmodels.tsa.arima.model import ARIMA
# Dividim en entrenament i test (últims 12 mesos)
train = serie.iloc[:-12]
test = serie.iloc[-12:]
# Ajustem ARIMA(2, 1, 2)
model = ARIMA(train, order=(2, 1, 2))
resultat = model.fit()
print(resultat.summary())
El resum de statsmodels mostra molts números, però els més importants són els coeficients ar.L1, ar.L2, ma.L1, ma.L2 i els seus p-valors. Si un coeficient no és significatiu, potser el teu model té més paràmetres dels necessaris.
Generar pronòstics amb intervals de confiança
Un cop ajustat el model, pots projectar cap endavant. El més útil de statsmodels és que no només et dóna una línia de predicció, sinó també un interval de confiança. Això és or: et diu on esperes que estigui el valor real, no només un punt.
# Pronòstic per als 12 mesos de test
forecast = resultat.get_forecast(steps=12)
prediccio = forecast.predicted_mean
interval = forecast.conf_int(alpha=0.05)
# Grafiquem
plt.figure(figsize=(12, 6))
plt.plot(train.index, train, label="Entrenament", color="#1a1a1a")
plt.plot(test.index, test, label="Test real", color="#a8e6cf")
plt.plot(test.index, prediccio, label="Predicció ARIMA", color="#ffcc00")
plt.fill_between(
test.index,
interval.iloc[:, 0],
interval.iloc[:, 1],
color="#ffcc00",
alpha=0.3,
label="Interval 95%",
)
plt.legend()
plt.title("Predicció amb ARIMA")
plt.show()

En la gràfica s’aprecia com la predicció segueix la dinàmica general de la sèrie. No és perfecta —mai ho serà—, però captura tant la tendència com gran part de l’oscil·lació.
Mètriques per avaluar el pronòstic
La gràfica és útil, però necessites números per comparar models. Les mètriques més comunes són el MAE (error absolut mitjà) i el RMSE (arrel de l’error quadràtic mitjà).
from sklearn.metrics import mean_absolute_error, mean_squared_error
mae = mean_absolute_error(test, prediccio)
rmse = mean_squared_error(test, prediccio, squared=False)
print(f"MAE: {mae:.2f}")
print(f"RMSE: {rmse:.2f}")
| Mètrica | Què mesura | Quan usar-la |
|---|---|---|
| MAE | Error absolut mitjà en les mateixes unitats que la sèrie | Quan tots els errors tenen la mateixa importància |
| RMSE | Penalitza més els errors grans | Quan els errors grans són especialment costosos |
També és útil comparar contra un baseline ingenu: predir que el proper valor és igual a l’últim valor observat. Si el teu ARIMA no millora això, alguna cosa falla.
# Baseline ingenu: l'últim valor de train es repeteix per a tot el test
baseline = pd.Series(train.iloc[-1], index=test.index)
mae_baseline = mean_absolute_error(test, baseline)
print(f"MAE baseline: {mae_baseline:.2f}")
print(f"MAE ARIMA: {mae:.2f}")
Com triar els millors paràmetres
Ajustar a mà p, d i q és didàctic, però en projectes reals convé automatitzar la cerca. Pots entrenar diversos models i quedar-te amb el que minimitzi l’AIC (Akaike Information Criterion) o el BIC.
import itertools
millor_aic = float("inf")
millor_ordre = None
for p, d, q in itertools.product(range(0, 4), range(0, 2), range(0, 4)):
try:
model = ARIMA(train, order=(p, d, q))
ajustat = model.fit()
if ajustat.aic < millor_aic:
millor_aic = ajustat.aic
millor_ordre = (p, d, q)
except Exception:
continue
print(f"Millor ordre: {millor_ordre} amb AIC: {millor_aic:.2f}")
Compte amb l’overfitting. Un model amb molts paràmetres pot ajustar molt bé el passat i fallar estrepitosament en el futur. L’AIC penalitza la complexitat, però segueix sent recomanable validar amb un test set o amb validació creuada temporal.
Diagnòstic de residus
Un bon model ARIMA ha de deixar residus que semblin soroll blanc: mitjana zero, variància constant i sense autocorrelació. Si els residus tenen patró, el model encara no ha capturat tota l’estructura de la sèrie.
# Residus del model
residus = resultat.resid
fig, axes = plt.subplots(2, 1, figsize=(10, 6), sharex=True)
# Sèrie de residus
axes[0].plot(residus, color="#1a1a1a", linewidth=1)
axes[0].axhline(0, color="#ffcc00", linestyle="--", linewidth=2)
axes[0].set_title("Residus del model ARIMA")
axes[0].grid(True, alpha=0.3)
# Histograma de residus
axes[1].hist(residus, bins=20, color="#ffcc00", edgecolor="#1a1a1a")
axes[1].set_title("Distribució dels residus")
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
Visualment busques dues coses: que els residus oscil·lin al voltant de zero sense patrons, i que el seu histograma s’assembli a una campana. Per confirmar-ho formalment pots aplicar el test de Ljung-Box:
from statsmodels.stats.diagnostic import acorr_ljungbox
ljung = acorr_ljungbox(residus, lags=10, return_df=True)
print(ljung.head())
Si els p-valors són alts (majors que 0.05), no hi ha evidència d’autocorrelació residual i el model és raonablement bo.
Un pas més enllà: SARIMA
Si la teva sèrie té estacionalitat forta, ARIMA normal no és suficient. Necessites SARIMA (Seasonal ARIMA), que afegeix tres paràmetres estacionals: P, D, Q i el període s.
from statsmodels.tsa.statespace.sarimax import SARIMAX
# SARIMA(1,1,1)(1,1,1,12) per a sèries mensuals amb estacionalitat anual
model_sarima = SARIMAX(train, order=(1, 1, 1), seasonal_order=(1, 1, 1, 12))
resultat_sarima = model_sarima.fit(disp=False)
prediccio_sarima = resultat_sarima.get_forecast(steps=12).predicted_mean
mae_sarima = mean_absolute_error(test, prediccio_sarima)
print(f"MAE ARIMA: {mae:.2f}")
print(f"MAE SARIMA: {mae_sarima:.2f}")
En sèries amb estacionalitat clara, SARIMA sol reduir l’error de forma notable. És l’extensió natural d’ARIMA quan els patrons es repeteixen cada cert temps.
Limitacions d’ARIMA
ARIMA és potent, però no és una vareta màgica:
- No captura bé estacionalitats complexes sense l’extensió SARIMA.
- No maneja múltiples sèries relacionades —per això hi ha els models VAR.
- És sensible a outliers i canvis estructurals.
- No inclou variables externes —si vols afegir promocions o festius, necessites ARIMAX o models de ML.
Tot i així, segueix sent un excel·lent punt de partida. Moltes empreses usen ARIMA com a benchmark abans de provar models més sofisticats.
Resum i següent pas
En aquest capítol has après a comprovar l’estacionarietat amb el test ADF, a diferenciar sèries temporals, a interpretar ACF i PACF, i a ajustar un model ARIMA amb statsmodels per generar pronòstics amb intervals de confiança.
ARIMA és un model clàssic, però el món del forecasting ha evolucionat molt. En el proper capítol veurem com usar machine learning —concretament models basats en arbres— per predir sèries temporals utilitzant features de retards, finestres mòbils i variables externes.