Aller au contenu principal

Module 3 — Stationnarité, différenciation, autocorrélation

Le module 2 a séparé les composantes ; celui-ci prépare la modélisation statistique. Une famille entière de modèles — ARIMA et ses dérivés du module 5 — repose sur une hypothèse précise, la stationnarité. Comprendre ce qu'elle exige, comment la tester, comment y arriver par différenciation, puis comment lire les autocorrélogrammes ACF et PACF pour choisir des ordres : c'est le bagage technique de ce module.

Ce que veut dire « stationnaire »

Une série est strictement stationnaire si toutes ses distributions jointes sont invariantes par translation dans le temps. C'est une propriété trop forte pour être exploitable en pratique. On lui préfère la stationnarité au second ordre, qui exige seulement trois choses : une moyenne constante, une variance constante, et une autocovariance qui ne dépend que de l'écart hh entre deux instants, non de leur position absolue.

Concrètement, si vous découpez votre série en morceaux d'un an et qu'ils se ressemblent en niveau, en dispersion et dans leurs corrélations décalées, elle est probablement stationnaire. Sur notre fil rouge, ce n'est manifestement pas le cas : la tendance croissante fait dériver la moyenne, la saisonnalité annuelle fait varier la moyenne au sein d'une année, et la variance change entre l'hiver (fort) et l'été (faible). Il faudra donc transformer la série avant tout ajustement d'ARIMA.

Le test ADF, avec ses limites

Le test de Dickey-Fuller augmenté teste l'hypothèse nulle « la série a une racine unitaire », c'est-à-dire « la série n'est pas stationnaire ». Un pp-value inférieur à 0,05 permet de rejeter cette hypothèse et donc de conclure à la stationnarité.

from statsmodels.tsa.stattools import adfuller

resultat = adfuller(ventes["quantite"].dropna(), autolag="AIC")
statistique, p_value, retards, n = resultat[0], resultat[1], resultat[2], resultat[3]

print(f"statistique ADF : {statistique:.3f}")
print(f"p-value : {p_value:.4f}")
print(f"retards choisis : {retards}")
print(f"n effectif : {n}")

Deux pièges à connaître. D'abord, l'ADF a peu de puissance sur des séries courtes : il conclura souvent à la non-stationnarité par manque de preuves, ce qui n'est pas la même chose qu'une preuve de non-stationnarité. Ensuite, il ne teste que la tendance — une saisonnarité forte peut faire passer une série pour non-stationnaire alors qu'elle l'est en réalité une fois dessaisonnalisée. Un test complémentaire utile est le KPSS, dont l'hypothèse nulle est la stationnarité : quand ADF et KPSS s'accordent, la conclusion est solide ; quand ils divergent, il faut regarder plus finement.

Rendre la série stationnaire par différenciation

Deux opérations résolvent la plupart des cas. La différenciation simple :

Δyt=ytyt1\Delta y_t = y_t - y_{t-1}

retire la tendance : la vente d'un jour se convertit en variation par rapport à la veille. La différenciation saisonnière de période ss :

Δsyt=ytyts\Delta_s y_t = y_t - y_{t-s}

retire la saisonnalité en comparant chaque instant au même moment du cycle précédent. Sur notre fil rouge, Δ7yt\Delta_7 y_t compare chaque jour au même jour de la semaine précédente, éliminant la structure hebdomadaire. On peut combiner les deux : ΔΔ7yt\Delta \Delta_7 y_t retire tendance et saisonnalité hebdomadaire.

Le nombre de différenciations à appliquer est habituellement noté dd pour la partie simple et DD pour la partie saisonnière. Le geste sain consiste à différencier le moins possible : chaque différenciation supplémentaire ampute la série d'observations et ajoute du bruit. En pratique, d{0,1}d \in \{0, 1\} et D{0,1}D \in \{0, 1\} suffisent presque toujours ; une deuxième différenciation (d=2d = 2) est un signal à ce qu'on ait mal spécifié la tendance.

serie = np.log(ventes["quantite"])
diff_1 = serie.diff().dropna() # d = 1
diff_1_7 = serie.diff().diff(7).dropna() # d = 1, D = 1

for nom, s in [("originale", serie), ("Δ", diff_1), ("Δ puis Δ_7", diff_1_7)]:
p = adfuller(s.dropna(), autolag="AIC")[1]
print(f"{nom:>16s} : p-value ADF = {p:.4f}")

