Une distance entre deux courbes qui tolère les décalages dans le temps : la DTW aligne les pics qui se ressemblent, même s'ils n'arrivent pas à la même heure. Elle sert à classer ou regrouper des profils de consommation, de capteurs ou de ventes quand la forme compte plus que l'horaire exact.
Deux personnes chantent la même chanson, l'une avec un temps de retard. Comparer note à note au même instant donne un grand écart. Les aligner d'abord montre qu'elles chantent la même chose.
On calcule l'écart entre chaque point de la première courbe et chaque point de la seconde : une grille de 24 × 24 écarts pour des courbes horaires.
On traverse la grille du début à la fin sans revenir en arrière. Le chemin peut associer une heure d'une courbe à plusieurs heures de l'autre : c'est l'étirement du temps.
Deux courbes de même forme mais décalées obtiennent une DTW faible, là où la distance euclidienne, qui compare heure par heure, les juge éloignées.
Une fenêtre (Sakoe-Chiba) interdit d'aligner des heures trop éloignées. Sans elle, un pic à 7 h peut être aligné sur un pic à 19 h et deux profils opposés se ressemblent.
Simulation de 200 foyers : consommation horaire avec un pic le matin, le soir, ou les deux. Chaque foyer vit avec son propre décalage, de -3 à +3 heures, et du bruit.
Chaque foyer est rattaché au profil type le plus proche. Le fournisseur adapte son offre : tarif heures creuses, programme d'effacement, conseils ciblés.
Sur une simulation, le vrai profil est connu. On compare trois distances : euclidienne, DTW sans limite et DTW avec une fenêtre de 3 heures. Seule la dernière classe correctement la quasi-totalité des foyers ; la DTW sans limite ne fait pas mieux que l'euclidienne.
Noms donnés pour R (dtw) et Python (tslearn).
La fenêtre de Sakoe-Chiba limite l'écart entre heures alignées (window.size / sakoe_chiba_radius). C'est le réglage décisif : il traduit le décalage que le métier juge acceptable, ici 3 heures.
Centrer et réduire chaque courbe (z-score) compare les formes et non les volumes. À éviter si le volume fait partie de la question.
Règle de déplacement dans la grille et écart local entre deux points. dtw en R utilise par défaut le motif symmetric2 et l'écart absolu, tslearn l'écart au carré : les distances ne se comparent pas d'un outil à l'autre.
# Courbes de charge : DTW en R
library(dtw)
set.seed(42)
heures <- 0:23
pic <- function(h) exp(-0.5 * ((heures - h) / 1.5)^2)
# Trois profils types de consommation journalière
types <- list(matin = pic(7), soir = pic(19), double = pic(7) + pic(19))
decaler <- function(x, d) x[((seq_along(x) - 1 - d) %% length(x)) + 1]
# 200 foyers simulés : un profil, décalé de -3 à +3 heures, plus du bruit
profils <- sample(names(types), 200, replace = TRUE)
decalages <- sample(-3:3, 200, replace = TRUE)
courbes <- t(mapply(function(p, d) decaler(types[[p]], d) + rnorm(24, 0, 0.1), profils, decalages))
# Chaque foyer est rattaché au profil type le plus proche
classer <- function(distance) apply(courbes, 1, function(x) names(types)[which.min(sapply(types, function(t) distance(x, t)))])
euclide <- classer(function(a, b) sqrt(sum((a - b)^2)))
dtw_libre <- classer(function(a, b) dtw(a, b)$distance)
dtw_fenetre <- classer(function(a, b) dtw(a, b, window.type = "sakoechiba", window.size = 3)$distance)
cat("Bien classés, distance euclidienne :", mean(euclide == profils), "\n")
cat("Bien classés, DTW sans fenêtre :", mean(dtw_libre == profils), "\n")
cat("Bien classés, DTW fenêtre 3 h :", mean(dtw_fenetre == profils), "\n")
# Courbes de charge : DTW en Python
import numpy as np
from tslearn.metrics import dtw
rng = np.random.default_rng(42)
heures = np.arange(24)
pic = lambda h: np.exp(-0.5 * ((heures - h) / 1.5) ** 2)
# Trois profils types de consommation journalière
types = {"matin": pic(7), "soir": pic(19), "double": pic(7) + pic(19)}
noms = list(types)
# 200 foyers simulés : un profil, décalé de -3 à +3 heures, plus du bruit
profils = rng.choice(noms, 200)
decalages = rng.integers(-3, 4, 200)
courbes = np.array([np.roll(types[p], d) + rng.normal(0, 0.1, 24) for p, d in zip(profils, decalages)])
def classer(distance):
# Chaque foyer est rattaché au profil type le plus proche
return np.array([noms[np.argmin([distance(c, types[k]) for k in noms])] for c in courbes])
euclide = classer(lambda a, b: np.linalg.norm(a - b))
dtw_libre = classer(lambda a, b: dtw(a, b))
dtw_fenetre = classer(lambda a, b: dtw(a, b, global_constraint="sakoe_chiba", sakoe_chiba_radius=3))
print("Bien classés, distance euclidienne :", (euclide == profils).mean())
print("Bien classés, DTW sans fenêtre :", (dtw_libre == profils).mean())
print("Bien classés, DTW fenêtre 3 h :", (dtw_fenetre == profils).mean())
La distance euclidienne compare les deux courbes instant par instant : un pic décalé d'une heure compte comme deux erreurs. La DTW aligne d'abord les courbes en étirant le temps, puis mesure l'écart restant. L'une compare des instants, l'autre des formes.
Pas au sens mathématique : elle ne respecte pas l'inégalité triangulaire. Les index de recherche rapide qui s'appuient sur cette propriété ne s'appliquent donc pas tels quels, et le K-means classique, qui fait des moyennes point par point, doit être adapté (moyenne DBA). Le plus proche voisin, la classification hiérarchique et les K-medoids fonctionnent sans problème.
Borner la fenêtre réduit déjà fortement le calcul. Des bornes inférieures rapides comme LB_Keogh écartent la plupart des candidats sans calculer la DTW complète. Au-delà de centaines de milliers de courbes, on résume souvent les courbes par tranches avant de les comparer.
Avec la DTW comme distance, le plus proche voisin reste une référence très difficile à battre pour classer des courbes.
Voir la fiche → pour segmenterAccepte n'importe quelle matrice de distances, DTW comprise. Donne des familles de profils sans fixer leur nombre à l'avance.
Voir la fiche → à adapterLe K-means classique fait des moyennes heure par heure : sur des courbes décalées, il fabrique des centres aplatis. tslearn propose une variante compatible DTW.
Voir la fiche →Dataistudio forme les équipes au machine learning et à l'IA, sur des cas concrets.
Nous utilisons des cookies de mesure d'audience et de suivi publicitaire pour comprendre la fréquentation du site et l'efficacité de nos annonces. Rien n'est déposé sans votre accord. En savoir plus