16 - Axes


Lien vers le tutoriel original : http://alignedleft.com/tutorials/d3/axes


Axes

Dernière mise à jour le 30 décembre 2012

Après avoir maîtrisé l’usage des échelles de D3, on a ce nuage de points :

Grand nuage de points mis à l'échelle

Ajoutons des axes horizontaux et verticaux, pour que nous puissions enlever ces horribles nombres rouges qui encombrent la visibilité.

Introduction aux axes

Tout comme les fonctions d’échelle, les axes de D3 sont en fait des fonctions avec des paramètres que vous définissez. Au contraire des échelles, lorsque qu’une fonction d’axe est appelée, elle ne retourne pas une valeur, mais elle génère les éléments visuels de l’axe, comprenant des lignes, des étiquettes et des marques.

Notez que les fonctions d’axe sont spécifiques à SVG, en ce qu’elle génère des éléments SVG. De plus, les axes sont prévus pour être utilisés avec des échelles quantitatives (par opposition aux échelles ordinales).

Paramétrer un axe

Utilisez d3.svg.axis() pour créer une fonction d’axe générique :

var xAxis = d3.svg.axis();

Au minimum, chaque axe a aussi besoin qu’on lui dise sur quelle échelle il opère. Ici nous lui passerons xScale du code de notre nuage :

xAxis.scale(xScale);

On peut également lui spécifier où les étiquettes doivent apparaître relativement à l’axe lui-même. Par défaut c’est bottom, ce qui veut dire que les étiquettes apparaîtront en bas de la ligne d’axe. (Bien que c’est par défaut, ça ne peut pas faire de mal de le dire explicitement.)

xAxis.orient("bottom");

Bien sûr, on peut être plus concis et écrire tout ça en une ligne :

var xAxis = d3.svg.axis()
                  .scale(xScale)
                  .orient("bottom");

Enfin, pour générer l’axe et insérer toutes ces petites lignes et ces étiquettes dans notre SVG, on doit appeler la fonction xAxis. Je placerai ce code à la fin de notre script, pour que l’axe soit généré après les autres éléments SVG :

svg.append("g")
    .call(xAxis);

La fonction call() de D3 prend en entrée une sélection et transmet cette sélection à une fonction. Donc, dans notre cas, on vient juste d’ajouter un nouvel élément groupe g pour contenir tous nos éléments d’axe qui-vont-bientôt-être-générés. (Le g n’est pas rigoureusement nécessaire, mais permet de garder les éléments organisés et d’appliquer une classe au groupe entier, ce que nous ferons dans un instant.)

Ce g devient la sélection pour le prochain maillon de la chaîne. call() transmet la sélection à la fonction xAxis, donc notre axe est généré dans le nouveau g. Ce bout de code au-dessus est un raccourci pour cet équivalent :

svg.append("g")
    .call(d3.svg.axis()
                .scale(xScale)
                .orient("bottom"));

Vous voyez, vous pourriez écrire votre axe dans la fonction call(), mais il est généralement plus facile pour notre cerveau de définir d’abord des fonctions, et de les appeler plus tard.

Quoi qu’il arrive, voilà à quoi ça ressemble :

Un axe simple, mais laid

Faire le ménage

Techniquement c’est un axe, mais ce n’est ni joli ni utile. Pour le nettoyer, assignons d’abord une classe axis au nouvel élément g, pour pouvoir le sélectionner avec CSS :

svg.append("g")
    .attr("class", "axis")  // Assigne la classe "axis"
    .call(xAxis);

Ensuite, on ajoute nos styles CSS dans le <head> de notre page :

.axis path,
.axis line {
    fill: none;
    stroke: black;
    shape-rendering: crispEdges;
}

.axis text {
    font-family: sans-serif;
    font-size: 11px;
}

La propriété shape-rendering est un attribut SVG, utilisée ici pour s’assurer que notre axe et ses marques sont définis au pixel près. Pas d’axe flou pour nous !

Axe plus propre

C’est mieux, mais le haut de l’axe est coupé, et on veut, quoi qu’il arrive, qu’il soit à la base du graphique. On peut utiliser la fonction transform sur le groupe de l’axe, en le poussant vers le bas :

svg.append("g")
    .attr("class", "axis")
    .attr("transform", "translate(0," + (h - padding) + ")")
    .call(xAxis);

Notez l’utilisation de (h - padding), pour que le bord haut du groupe soit défini à h, la hauteur de notre image, moins la valeur de marge padding que l’on a créée plus tôt.

Axe propre et joli

C’est bien mieux ! Voilà le code jusqu’ici.

S’occuper des marques (ticks)

Les tiques (ticks) peuvent transmettre des maladies, mais les marques (ticks) de D3 communiquent de l’information. Et pourtant afficher plus de marques n’est pas forcément plus efficace, et à partir d’un certain moment elles commencent à surcharger votre graphique. Vous noterez que l’on a jamais spécifié combien de marques inclure dans l’axe, ni à quel intervalle elles devraient apparaître. Sans instructions données, D3 a examiné magiquement notre échelle xScale et en a déduit combien de marques ajouter, et à quel intervalle (tous les 50 dans notre cas).

Comme vous pouvez l’imaginer, vous pouvez personnaliser tous les aspects de vos axes, en commençant par un nombre approximatif de marques, en utilisant ticks() :

var xAxis = d3.svg.axis()
                  .scale(xScale)
                  .orient("bottom")
                  .ticks(5);  // Définit un nombre approximatif de 5 marques

Moins de marques

Voilà le code.

