Aller au contenu principal

Module 7 — Variables de calendrier et régresseurs externes

Les modules 5 et 6 ont exploré deux familles qui prennent la série comme unique entrée. Ce module change de perspective : on transforme la prévision en un problème de régression dont les variables sont construites depuis le calendrier, l'historique et des sources externes. Cette approche, popularisée par les concours Kaggle et par l'industrie, autorise l'usage de gradient boosting — souvent le meilleur choix sur les séries à multiples saisonnalités et régresseurs.

De la série au tableau

L'idée centrale est de fabriquer, pour chaque date tt à prévoir, un vecteur de caractéristiques qui décrit son contexte : des retards de la série elle-même, des agrégats de fenêtre sur le passé récent, et des indicateurs de calendrier. Une fois ce tableau construit, n'importe quel régresseur — régression linéaire, random forest, gradient boosting — peut s'y appliquer.

import pandas as pd

def construire_variables(serie, retards=(7, 14, 21, 28), fenetres=(7, 28)):
df = serie.to_frame(name="y").copy()

# Retards : la valeur d'il y a k jours (jamais du jour meme).
for k in retards:
df[f"lag_{k}"] = df["y"].shift(k)

# Moyennes mobiles sur des fenetres du passe.
for f in fenetres:
df[f"moy_{f}"] = df["y"].shift(1).rolling(f).mean()
df[f"ecart_{f}"] = df["y"].shift(1).rolling(f).std()

# Variables de calendrier a partir de l'index.
df["jour_semaine"] = df.index.dayofweek
df["jour_mois"] = df.index.day
df["semaine_annee"] = df.index.isocalendar().week.astype(int)
df["mois"] = df.index.month

return df

Trois précautions critiques dans ce code. Tous les retards partent de shift(k) avec k1k \geq 1 : on n'utilise jamais la valeur du jour lui-même comme variable (ce serait de la fuite). Les moyennes mobiles partent aussi de shift(1).rolling(f) : la fenêtre inclut les ff jours antérieurs, jamais le jour en cours. Enfin, la construction se fait sur la série concaténée train + périmètre à prévoir, mais l'ajustement du modèle n'utilise que les lignes du train — ce point sera précisé plus bas.

Les variables de calendrier qui comptent

L'expérience empirique donne une short-list qui rend la plupart des projets performants.

Le jour de la semaine est le premier signal, y compris lorsque la saisonnalité hebdomadaire est déjà captée par un modèle statistique : le boosting apprend des interactions (samedi × promotion, dimanche × férié) qu'un modèle additif rate.

Le jour du mois, le mois et la semaine ISO captent les rythmes de paie, les vacances scolaires, le mois d'août calme. Un raffinement utile est le décalage à un férié : combien de jours nous séparent du prochain férié ? Combien depuis le dernier ? Ces deux variables font des miracles sur la vente au détail.

Les encodages cycliques — sinus et cosinus du jour de l'année, du jour de la semaine — sont préférables aux nombres bruts pour les modèles linéaires ; ils sont facultatifs pour un gradient boosting qui gère les splits sur des entiers sans problème. Cette différence de traitement est un point de confusion fréquent.

import numpy as np

def encodages_cycliques(df):
df = df.copy()
df["jour_annee_sin"] = np.sin(2 * np.pi * df.index.dayofyear / 365.25)
df["jour_annee_cos"] = np.cos(2 * np.pi * df.index.dayofyear / 365.25)
df["jour_semaine_sin"] = np.sin(2 * np.pi * df.index.dayofweek / 7)
df["jour_semaine_cos"] = np.cos(2 * np.pi * df.index.dayofweek / 7)
return df

Les régresseurs externes : la question qui compte

Trois familles se distinguent, et elles ne se comportent pas de la même façon.

Les régresseurs connus dans le futur sont des variables dont vous connaissez la valeur à l'horizon à prévoir : le calendrier des jours fériés, un planning de promotions, un calendrier scolaire. Ils sont utilisables sans précaution particulière — leur valeur est disponible au moment de la prédiction, ce n'est pas une fuite.

Les régresseurs prévisibles sont des variables futures qu'on peut anticiper avec une prévision propre : la météo à 7 jours donnée par Météo France, une prévision de trafic, un forecast interne. Utilisables, à condition d'utiliser à l'entraînement les prévisions à même horizon que celles disponibles en production, sinon vous entraînez un modèle qui exploite une information trop précise.

