LangueFR
Français
English
Retour au sommaire du cours

Séries temporelles, signaux et durées de vie

Dès que le temps entre en jeu, l'hypothèse d'indépendance des observations — sur laquelle repose tout le reste du cours — tombe. Validation croisée, tests statistiques, feature engineering : tout doit être repensé. Ce module couvre les trois familles de problèmes temporels que l'on rencontre réellement : prévoir une trajectoire, analyser un signal, et estimer une durée avant événement.

1. Ce que le temps change

quatre conséquences directes
Autocorrélation   les observations voisines se ressemblent : la taille d'échantillon
                  EFFECTIVE est bien inférieure à n. Un test qui l'ignore déclare
                  significatif ce qui ne l'est pas (module 9).
Non-stationnarité moyenne, variance et saisonnalité évoluent. Le passé lointain
                  peut être trompeur plutôt qu'informatif.
Ordre imposé      entraîner sur le futur pour prédire le passé n'a aucun sens :
                  la validation devient séquentielle (module 12).
Horizon           prévoir à J+1 et à J+30 sont deux problèmes différents, avec des
                  variables et des performances différentes.

L'autocorrélation, concrètement

Si la température d'aujourd'hui ressemble fortement à celle d'hier, 365 mesures quotidiennes ne contiennent pas 365 informations indépendantes — peut-être l'équivalent de trente. Toutes les formules du module 9 en \(\sqrt{n}\) deviennent alors optimistes : un intervalle de confiance calculé naïvement sur des données autocorrélées est beaucoup trop étroit, et l'on conclut à des effets qui n'existent pas. C'est la raison pour laquelle on ne peut pas appliquer directement les outils des modules précédents à une série temporelle.

La première question à poser. S'agit-il vraiment d'une prévision ? Beaucoup de problèmes présentés comme temporels sont en réalité des régressions ordinaires où la date sert de variable explicative parmi d'autres. On ne fait de la prévision que lorsqu'il faut extrapoler au-delà de la dernière observation, sans connaître les variables explicatives futures. Si les régresseurs futurs sont connus (un calendrier, une météo prévue, un plan de production), le problème est plus simple et les outils du module 14 suffisent.

2. Décomposition et stationnarité

Une série se lit d'abord comme la somme (ou le produit) de trois composantes : tendance (l'évolution de fond), saisonnalité (le motif qui se répète à période fixe) et résidu (ce qui reste). La décomposition STL, par lissage local, est l'outil de référence : robuste aux valeurs aberrantes, elle accepte une saisonnalité qui évolue lentement.

Python
import polars as pl, pandas as pd
from statsmodels.tsa.seasonal import STL
from statsmodels.tsa.stattools import adfuller, kpss

serie = pd.read_csv("conso.csv", index_col="date", parse_dates=True)["valeur"].asfreq("D")

res = STL(serie, period=7, robust=True).fit()
res.plot()                                   # tendance, saison, résidu

print("ADF  p =", adfuller(serie)[1])        # H0 = racine unitaire (NON stationnaire)
print("KPSS p =", kpss(serie, regression="c")[1])   # H0 = stationnarité

Une série est stationnaire si ses propriétés statistiques ne dépendent pas de la date : même moyenne, même variance, même structure de dépendance à tout moment. Les modèles classiques l'exigent, parce qu'ils estiment des coefficients supposés constants. On s'y ramène par différenciation (\(\nabla y_t = y_t - y_{t-1}\), qui élimine une tendance linéaire), différenciation saisonnière (\(y_t - y_{t-s}\)) ou transformation stabilisante (logarithme si la variance croît avec le niveau).

Utiliser ADF et KPSS ensemble est plus sûr : leurs hypothèses nulles sont inversées, donc un désaccord signale une situation ambiguë qu'il vaut mieux examiner graphiquement plutôt que trancher mécaniquement.

3. Lire une ACF et une PACF

