Module 5 — Variables cycliques, dates et durées
Une date est la variable la plus riche et la plus mal exploitée des jeux de données réels. Telle quelle, elle est inutilisable ; bien décomposée, elle porte des saisonnalités, des habitudes hebdomadaires, des effets de calendrier. Ce module montre comment l'ouvrir, et pourquoi un cas particulier — les cycles — demande un traitement inattendu.
Décomposer une date : une colonne devient dix variables
Un horodatage brut n'apprend rien au modèle. Il faut en extraire les composantes qui portent une régularité :
df["annee"] = df["date"].dt.year
df["mois"] = df["date"].dt.month
df["jour_semaine"] = df["date"].dt.dayofweek # 0 = lundi
df["heure"] = df["date"].dt.hour
df["fin_semaine"] = df["jour_semaine"].isin([5, 6]).astype(int)
df["debut_mois"] = (df["date"].dt.day <= 5).astype(int)
Chacune de ces variables répond à une question métier différente : l'année capte une tendance de fond, le mois une saisonnalité, le jour de la semaine des habitudes de consommation, l'heure des rythmes journaliers. Les indicateurs binaires (fin de semaine, début de mois) matérialisent des effets de seuil qu'un modèle linéaire ne trouverait pas seul.
Deux variables méritent d'être ajoutées quand le domaine s'y prête : les jours fériés et les périodes de vacances scolaires, décisifs sur les données de vente, de trafic ou de fréquentation. Ce sont des données externes, mais leur apport dépasse presque toujours le coût de les récupérer.
Le problème des cycles : décembre et janvier sont voisins
Voici le piège propre aux variables temporelles. Encodons le mois de 1 à 12. Pour le modèle, l'écart entre janvier (1) et décembre (12) vaut 11, alors qu'en réalité ces deux mois sont adjacents. Même problème entre 23 h et 0 h, ou entre dimanche et lundi.
La conséquence est concrète : une régularité qui traverse la frontière du cycle — une hausse d'activité de décembre à janvier — devient invisible, car le modèle voit deux valeurs très éloignées.
Le one-hot du module 4 résout la question en supprimant tout ordre, mais il perd la continuité : février n'est alors plus plus proche de janvier que de juillet. Pour douze mois c'est acceptable ; pour les minutes d'une journée, c'est intenable.
L'encodage cyclique par sinus et cosinus
La solution élégante consiste à placer le cycle sur un cercle. On convertit la valeur en angle, puis on prend son sinus et son cosinus :
où est la valeur et la période du cycle (12 pour les mois, 24 pour les heures, 7 pour les jours).
import numpy as np
df["mois_sin"] = np.sin(2 * np.pi * df["mois"] / 12)
df["mois_cos"] = np.cos(2 * np.pi * df["mois"] / 12)
Deux colonnes remplacent la variable, et la propriété recherchée est acquise : décembre et janvier se retrouvent géométriquement voisins sur le cercle, tout comme n'importe quelle paire de valeurs consécutives. Pourquoi deux fonctions et non une seule ? Parce que le sinus seul est ambigu — il prend la même valeur pour deux positions distinctes du cercle. Le couple sinus-cosinus identifie la position sans ambiguïté.
Cet encodage sert surtout les modèles sensibles aux distances et les modèles linéaires. Les arbres, qui découpent par seuils, s'en accommodent mais en profitent moins : ils peuvent isoler « mois = 12 » par une suite de coupes.
Durées, ancienneté et écarts : souvent le plus prédictif
Les variables les plus utiles ne sont pas les dates elles-mêmes mais les écarts entre dates. Elles répondent à des questions directement métier :
- ancienneté : nombre de jours depuis l'inscription ;
- récence : nombre de jours depuis le dernier achat — la lettre R du cadre RFM du cours précédent ;
- durée : temps écoulé entre deux étapes d'un processus ;
- temps jusqu'à un événement : jours restants avant l'échéance d'un contrat.
df["anciennete_jours"] = (df["date_reference"] - df["date_inscription"]).dt.days
Une précaution capitale sur la date de référence. Pour un modèle destiné à la production, l'ancienneté doit être calculée par rapport au moment de la prédiction, pas par rapport à une date fixe du jeu d'entraînement. Autrement, la variable se décale avec le temps et le modèle se dégrade silencieusement en production.
Utiliser une date postérieure à l'événement prédit crée une fuite massive. Prédire un désabonnement avec la variable « jours depuis le dernier contact au service client » alors que ce contact a lieu pendant la résiliation donne un modèle parfait et inutilisable. Pour chaque variable temporelle, posez la question : cette date est-elle connue au moment où je dois prédire ? C'est le sujet du module 8.
En résumé
- Une date brute n'apprend rien : on la décompose en année, mois, jour de semaine, heure, plus des indicateurs de seuil et, si possible, jours fériés et vacances.
- Numéroter un cycle rend décembre et janvier artificiellement lointains, ce qui masque les régularités traversant la frontière.
- L'encodage cyclique par sinus et cosinus place le cycle sur un cercle et rétablit la proximité ; le couple lève l'ambiguïté d'une fonction seule.
- Les écarts entre dates (ancienneté, récence, durée) sont souvent les variables les plus prédictives, à calculer par rapport au moment de la prédiction.
Module suivant : les variables de texte, avec les sacs de mots, les n-grammes et la pondération TF-IDF.