D3JS - Histogramme (Bar Chart)

Comment réaliser un histogramme et lui associer une légende
d3js7.x
Sources :

La préparation

Nous commençons par établir les dimensions et les marges de notre graphique.

const margin = {top: 20, right: 20, bottom: 90, left: 120},
	width = 800 - margin.left - margin.right,
	height = 400 - margin.top - margin.bottom;

Les marges à gauche et en bas ont été augmenté pour l'affichage de la population et du pays.
Ensuite, nous définissons la fonction permettant le remplissage de l'axe des abcisses et celui des ordonnées.

const x = d3.scaleBand()
	.range([0, width])
	.padding(0.1);

const y = d3.scaleLinear()
	.range([height, 0]);

Le détails de ces méthodes peut être consulté sur cette page : d3-scale. Pour l'axe X, nous utilisons une échelle ordinale qui permet de représenter une liste de données ordonnées (ici le nom de nos pays). Nous précisons que cet axe va s'étendre sur toute la largeur de notre graphique grâce à la méthode range. Nous définissons ensuite l'espacement entre nos barres via la méthode padding. Pour l'axe Y, nous utilisons une échelle linéaire sur un range égal à la hauteur de notre graphique.

Il nous reste maintenant à créer notre SVG sur le noeud DOM avec l'ID chart. Nous précisons la taille de ce SVG ainsi qu'une fonction de transformation sur le noeud G permettant de tenir compte des marges et de positionner correctement l'ensemble.

const svg = d3.select("#chart").append("svg")
	.attr("id", "svg")
	.attr("width", width + margin.left + margin.right)
	.attr("height", height + margin.top + margin.bottom)
	.append("g")
	.attr("transform", "translate(" + margin.left + "," + margin.top + ")");

Il nous faut également ajouter un DIV pour notre tooltip.

const div = d3.select("body").append("div")
	.attr("class", "chart-tooltip")         
	.style("opacity", 0);

N'oubliez pas de regarder le code source pour récupérer le CSS associé à ce tooltip et au graphique.

Le traitement des données

Contrairement à l'introduction précédente dans laquelle nous avons utilisé un objet JSON, nous utiliserons cette fois-ci un fichier data.tsv (Tab Separated Values, il peut-être récupéré via firebug dans l'onglet réseau ou directement sur ce lien : data.tsv)

// On demande à D3JS de charger notre fichier
d3.tsv("/tutorials/d3js/barchart/data.tsv").then(function(data) {
	// Conversion des caractères en nombres
	data.forEach(d => d.population = +d.population);

	// Mise en relation du scale avec les données de notre fichier
	// Pour l'axe X, c'est la liste des pays
	// Pour l'axe Y, c'est le max des populations
	x.domain(data.map(d => d.country));
	y.domain([0, d3.max(data, d => d.population)]);
	
	// Ajout de l'axe X au SVG
	// Déplacement de l'axe horizontal et du futur texte (via la fonction translate) au bas du SVG
	// Selection des noeuds text, positionnement puis rotation
	svg.append("g")
		.attr("transform", "translate(0," + height + ")")
		.call(d3.axisBottom(x).tickSize(0))
		.selectAll("text")	
			.style("text-anchor", "end")
			.attr("dx", "-.8em")
			.attr("dy", ".15em")
			.attr("transform", "rotate(-65)");
	
	// Ajout de l'axe Y au SVG avec 6 éléments de légende en utilisant la fonction ticks (sinon D3JS en place autant qu'il peut).
	svg.append("g")
		.call(d3.axisLeft(y).ticks(6));

	// Ajout des bars en utilisant les données de notre fichier data.tsv
	// La largeur de la barre est déterminée par la fonction x
	// La hauteur par la fonction y en tenant compte de la population
	// La gestion des events de la souris pour le popup
	svg.selectAll(".bar")
		.data(data)
	.enter().append("rect")
		.attr("class", "bar")
		.attr("x", d => x(d.country))
		.attr("width", x.bandwidth())
		.attr("y", d => y(d.population))
		.attr("height", d => height - y(d.population))					
		.on("mouseover", function(event, d) {
			div.transition()        
				.duration(200)      
				.style("opacity", .9);
			div.html("Population : " + d.population)
				.style("left", (event.pageX + 10) + "px")     
				.style("top", (event.pageY - 50) + "px");
		})
		.on("mouseout", function(event, d) {
			div.transition()
				.duration(500)
				.style("opacity", 0);
		});
});

