La vie d’un bouchon

Récemment le site kaggle.com a organisé une compétition de data-mining dont l’objectif était de prédire le temps de trajet d’une voiture sur différentes portions d’une autoroute australienne à différents moments.

Pour cela, le site a mis à disposition des données fournies par la NSW Roads and Traffic Authority (l’office australien des routes) : il s’agit d’enregistrements effectués toutes les trois minutes par 61 capteurs placés à différents points d’une autoroute. Au final on dispose de 61 séries temporelles contenant chacune environ 100 000 données.

L’une des difficultés de la compétition est que les voitures roulent à peu près tout le temps à la même vitesse et donc les temps de trajet sont presque tout le temps les mêmes, sauf lorsqu’un embouteillage se forme : dans ce cas le temps de trajet devient non seulement très élevé mais aussi assez volatile.

Une autre particularité est que les séries sont non seulement fortement autocorrélées, mais elles sont aussi corrélées entre elles dans le temps. En effet, si le temps de trajet augmente sur une portion de l’autoroute, il est probable que cela affecte quelques minutes plus tard le temps de trajet sur les portions adjacentes.

Une manière de visualiser ce phénomène consiste à représenter la fonction de corrélation croisée entre deux séries consécutives : cette fonction associé à tout entier k la corrélation entre la première série et la seconde série retardée de k périodes. Par exemple, le graphique suivant représente la fonction de corrélation croisée entre la première et la seconde série : les deux séries sont fortement corrélées, mais on peut voir que le pic de la fonction est atteint pour un retard de deux périodes, ie. six minutes.

Fonction de corrélation croisée entre la première et la seconde série. Le maximum est atteint pour un retard de deux périodes, c'est à dire six minutes.

La corrélation croisée entre la première et la quatrième série a à peu près la même forme, mais le maximum de la fonction est décalé vers la droite.

Fonction de corrélation croisée entre la première série et la quatrième série. On peut noter que le maximum s'est déplacé vers la droite par rapport au graphique précédent : autrement dit un choc affectant la circulation sur la quatrième portion aura un impact plus tardif sur le trafic de la première portion d'autoroute.

Cette démarche n’est ici pas très pratique car on obligé de réaliser un très grand nombre de graphiques pour voir les corrélations entre toutes les séries et parce qu’il n’est pas possible d’observer à la fois l’autocorrélation et les corrélations croisées.

Une alternative intéressante consiste à représenter sur un même les différentes routes, le temps et la variable d’intérêt. Sur le graphique ci-dessous, chaque ligne représente une portion de route et chaque colonne représente une date. Le temps de parcours sur une route donnée à un instant donné est représenté par la couleur du carré au croisement de la route et de l’instant considérés : plus la couleur est foncée, plus le temps de trajet est long.

Evolution du temps de trajet au cours du temps. Les temps de trajet ont été divisés par le temps de trajet moyen sur chaque portion de route de manière à tenir compte du fait que les différentes portions ont des longueurs différentes. Utilisez le slider pour vous déplacer dans le temps et survolez le graphique pour avoir des informations supplémentaires.

La plupart des points sont rose clair. Il s’agit de moments où le trafic est normal. Néanmoins, on observe régulièrement des amas de points de couleur foncée : ils correspondent vraisemblablement à des embouteillages. On peut noter qu’ils se forment généralement en semaine, toujours aux mêmes heures et souvent aux mêmes endroits.

Ces amas ont de plus deux caractéristiques intéressantes. La première est qu’ils présentent des sortes de stries orientées vers le bas et la droite. Il s’agit du phénomène capturé par la fonction de corrélation croisée dans le graphique ci-dessus. Il s’agit probablement d’une situation où un bouchon se forme sur un tronçon et s’allonge progressivement, ce qui affecte la fluidité du trafic sur les tronçons de route antérieurs.

La deuxième caractéristique intéressante est que les amas de points foncés ont une forme de triangle orienté vers le bas. La partie gauche du triangle correspond à la phase de naissance et d’allongement du bouchon, tandis que la partie droite correspond à la phase de résorption : le nombre de voitures qui entrent dans le bouchon devient moins important que celui des voitures qui en sortent ; l’embouteillage diminue donc en taille mais le trafic reste perturbé sur le tronçon où il s’est formé.

Le Code R

Les données utilisées peuvent être téléchargées depuis la page de la compétition. Néanmoins je mets à disposition les données directement au format R, avec quelques variables additionnelles. Vous pouvez les récupérer avec la ligne suivante :

# charge un data.frame nommé "data".

load(url("http://francois.guillem.free.fr/data/rta.rda"))

Pour calculer et visualiser la fonction de corrélation croisée, on utilise la fonction « ccf ». Cette fonction plante bizarrement en présence de valeurs manquantes donc attention de prendre une plage d’observations où il n’y a pas de valeur manquante.

ccf(data[5000:10000,2],data[5000:10000,3])

Enfin pour créer le graphique ci-dessus, on utilise la fonction « image ». Le code ci-dessous génère dans le répertoire de travail courant les 28 images utilisées dans l’animation ci-dessus.

# On sélectionne les données utilisées
df <- data[182:15082,2:62]

# On normalise les données en divisant par la moyenne.
# On élimine ainsi le fait que les portions de route n'ont pas
# toutes la même taille.
for (i in 1:61) {
  df[ ,i] <- df[ ,i]/mean(df[,i], na.rm = TRUE)
}

# On prend le logarithme de manière à diminuer la dispersion
# des données.
df <- log(df)

col <- colorRampPalette(c("white","red"))
zlim <- range(df,na.rm = TRUE)

# Un jour contient 480 données. On crée un graphique par jour 
png()
for (i in 182 + 240 + 0:27*480) {
  par(mar = c(2,0,0,0))
  image(x = i:(i+480),
        y = 1:61,
        z = as.matrix(df[i:(i+480),]),
        col = col(100),
        xaxt = "n",
        yaxt = "n",
        zlim = zlim)

  axis(1, 182 + 0:31*480,
       labels = format(seq(from=as.Date("2010-03-02"),
                             to = as.Date("2010-03-02") + 31,
                             by = 1),
                         "%A %d %B"))

  abline(v = 182+0:31*480)
  abline(h = 31)
}
dev.off()
Cette entrée a été publiée dans Visualisation. Vous pouvez la mettre en favoris avec ce permalien.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

*

Vous pouvez utiliser ces balises et attributs HTML : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>