Sur notre fil rouge, la série d'origine donne typiquement un pp-value au-delà de 0,3, la première différence tombe sous 0,05, et l'ajout de Δ7\Delta_7 améliore encore la stationnarité tout en supprimant l'onde hebdomadaire.

Deux fuites du futur classiques à ce stade

Ne calculez jamais la moyenne ou la variance sur l'ensemble complet pour normaliser avant de découper : c'est de la fuite. De même, ne choisissez pas dd et DD en regardant la série entière : décomposez sur le train uniquement, puis appliquez les mêmes opérations au test. C'est le prolongement de la règle du module 1.

ACF et PACF : deux vues complémentaires

Une fois la série rendue stationnaire, deux graphiques renseignent sur la structure d'autocorrélation restante. La fonction d'autocorrélation (ACF) au décalage hh est la corrélation entre yty_t et ythy_{t-h}. Elle capture la corrélation totale, y compris ce qui transite par les intermédiaires. La fonction d'autocorrélation partielle (PACF), elle, retire l'effet des retards intermédiaires : la PACF au décalage hh mesure la corrélation résiduelle entre yty_t et ythy_{t-h} une fois neutralisées yt1,,yth+1y_{t-1}, \dots, y_{t-h+1}.

from statsmodels.graphics.tsaplots import plot_acf, plot_pacf

figure, (a1, a2) = plt.subplots(2, 1, figsize=(10, 6))
plot_acf(diff_1_7, lags=40, ax=a1)
plot_pacf(diff_1_7, lags=40, ax=a2, method="ywm")

Ces deux graphes forment une petite grammaire de lecture. Un AR(pp) pur a une PACF qui coupe brusquement après le décalage pp et une ACF qui décroît lentement. Un MA(qq) pur a l'inverse : ACF qui coupe après le décalage qq, PACF qui décroît. Un ARMA mêle les deux : les deux graphes décroissent, plus difficiles à lire, et c'est pour cela que la sélection par AIC (module 5) prend le relais dès qu'on quitte les cas simples.

Le prolongement saisonnier

Quand la série contient une saisonnalité de période ss, l'ACF présente des pics aux multiples de ss : sur nos ventes quotidiennes, on voit des pics à 7, 14, 21, 28. Ces pics guident les ordres saisonniers PP et QQ du module suivant, exactement comme les pics « courts » guident pp et qq. La règle mnémotechnique : « les pics saisonniers résiduels après Δs\Delta_s appellent des termes saisonniers dans SARIMA ».

Un piège fréquent de la PACF

La PACF calculée par la méthode de Yule-Walker biaisée peut osciller de façon trompeuse sur des séries courtes ; c'est pourquoi le code ci-dessus utilise method="ywm" (Yule-Walker modifié, non biaisé). Sur les séries longues, l'écart est invisible, mais la bonne habitude est de fixer la méthode explicitement.

Un autre piège concerne les bandes de confiance : par défaut, statsmodels dessine des bandes calculées sous l'hypothèse de bruit blanc. Un pic qui dépasse ces bandes n'est donc pas la preuve d'une structure, seulement une piste. C'est particulièrement vrai autour des retards saisonniers, où les corrélations induites par le calendrier peuvent tromper.

Ce que je fais toujours avant SARIMA

Cinq gestes, dans l'ordre : (1) log si la variance croît avec le niveau ; (2) test ADF ; (3) différencier simplement si nécessaire (d=1d = 1) ; (4) différencier saisonnièrement si l'ACF montre une saisonnalité résiduelle (D=1D = 1) ; (5) tracer ACF et PACF sur la série finalement stationnaire. Cette liste économise plusieurs heures d'ajustement à l'aveugle et donne des ordres (p,d,q)(P,D,Q)s(p, d, q)(P, D, Q)_s directement plausibles au module suivant.

En résumé

  • Stationnarité au second ordre : moyenne, variance et autocovariance qui ne dépendent que de l'écart.
  • ADF teste la racine unitaire, KPSS la stationnarité ; leur accord donne une conclusion solide.
  • Différenciation simple (dd) contre la tendance, saisonnière (DD) contre la saisonnalité ; différencier le moins possible.
  • ACF et PACF guident pp, qq (courts) et PP, QQ (saisonniers) — les motifs de coupure et de décroissance renseignent sur AR, MA ou ARMA.

Module suivant : les modèles de référence à ne jamais négliger, première ligne d'un tableau comparatif qu'on remplira jusqu'à la fin du cours.