COMMENTAIRES

morgpion


Bonjour,
Merci et bravo pour les tutos !
Problème réglé ci-dessous avec explication.
Je rencontre une erreur "data.foreach is not a function", et plus haut "anonymous" pour la ligne:
"d3.tsv("d3js/barchart/data.tsv", function(error, data) {"
Si je mets un console.log(data[0].population) par exemple, data est inconnu.
Est-ce dû à une version de d3js ?

J'espère que vous aurez un peu de temps pour me répondre.
Morgan

morgpion


Ce problème venait d'un changement dans la v5 de d3 pour les promises :
d3.tsv("d3js/barchart/data.tsv", function(error, data) {
//...
})

doit être remplacé par
d3.tsv("d3js/barchart/data.tsv").then((data) {
//...
})

Avec .then, erreur est traité sans le passer en paramètre.

Je ne veux pas induire d'autres apprenants en erreur, pouvez-vous confirmer svp ?

sylTer


Bonjour,

Merci à l'auteur pour ce(s) super(s) exemple.
J'arrive parfaitement à faire tourner l'exemple en v4.
Lorsque je cherche à le faire tourner en V5 et *avec* le correctif de morgpion, je tombe sur une autre erreur avec cette fameuse ligne.

d3.tsv("d3js/barchart/data.tsv").then((data) {
SyntaxError: missing ) after argument list

Je cherche en vain l'origine du problème et suis preneur de toute piste ! :)

ericfrigot


Bonjour,
Je viens enfin de mettre à jour ce tutoriel pour qu'il soit bien compatible avec la v5 de D3JS.
Pour avoir souvent rencontré ce type d'erreur, je pense que vous avez oublié une parenthèse fermente.
Le code doit être :
d3.tsv("d3js/barchart/data.tsv").then((data) {
// Ici tout votre code de traitement du fichier
});

Sur la dernière ligne on doit bien trouver la fermeture de l'accolade, puis la fermeture de la parenthèse.

sylTer


Merci. C'est clair qu'il me manquait une parenthèse, mais pas à ce niveau là. D'ailleurs mon IDE faisait bien la correspondance entre ces (){}.

Bref, j'ai fais du copier/coller avec la nouvelle version est tout fonctionne.
Vraiment merci de ces tutoriaux pour découvrir JS et 3DJS (je viens de python et des SIG).
Au plasir de lire de nouveaux billets !

ericfrigot


Bonjour,
Je tarde un peu à vous répondre mais effectivement je confirme que le problème vient bien d'un changement depuis la version 5.
C'est pour cette raison qu'en haut de la liste de tutoriaux D3 j'indique la version mais je vais peut-être l'ajouter dans chaque tutoriel comme je le fais sur les exemples de la section "Playing". Merci en tout cas !

morgpion


Merci pour votre réponse (pas du tout tardive, pas de panique :) ). Effectivement je n'avais pas vu que les tutos étaient pour la v4. Par contre dans le tuto 1 pour d3 https://www.datavis.fr/inde... vous avez mis la balise script src pour la v5 dans l'exemple de head. C'est pour ça que je n'étais pas sûr d'avoir vu juste au sujet de l'erreur.
Bonne journée !

Pitch


Bonjour,

Merci pour ce beau tuto détaillé!

Ou peut-on trouver le code source mentionné ?

ericfrigot


Bonjour,
De quel code source parlez-vous ? Tout le code pour réaliser cet histogramme est présent directement sur la page pour la partie javascript.