L'autocorrélation (ACF) au décalage \(k\) mesure la corrélation entre la série et elle-même décalée de \(k\) pas. La PACF mesure la même chose en retirant l'effet des décalages intermédiaires — la corrélation directe entre \(y_t\) et \(y_{t-k}\), une fois les \(k-1\) précédents pris en compte.

ce que chaque forme signale
ACF qui décroît TRÈS LENTEMENT        → série non stationnaire : différencier
ACF avec un pic à 7, 14, 21…          → saisonnalité hebdomadaire
ACF qui se coupe net après q          → processus MA(q)
PACF qui se coupe net après p         → processus AR(p)
ACF et PACF décroissant toutes deux   → ARMA mixte : laisser l'auto-sélection choisir

  ACF │█                               PACF │█
      │█ █                                  │█
      │█ █ █ ▄                              │█ ▄
      │█ █ █ █ ▄ ▂                          │─ ─ ─ ─ ─ ─  ← coupure après 2
      └──────────────► décalage             └──────────────► décalage
      décroissance lente = AR               → AR(2)
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
plot_acf(serie.diff().dropna(), lags=40)     # sur la série DIFFÉRENCIÉE
plot_pacf(serie.diff().dropna(), lags=40)

4. Modèles classiques : ETS et ARIMA

Lissage exponentiel (ETS) — l'idée tient en une ligne : une moyenne pondérée du passé dont les poids décroissent géométriquement, de sorte que le passé récent compte davantage. Holt-Winters ajoute une tendance et une saisonnalité, chacune avec son propre coefficient de lissage :

\[ \ell_t = \alpha\,(y_t - s_{t-m}) + (1-\alpha)(\ell_{t-1}+b_{t-1}), \qquad \hat y_{t+h} = \ell_t + h\,b_t + s_{t+h-m} \]

où \(\ell\) est le niveau, \(b\) la pente et \(s\) la composante saisonnière. Simple, rapide, et étonnamment difficile à battre sur des séries métier courtes et régulières.

ARIMA(p,d,q) — \(d\) différenciations pour se ramener à la stationnarité, \(p\) termes autorégressifs (le passé de la série) et \(q\) termes de moyenne mobile (le passé des erreurs) :

\[ \nabla^d y_t = c + \sum_{i=1}^{p}\phi_i \nabla^d y_{t-i} + \sum_{j=1}^{q}\theta_j \varepsilon_{t-j} + \varepsilon_t \]

La partie AR dit « la valeur d'aujourd'hui ressemble à celles d'hier » ; la partie MA dit « l'erreur d'aujourd'hui ressemble à celles d'hier », ce qui capte les chocs dont l'effet se dissipe. SARIMA ajoute les mêmes termes à la fréquence saisonnière, SARIMAX ajoute des régresseurs externes.

Python — sélection automatique et prévision
from statsforecast import StatsForecast
from statsforecast.models import AutoARIMA, AutoETS, SeasonalNaive

sf = StatsForecast(
    models=[AutoARIMA(season_length=7), AutoETS(season_length=7),
            SeasonalNaive(season_length=7)],      # LA baseline à battre
    freq="D", n_jobs=-1,
)
sf.fit(df)                                        # df : unique_id, ds, y
prev = sf.forecast(h=28, level=[80, 95])          # avec intervalles de prédiction
print(prev.head())

Quand les modèles classiques battent le machine learning

Systématiquement quand : la série est courte (moins de quelques centaines de points), il y en a peu (une ou dix, pas dix mille), la structure est régulière, et il faut des intervalles de prévision fiables. Les compétitions M4 et M5 l'ont confirmé : les méthodes statistiques simples, et surtout les combinaisons de méthodes simples, restent redoutables. Le ML prend l'avantage quand on doit prévoir des milliers de séries apparentées simultanément, avec beaucoup de régresseurs externes — une chaîne de magasins, un parc de capteurs — parce qu'il apprend alors des motifs communs à toutes les séries.

5. Approche ML : variables et gradient boosting