Vous avez probablement noté que, bien que l’on ait défini cinq marques, D3 a décidé d’en afficher sept. C’est parceque D3 assure vos arrières, et qu’il s’est rendu compte qu’afficher seulement cinq marques aurait demandé de découper notre domaine d’entrée en valeurs bien moins jolies — dans notre cas, 0, 150, 300, 450, et 600. D3 interprète la valeur dans ticks() comme une suggestion, et remplacera votre suggestion avec ce qu’il détermine comme la valeur la plus propre et la plus facile à lire par un humain — dans notre cas, des intervalles de 100 — même lorsque cela requiert d’ajouter quelques marques de plus que ce que vous avez demandé. C’est en fait une fonctionnalité brillante qui accroit l’évolutivité (scalability) de votre désign ; à mesure que l’ensemble de données évolue, et que le domaine d’entrée s’étend ou se contracte (des nombres plus grands ou plus petits), D3 s’assure que les marques restent claires et faciles à lire.

Pourquoi pas (Y Not) ?

Il est temps d’étiqueter notre axe vertical ! En copiant et en modifiant le code que l’on a déjà écrit pour l’axe xAxis, on ajoute ça vers le haut de notre code

// Définit l'axe Y
var yAxis = d3.svg.axis()
                  .scale(yScale)
                  .orient("left")
                  .ticks(5);

et ceci, près du bas :

// Créé l'axe Y
svg.append("g")
    .attr("class", "axis")
    .attr("transform", "translate(" + padding + ",0)")
    .call(yAxis);

Notez que les étiquettes seront orientées vers la gauche left et que le groupe g de l’axe yAxis est déplacé par translation vers la droite par un montant de padding.

Axe Y initial

Ça commence à prendre forme ! Mais les étiquettes de l’axe yAxis sont coupées. Pour leur donner plus de place sur la gauche, je vais augmenter la valeur de padding de 20 à 30 :

var padding = 30;

Bien sûr, vous pourriez aussi faire apparaître des variables séparées de padding pour chaque axe, disons xPadding et yPadding, pour avoir plus de contrôle sur la disposition du graphique.

Voilà le code, et voila à quoi il ressemble :

Nuage de points avec un axe Y

Touches finales

Pour vous prouvez que notre nouvel axe est dynamique et évolutif, j’aimerais passer d’un ensemble statique à un ensemble de nombres aléatoires :

// Ensemble dynamique de nombres aléatoires
var dataset = [];
var numDataPoints = 50;
var xRange = Math.random() * 1000;
var yRange = Math.random() * 1000;
for (var i = 0; i < numDataPoints; i++) {
    var newNumber1 = Math.round(Math.random() * xRange);
    var newNumber2 = Math.round(Math.random() * yRange);
    dataset.push([newNumber1, newNumber2]);
}

Ce code crée un nouveau tableau, itère 50 fois, choisit deux nombres aléatoires à chaque fois, et ajoute (pousse) cette paire de valeurs dans le tableau dataset.

Nuage de points avec données aléatoires

Essayez le code ici. Chaque fois que vous rechargez la page, vous obtenez différentes valeurs. Notez comment les axes se mettent à l’échelle pour correspondre aux nouveaux domaines, et comment les marques et les étiquettes sont choisies en conséquence.

Ma démonstration étant finie, je pense que l’on peut retirer ces horribles étiquettes rouges, en commentant les lignes de code qui les concernent :

Nuage de points avec des données aléatoires et sans étiquettes rouges

Le code final de notre nuage !

Mettre en forme les étiquettes

Une dernière chose : Jusqu’ici, on a travaillé avec des entiers qui sont propres et faciles à lire. Mais les données sont souvent plus complexes, et dans ces cas, vous voudrez avoir plus de contrôle sur la manière dont les étiquettes sont mises en forme. Découvrez tickFormat(), qui vous permet de spécifier comment les nombres doivent être formatés. Par exemple, vous pourriez souhaiter avoir trois chiffres après la virgule, ou afficher les valeurs en pourcentage, ou les deux.

Dans ce cas, vous définiriez d’abord une nouvelle fonction de mise en forme. Celle-ci, par exemple, dit de traiter les valeurs comme des pourcentages avec une précision d’une décimale. (Allez voir la référence de d3.format() pour plus d’options.)

var formatAsPercentage = d3.format(".1%");

Ensuite, transmettez-la à la fonction de formattage des marques :

xAxis.tickFormat(formatAsPercentage);

Conseil de développeur : Je trouve plus facile de tester ces fonctions de formattage dans la console JavaScript. Par exemple, ouvrez juste une page qui charge D3, comme notre nuage final, et écrivez votre règle de formattage dans la console. Ensuite testez-la en lui passant une valeur, comme vous le feriez avec n’importe quelle autre fonction :

Test de formattage dans la console

Vous pouvez voir ici que la valeur 0.54321 est convertie en 54.3% pour l’affichage — parfait !

Essayez ce code ici. Un formattage en pourcentage n’a pas de sens avec l’ensemble de données de notre nuage, mais à titre d’exercice, vous pourriez essayer de modifier comment les nombres aléatoires sont générés, pour avoir des nombres non-entiers (et compris entre 0 et 1), ou expérimenter la fonction de formattage elle-même.


Note finale du traducteur

La traduction de cette série de tutoriels est désormais terminée. J’espère qu’elle vous aura été utile. N’hésitez pas à la faire connaître autour de vous et à laisser un petit “Thanks for your tutorials on D3” sur Twitter à Scott Murray.

Après avoir intégré les bases de D3 en français, vous souhaiterez peut-être lire le livre écrit, en anglais, par Scott Murray sur D3. Il est disponible en édition papier, ou accessible, gratuitement, en ligne.

Un tutoriel visuel et intéractif sur les transitions est aussi disponible sur son site. Il suffit de cliquer sur le bouton Next > | Run it > et d’analyser les lignes de code.

Bonnes visualisations !