Lien vers le tutoriel original : http://alignedleft.com/tutorials/d3/making-a-bar-chart
Faire un graphique à barres
Dernière mise à jour le 30 décembre 2012
Intégrons maintenant tout ce que l’on a appris jusqu’ici pour générer un graphique à barres simple avec D3.
On commencera par revoir le graphique à barres que l’on a fait plutôt en utilisant des éléments div. Puis on adaptera ce code pour dessiner les barres avec SVG, nous donnant plus de flexibilité sur la présentation visuelle. Finalement, on ajoutera des étiquettes (labels), pour voir plus clairement les valeurs.
Le vieux graphique
Voilà ce que l’on avait la dernière fois, avec des nouvelles données.
var dataset = [ 5, 10, 13, 19, 21, 25, 22, 18, 15, 13,
11, 12, 15, 20, 18, 17, 16, 18, 23, 25 ];
d3.select("body").selectAll("div")
.data(dataset)
.enter()
.append("div")
.attr("class", "bar")
.style("height", function(d) {
var barHeight = d * 5;
return barHeight + "px";
});
Ça peut être difficile à imaginer, mais on peut clairement améliorer ce simpe graphique à barres fait avec des divs.
Le nouveau graphique
En premier, on doit définir la taille du nouveau SVG :
// Largeur et hauteur
var w = 500;
var h = 100;
(Bien sûr, vous pourriez appeler w et h d’une autre manière, comme svgWidth et svgHeight. Utilisez ce qui vous semble le plus clair. JavaScript a une culture de l’efficacité, donc vous verrez souvent des variables à un seul caractère, du code écrit sans espaces, et d’autres syntaxes difficiles à lire, et pourtant programmatiquement efficace.)
Ensuite, on dit à D3 de créer un élément SVG vide et on l’ajoute au DOM :
// Crée l'élément SVG
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
Ça insert un nouvel élément <svg> juste avant la balise fermante </body>, et assigne au SVG les dimensions de 500 par 100 pixels. Cette déclaration met également le résultat dans une nouvelle variable appelée svg, pour que nous puissions facilement référencer SVG sans avoir à le resélectionner plus tard en utilisant quelque chose comme d3.select(“svg”).
Ensuite, plutôt que de créer des divs, on génère des rects et on les ajoute à svg.
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", 20)
.attr("height", 100);
Ce code sélectionne tous les rects à l’intérieur de svg. Bien sûr, il n’y en a encore aucun, donc une sélection vide est retournée. (Bizarre, je vous l’accorde, mais restez avec moi. Avec D3, vous devez toujours sélectionner quoi que ce soit sur lequel vous voulez travailler, même si cette sélection est vide.)
Ensuite, data(dataset) voit que l’on a 20 valeurs dans l’ensemble de données, donc il appelle enter() 20 fois. enter(), à son tour, retourne une sélection placeholder pour chaque élément de données qui n’a pas de rect correspondant — ce qui veut dire tous les rects.
Pour chacun des 20 placeholders, append(“rect”) insert un rect dans le DOM. Comme nous l’avons appris dans l’introduction à SVG, chaque rect doit avoir les valeurs x, y, width, et height définies. On utilise attr() pour ajouter ces attributs sur chacun des rect nouvellement créés.
Okay, pas sûr. Toutes les barres sont présentes (regardez le DOM sur la page de démo avec votre inspecteur web), mais toutes partagent les mêmes valeurs de x, y, width, et height, avec pour résultat qu’elles se superposent toutes. Ce n’est pas encore de la visualisation de données.
Corrigeons d’abord le fait qu’elles se superposent. Plutôt que d’utiliser un x de zéro, on lui assignera une valeur dynamique qui correspond à i, la position de chaque élément dans l’ensemble de données. Donc la première barre sera à zéro, mais les suivantes seront à 21, puis 42, et ainsi de suite.
.attr("x", function(d, i) {
return i * 21; // Largeur de barre de 20 plus 1 pour la marge
})
Ça fonctionne, mais c’est pas super flexible. Si nos données étaient plus nombreuses, les barres se suivraient sur la droite, même après la fin du SVG ! Comme chaque barre fait 20 pixels de large, plus 1 pixel de marge, un SVG de 500 pixels de large ne pourrait afficher que 23 éléments de données. Notez comment la 24ème barre se trouve coupée :
Une bonne pratique est d’utiliser des coordonnées flexibles et dynamiques — hauteurs, largeurs, valeurs de x, et valeurs de y — de manière à ce que votre visualisation se redimensionne de manière appropriée en fonction de vos données.
Comme avec n’importe quel problème en programmation, il y a mille façons d’arriver à ce résultat. J’en utiliserai une simple. Premièrement, je vais changer la ligne où l’on définit la position x de chaque barre :
.attr("x", function(d, i) {
return i * (w / dataset.length);
})
Notez comment la valeur de x est maintenant liée directement à la largeur de SVG (w) et au nombre de valeurs dans notre ensemble de données (dataset.length). C’est excitant, car maintenant nos barres seront espacées uniformément, que l’on ait vingt barres :
ou juste cinq :
Maintenant on devrait rendre les largeurs des barres proportionnelles, elles aussi, pour qu’elles deviennent plus fines à mesure que des données sont ajoutées, ou plus large lorsqu’il y a moins de valeurs. Je vais ajouter une nouvelle variable à côté de là où l’on définit la hauteur et la largeur de SVG
// Largeur et hauteur
var w = 500;
var h = 100;
var barPadding = 1; // <-- Nouveau !
puis faire référence à cette variable dans la ligne où l’on définit la largeur width de chaque barre. Plutôt qu’une valeur statique de 20, la largeur sera maintenant définit comme une fraction entre la largeur du SVG et le nombre de données, moins la valeur de la marge :
.attr("width", w / dataset.length - barPadding)
Ça fonctionne ! Les largeurs et les positions en x des barres se mettent à l’échelle correctement qu’il y ait vingt points, juste cinq
ou même cent :
Pour finir, programmons la hauteur de chaque barre. Vous espériez que ce soit aussi facile que faire référence à la valeur d lorsque l’on définit la hauteur height :
.attr("height", function(d) {
return d;
});
Hum, ça à l’air funky. On pourrait peut-être agrandir un peu nos nombres ?
.attr("height", function(d) {
return d * 4; // <-- Fois quatre !
});
Hélas, c’est pas aussi facile — on veut que nos barres grandissent depuis le bord bas, pas depuis le haut — mais ne blamez pas D3, blamez SVG.
Rappellez-vous de l’introduction à SVG : lorsque l’on dessine des rects, les valeurs x et y spécifient les coordonnées du coin en haut à gauche. Ce qui veut dire que l’origine ou point de référence de chaque rect est son point supérieur gauche. Pour nos besoins, il serait teeeeelllement plus facile que notre point de référence soit le point inférieur gauche, mais c’est juste pas comme ça que SVG fonctionne, et, franchement, SVG est plutôt indifférent à vos sentiments en la matière.
Étant donné que nos barres doivent “grandir depuis le haut”, où est “le haut” de chaque barre par rapport au haut de SVG ? Eh bien, le haut de chaque barre peut être définit comme une relation entre la hauteur de SVG et la valeur correspondante de la donnée, comme ça :
.attr("y", function(d) {
return h - d; // Hauteur moins la valeur de la donnée
})
Ensuite, pour mettre le “bas” de chaque barre au bas du SVG, chaque hauteur des rects doit être juste la valeur de la donnée elle-même :
.attr("height", function(d) {
return d; // Juste la valeur de la donnée
});
Agrandissons le tout un peu en changeant d par d * 4. (Note : Plus tard on en apprendra plus sur les échelles de D3, qui offrent de meilleurs moyens d’accomplir cela.)
Voilà le code de notre graphique où nos barres grandissent du bas.
Couleurs
Ajouter une couleur est facile. Utilisez juste attr() pour définir un remplissage fill :
.attr("fill", "teal");
Teal correspond à la couleur appelée sarcelle
Voila un graphique à barres tout en sarcelle. Mais souvent vous voudrez ajuster la couleur de la forme pour refléter une des qualités de la donnée. Ce qui veut dire que vous voudrez programmer la donnée en couleur. (Pour notre graphique, ça donne une programmation double, dans laquelle la même valeur de donnée est programmée dans deux propriétés visuelles : la hauteur et la couleur.)
Utiliser une donnée pour définir une couleur revient à écrire une fonction qui elle aussi référence d. Ici nous remplaçons “teal” par une fonction personnalisée :
.attr("fill", function(d) {
return "rgb(0, 0, " + (d * 10) + ")";
});
Voilà le code. C’est pas particulièrement un codage visuel utile, mais au moins vous avez une idée de comment traduire la donnée en couleur. Ici, d est multiplé par 10, puis il est utilisé dans la valeur du bleu dans une définition de couleur rgb(). Plus grande sera la valeur de d (barres plus grandes), plus bleue la barre sera. Pour des valeurs plus petites de d (barres plus petites) les barres seront moins bleues (plus proche du noir).
Étiquettes (ou labels)
Les visuels sont supers, mais quelques fois vous aurez besoin d’afficher les valeurs des données en texte dans votre visualisation. C’est la que les étiquettes de valeur entrent en jeu, et elles sont vraiment, vraiment faciles à générer avec D3.
Vous vous rappelez de l’introduction à SVG : vous pouvez ajouter des éléments text à l’élément SVG. On débutera avec :
svg.selectAll("text")
.data(dataset)
.enter()
.append("text")
Ça semble familier ? Tout comme on a fait pour les rects, on fait de même pour les texts. D’abord, sélectionnez ce que vous voulez, amenez les données, ajoutez les nouveaux éléments avec enter() (qui sont juste des placeholders à ce moment), et finallement ajoutez les nouveaux éléments text elements au DOM.
Complétons ce code pour inclure la valeur de la donnée dans chaque élément de texte en utilisant la méthode text()
.text(function(d) {
return d;
})
et définissons les valeurs x et y pour positionner le texte. C’est plus facile si je copie/colle juste le même code que l’on a utilisé plus haut pour les barres :
.attr("x", function(d, i) {
return i * (w / dataset.length);
})
.attr("y", function(d) {
return h - (d * 4);
});
Aha ! Des étiquettes ! Mais certaines sont coupées en haut. Essayons de les déplacer vers le bas, à l’intérieur des barres, en ajoutant quelques pixels à x et y :
.attr("x", function(d, i) {
return i * (w / dataset.length) + 5; // +5
})
.attr("y", function(d) {
return h - (d * 4) + 15; // +15
});
Mieux, mais pas lisible. Heureusement, on peut corriger ça :
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "white");
Fantasti-code ! Si vous n’êtes pas un maniaque de typographie, alors ça suffit. Si, au contraire, vous êtes comme moi, vous noterez que les étiquettes ne sont pas parfaitement alignées dans leur barre. C’est assez facile de corriger ça. Utilisons l’attribut SVG text-anchor pour centrer le texte horizontalement par rapport à x :
.attr("text-anchor", "middle")
Ensuite, changeons la manière de calculer la position x en la définissant comme le côté gauche de chaque barre plus la moitié de la largeur de la barre :
.attr("x", function(d, i) {
return i * (w / dataset.length) + (w / dataset.length - barPadding) / 2;
})
Montons également les étiquettes d’un pixel pour un espacement parfait :
.attr("y", function(d) {
return h - (d * 4) + 14; // 15 vaut est maintenant 14
})
Fini ! Maintenant, éloignons-nous un peu des graphiques à barres.