On transforme la prévision en régression tabulaire en construisant des variables exclusivement passées, puis on applique le module 14.

Python — polars, fenêtres strictement passées
import numpy as np, polars as pl

df = (
    df.sort("date")
      .with_columns(
          # décalages : la valeur d'il y a 1, 7 et 364 jours (même jour de semaine)
          *[pl.col("y").shift(k).over("serie_id").alias(f"lag_{k}") for k in (1, 7, 364)],

          # moyennes glissantes sur le PASSÉ uniquement — le shift(1) est ESSENTIEL
          pl.col("y").shift(1).rolling_mean(7).over("serie_id").alias("ma_7"),
          pl.col("y").shift(1).rolling_mean(28).over("serie_id").alias("ma_28"),
          pl.col("y").shift(1).rolling_std(28).over("serie_id").alias("sd_28"),
          # tendance courte : rapport des deux moyennes
          (pl.col("y").shift(1).rolling_mean(7) /
           pl.col("y").shift(1).rolling_mean(28)).over("serie_id").alias("ratio_7_28"),

          # calendrier et saisonnalité cyclique (module 12)
          pl.col("date").dt.weekday().alias("jour_semaine"),
          (2 * np.pi * pl.col("date").dt.ordinal_day() / 365.25).sin().alias("an_sin"),
          (2 * np.pi * pl.col("date").dt.ordinal_day() / 365.25).cos().alias("an_cos"),
      )
)

Deux décisions structurent ensuite le travail. Stratégie d'horizon : la prévision récursive réinjecte ses propres prédictions pour avancer pas à pas (simple, mais les erreurs s'accumulent) ; la prévision directe entraîne un modèle par horizon (plus coûteux, plus stable au-delà de quelques pas). Validation : uniquement en avançant dans le temps, avec une fenêtre glissante ou extensible, et en respectant un écart égal à l'horizon entre entraînement et test — sans quoi on évalue une situation impossible en production.

Le piège qui invalide silencieusement un projet de prévision. Une moyenne glissante calculée sans shift(1) inclut la valeur courante : le modèle « prédit » en lisant sa propre cible, et le score est spectaculaire jusqu'à la mise en production. Le second piège est la disponibilité : une variable peut être passée mais ne pas être encore connue au moment où la prévision doit être produite — un chiffre consolidé publié avec trois jours de retard. Toujours raisonner en « que sais-je à l'instant où je prédis ? », pas en « quelle est la date de cette ligne ? ».

6. Deep learning et modèles de fondation

  • N-BEATS / N-HiTS — empilements de blocs résiduels, sans composante récurrente ; performances solides et entraînement rapide.
  • DeepAR — modèle autorégressif probabiliste entraîné sur un ensemble de séries apparentées : il apprend des motifs partagés, ce qui aide beaucoup les séries courtes.
  • Temporal Fusion Transformer — attention sur les séquences (module 16) avec gestion propre des covariables connues à l'avance (calendrier, promotions) et une part d'interprétabilité.
  • Modèles de fondation (Chronos, TimesFM, Moirai, TimeGPT) — pré-entraînés sur d'énormes corpus de séries, utilisables en zero-shot. Intéressants pour obtenir une baseline sans entraînement ; pas systématiquement meilleurs qu'un bon modèle spécifique, et à manier avec précaution quand la série contient de l'information confidentielle (module 27).

La bibliothèque nixtla (statsforecast / mlforecast / neuralforecast) donne accès aux trois familles avec la même interface, ce qui permet de les comparer honnêtement — la seule façon de trancher.

7. Prévision probabiliste et évaluation

Une prévision ponctuelle est rarement suffisante : dimensionner un stock ou une réserve d'énergie demande un quantile, pas une moyenne (module 24). On entraîne donc directement sur la perte pinball, qui pénalise asymétriquement selon le quantile visé :

\[ L_\tau(y,\hat y) = \max\big(\tau(y-\hat y),\; (\tau-1)(y-\hat y)\big) \]