Les régresseurs inconnus dans le futur sont interdits d'usage naïf : la température réelle du jour, le nombre réel de patients en salle, la valeur d'un indicateur boursier. Les intégrer sans prévoir leur valeur revient à demander au modèle « prévoyez les ventes de demain sachant la température de demain » — ce n'est pas la prévision, c'est un ajustement en régime interpolé.

La faute la plus classique de ce module

Ajouter comme régresseur la valeur du jour à prédire d'une variable dont on n'aurait pas la mesure en production. Sur notre fil rouge, ajouter « incidence grippale du jour » comme régresseur est une fuite si l'incidence n'est publiée qu'une semaine après. Le score de validation sera excellent et le modèle échouera en production. La question à poser pour chaque régresseur : « à midi le lundi, ai-je la valeur du mardi ? »

Un modèle de gradient boosting sur notre fil rouge

import lightgbm as lgb
from sklearn.metrics import mean_absolute_error

# Construction avec retards, fenetres et cycles.
donnees = encodages_cycliques(construire_variables(ventes["quantite"]))
donnees["est_ferie"] = donnees.index.isin(feries_fr["ds"]).astype(int)

# Decoupage chronologique.
train = donnees.loc[:"2024-12-31"].dropna()
test = donnees.loc["2025-01-01":"2025-01-28"]

variables = [c for c in donnees.columns if c != "y"]

modele = lgb.LGBMRegressor(
n_estimators=800,
learning_rate=0.03,
num_leaves=64,
min_child_samples=30,
feature_fraction=0.8,
bagging_fraction=0.8,
bagging_freq=5,
random_state=42,
)
modele.fit(
train[variables], train["y"],
eval_set=[(test[variables], test["y"])],
eval_metric="mae",
)

prevision_gbm = modele.predict(test[variables])
print(f"gradient boosting : MAE = {mean_absolute_error(test['y'], prevision_gbm):.1f}")

Deux détails qui font une différence énorme. Le paramètre min_child_samples=30 bride le boosting sur des feuilles très petites qui mémoriseraient des dates uniques du train (une promotion précise du 15 juin 2023) et détérioreraient la généralisation. Le sous-échantillonnage de colonnes (feature_fraction=0.8) et de lignes par arbre (bagging_fraction=0.8) rend l'apprentissage plus robuste sur les régresseurs bruités.

Retards, fenêtres et horizons cohérents

Un piège subtil : pour prévoir à horizon 28 jours, les retards courts disponibles au moment de la prédiction sont ceux d'au moins 28 jours. Le retard 7 n'est utilisable qu'à condition de prédire de façon récursive — le modèle fournit lui-même sa propre valeur à t7t-7. Sur des horizons longs, la récursivité amplifie les erreurs.

La solution la plus honnête consiste à entraîner un modèle par horizon : un modèle pour t+1t+1, un autre pour t+7t+7, un autre pour t+28t+28. Chacun utilise uniquement des retards disponibles à son propre horizon. C'est plus coûteux mais nettement plus stable, et c'est le protocole du concours M5 gagnant.

Ne pas oublier le tableau

Sur notre fil rouge, un gradient boosting bien ajusté avec les variables ci-dessus atteint souvent une MAE inférieure de 20 à 35 % à celle du naïf saisonnier, et bat SARIMA et Prophet de 10 à 20 %. Cette avance justifie la complexité opérationnelle — plus de variables à surveiller, plus de tuyauterie à maintenir — surtout quand le métier a des régresseurs riches à fournir.

La checklist du régresseur externe

Pour chaque variable externe envisagée, poser ces trois questions dans l'ordre : (1) quand devient-elle disponible en production ? (2) une prévision de cette variable existe-t-elle à l'horizon voulu ? (3) sa qualité est-elle stable dans le temps ? Si la réponse à l'une des trois est floue, ne pas l'utiliser tant que la réponse n'est pas nette. C'est cette discipline qui rend un modèle déployable, indépendamment de sa performance en validation.

En résumé

  • Transformer la prévision en problème de régression ouvre l'accès au gradient boosting, souvent le meilleur outil sur séries à saisons multiples et régresseurs.
  • Les variables de calendrier utiles : jour de semaine, mois, semaine ISO, distance au prochain férié, encodages cycliques pour les modèles linéaires.
  • Distinguer les régresseurs connus, prévisibles et inconnus dans le futur ; les inconnus n'entrent que via leur propre prévision, jamais leur valeur réelle.
  • Sur les horizons longs, préférer un modèle par horizon à la récursivité, qui amplifie les erreurs.

Module suivant : LSTM et Transformers temporels, quand le profond gagne et quand il perd.