Pour \(\tau = 0{,}9\), sous-estimer coûte neuf fois plus cher que surestimer : le modèle apprend donc à viser haut, et sa prédiction converge vers le quantile 90 %.

métriques de prévision
MAE / RMSE   interprétables, mais non comparables entre séries d'échelles différentes
MAPE         en pourcentage ; explose si y proche de 0 et pénalise asymétriquement
             (surestimer de 100 % est borné, sous-estimer ne l'est pas)
sMAPE        version symétrique, un peu moins instable
MASE         erreur rapportée à celle de la prévision NAÏVE : < 1 = mieux que naïf.
             LA métrique à préférer pour comparer des séries hétérogènes.
Pinball      évalue un quantile donné
CRPS         évalue toute la distribution prédictive ; se réduit à la MAE pour une
             prévision ponctuelle. La référence en prévision probabiliste.

Et la règle d'or : toujours comparer à la prévision naïve — la dernière valeur, ou la valeur de la même période l'an dernier. Un modèle qui ne bat pas la naïve saisonnière ne sert à rien, et c'est plus fréquent qu'on ne le croit.

8. Espace d'états et filtre de Kalman

Un modèle espace-état sépare ce qu'on cherche à connaître — un état caché \(x_t\) qui évolue selon une dynamique — de ce qu'on observe — \(z_t\), bruité et parfois indirect :

\[ x_t = F x_{t-1} + B u_t + w_t, \qquad z_t = H x_t + v_t, \qquad w_t\sim\mathcal{N}(0,Q),\; v_t\sim\mathcal{N}(0,R) \]

Le filtre de Kalman alterne deux étapes à chaque instant :

prédiction puis correction
PRÉDICTION   x̂ = F·x̂      "d'après mon modèle de dynamique, voilà où je devrais être"
             P  = F·P·Fᵀ + Q   l'incertitude AUGMENTE (le modèle n'est pas parfait)

CORRECTION   K  = P·Hᵀ(H·P·Hᵀ + R)⁻¹     le GAIN : combien croire la mesure ?
             x̂ = x̂ + K(z - H·x̂)          on corrige avec l'écart à la mesure
             P  = (I - K·H)·P             l'incertitude DIMINUE

Si R est grand (capteur bruité) → K petit → on fait confiance au modèle
Si Q est grand (modèle grossier) → K grand → on fait confiance à la mesure

C'est l'estimateur optimal au sens de la variance minimale dans le cas linéaire-gaussien, et il est récursif : coût constant par pas, mémoire constante, quelle que soit la longueur de l'historique. D'où son omniprésence en navigation, en poursuite de cible et en fusion de capteurs. Les extensions traitent le cas non linéaire : EKF (linéarisation locale), UKF (propagation de points sigma, plus stable), et filtres particulaires (Monte-Carlo séquentiel, sans hypothèse gaussienne).

Pour un ingénieur, l'intérêt est double : lisser un signal bruité en temps réel, et estimer des grandeurs non mesurables directement à partir de mesures indirectes — ce qui relie ce module à l'assimilation de données du module 21.

9. Ruptures et anomalies

  • Détection de rupture — identifier les dates où le régime change (changement de moyenne, de variance, de tendance). La bibliothèque ruptures implémente PELT et la segmentation binaire ; CUSUM reste la méthode de référence pour une surveillance en ligne. Usage concret : savoir à partir de quand les données sont encore représentatives, donc à partir de quand réentraîner (module 26).
  • Anomalies ponctuelles — un point s'écarte fortement de la prévision : on compare le résidu à un intervalle de prédiction plutôt qu'à un seuil fixe, ce qui rend la détection insensible à la saisonnalité (un pic le samedi n'est pas une anomalie si tous les samedis font un pic).
  • Anomalies collectives — un motif anormal sur une fenêtre, détectable par distance entre sous-séquences (matrix profile) ou par erreur de reconstruction d'un autoencodeur (module 16).
  • Un rappel utile — sur un flux, tester en continu revient à faire des milliers de tests : sans contrôle du taux de fausses alertes (module 9), le système d'alerte devient du bruit et finit ignoré.

10. Analyse spectrale et filtrage

Sur un signal physique — vibration, acoustique, mesure de capteur —, le domaine fréquentiel révèle souvent ce que le domaine temporel cache. La FFT donne le périodogramme, la méthode de Welch le lisse en moyennant des segments (ce qui réduit la variance de l'estimation au prix de la résolution), et la densité spectrale de puissance identifie les composantes périodiques : une fréquence de rotation, une résonance, un harmonique de défaut de roulement.

Python
import numpy as np
from scipy import signal

f, Pxx = signal.welch(x, fs=25_600, nperseg=4096)        # densité spectrale de puissance
pic = f[np.argmax(Pxx)]
print(f"fréquence dominante : {pic:.1f} Hz")

# spectrogramme : évolution du contenu fréquentiel DANS LE TEMPS
f, t, Sxx = signal.spectrogram(x, fs=25_600, nperseg=1024)

# filtrage passe-bande SANS déphasage (filtrage aller-retour)
sos = signal.butter(4, [50, 2000], btype="band", fs=25_600, output="sos")
x_filtre = signal.sosfiltfilt(sos, x)      # filtfilt : phase nulle, ordre doublé

Deux usages en machine learning. D'abord, le spectre fournit d'excellentes variables : énergie par bande, fréquence dominante, kurtosis spectral, coefficients cepstraux — c'est la base de la maintenance prédictive sur signaux vibratoires. Ensuite, le spectrogramme transforme un signal unidimensionnel en image bidimensionnelle, ce qui permet d'appliquer directement un CNN (modules 16 et 18) — l'approche standard en classification audio.

Deux pièges classiques du traitement du signal restent valables ici : le repliement de spectre (respecter Shannon, filtrer avant d'échantillonner, sinon les hautes fréquences se déguisent en basses) et les fuites spectrales (utiliser une fenêtre de Hann plutôt qu'une troncature brutale, qui introduit des discontinuités artificielles). Et pour les phénomènes non stationnaires, la transformée en ondelettes offre un meilleur compromis temps-fréquence que la FFT à fenêtre.

11. Durées de vie, censure et fiabilité

Troisième famille temporelle, et la plus mal traitée en pratique : on veut prédire le temps avant un événement — panne d'un composant, résiliation d'un client, rechute d'un patient. La difficulté s'appelle la censure : à la fin de l'étude, beaucoup d'unités n'ont pas encore connu l'événement.

pourquoi on ne peut pas ignorer la censure
  unité 1  ├──────────✕                      ✕ = panne observée (durée connue : 8 mois)
  unité 2  ├───────────────────────→         → = toujours en service à la fin
  unité 3  ├────✕                                 (durée INCONNUE, mais ≥ 14 mois)
  unité 4  ├──────────────────→
           └──────────────────────────► temps

  Les EXCLURE   → on ne garde que les pannes précoces → durée de vie SOUS-ESTIMÉE
  Les compter   → on suppose qu'elles tombent en panne à la date de fin d'étude
  comme pannes     → encore sous-estimée
  → il faut une méthode qui utilise l'information "a survécu AU MOINS 14 mois"

Deux fonctions suffisent à tout exprimer — la survie et le taux de défaillance instantané :

\[ S(t) = \mathbb{P}(T \gt t), \qquad h(t) = \lim_{dt\to 0}\frac{\mathbb{P}(t \leq T \lt t+dt \mid T \geq t)}{dt}, \qquad S(t) = e^{-\int_0^t h(u)\,du} \]

\(h(t)\) se lit « sachant que l'unité a tenu jusqu'à \(t\), quel est son risque de tomber maintenant ? » — c'est la quantité qui intéresse un ingénieur fiabiliste, et elle est directement reliée à la survie par l'exponentielle de son intégrale.

les outils, par ordre d'usage
Kaplan-Meier      estimation non paramétrique de S(t), gérant la censure.
                  Le point de départ, toujours.
Test du log-rank  comparer deux courbes de survie (deux lots, deux traitements).
Cox (PH)          h(t|x) = h₀(t)·exp(βᵀx) : les covariables agissent de façon
                  MULTIPLICATIVE sur le risque, sans qu'on spécifie h₀.
                  exp(β) se lit comme un rapport de risques.
                  Hypothèse à vérifier : risques proportionnels (résidus de Schoenfeld).
Weibull / AFT     modèle paramétrique : les covariables ACCÉLÈRENT le temps.
                  Permet d'EXTRAPOLER au-delà de la fenêtre d'observation,
                  ce que Cox ne fait pas.
Survie par ML     Random Survival Forest, gradient boosting de Cox, DeepSurv :
                  capturent non-linéarités et interactions.

Le paramètre de forme de la loi de Weibull se lit directement comme un diagnostic de mode de défaillance : \(k \lt 1\) correspond à un taux décroissant (mortalité infantile, défauts de fabrication), \(k = 1\) à un taux constant (défaillances aléatoires, loi exponentielle sans mémoire), \(k \gt 1\) à un taux croissant (usure). C'est la lecture de la courbe en baignoire de la fiabilité, et c'est ce qui permet de distinguer un problème de qualité d'un problème de vieillissement — deux situations qui appellent des actions opposées.

Python — lifelines
from lifelines import KaplanMeierFitter, CoxPHFitter, WeibullFitter
from lifelines.statistics import logrank_test

# evenement = 1 si la panne a été observée, 0 si l'unité était encore en service
km = KaplanMeierFitter().fit(durees, evenement_observe)
km.plot_survival_function()
print("durée médiane de survie :", km.median_survival_time_)

# comparer deux lots
print(logrank_test(d_lotA, d_lotB, e_lotA, e_lotB).p_value)

cox = CoxPHFitter().fit(df, duration_col="duree", event_col="panne")
cox.print_summary()                      # rapports de risques et intervalles
cox.check_assumptions(df)                # test des risques proportionnels

wb = WeibullFitter().fit(durees, evenement_observe)
print(f"paramètre de forme ρ = {wb.rho_:.2f}")   # < 1 rodage, ≈ 1 aléatoire, > 1 usure

Le lien avec la maintenance prédictive

Prédire la durée de vie résiduelle (RUL) d'un équipement combine tout ce module : variables spectrales issues des capteurs (section 10), modèle de dégradation en espace d'états (section 8), et modèle de survie pour la probabilité de défaillance à un horizon donné (section 11). Deux exigences industrielles s'ajoutent : une prédiction probabiliste, parce qu'une décision de remplacement se prend sur un quantile bas et non sur une espérance (module 24), et une anticipation suffisante — un modèle qui prédit la panne deux heures avant est exact et inutile. La métrique doit donc incorporer le coût asymétrique entre une intervention inutile et une défaillance subie.

Ce qu'il faut retenir du module

  • L'autocorrélation réduit l'information effective : les tests et la validation doivent en tenir compte.
  • Décomposer et stationnariser avant de modéliser ; ACF et PACF restent les graphiques les plus instructifs.
  • ETS et ARIMA battent souvent le ML sur peu de séries courtes ; le ML gagne sur beaucoup de séries avec covariables.
  • Toute variable doit être strictement passée et réellement disponible au moment de la prévision.
  • Comparer systématiquement à la prévision naïve, et préférer MASE et CRPS.
  • Kalman estime un état caché en temps réel, avec un coût constant par pas ; le gain arbitre entre modèle et mesure.
  • La censure ne s'ignore pas : Kaplan-Meier, Cox et Weibull existent pour ça, et le paramètre de forme de Weibull diagnostique le mode de défaillance.

ARIMAPrévisionKalmanFFTSurvieFiabilité