web-dev-qa-db-fra.com

d3.scale.category10 () ne se comporte pas comme prévu

Je rencontre un comportement inattendu lorsque j'utilise d3.scale.category10 () pour générer 10 couleurs fixes. 

Pour commencer, je remarque que colors.range () renvoie un tableau de couleurs correctement ordonnées, conformément à la documentation

var colors = d3.scale.category10();
console.log(colors.range());
// -> ["#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd", "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf"] 

Mon attente est que l'appel des couleurs (0) renvoie toujours l'élément de zéro, les couleurs (1) en premier, etc. Cependant, ce que je remarque, c’est que si j’appelle d’abord les couleurs (1), l’élément zéro est renvoyé au lieu du premier à partir de ce moment. Par la suite, l'appel des couleurs (0) renverra le premier élément au lieu du zéro. Il semble donc que la valeur de retour est liée à l'ordre d'utilisation des indices, au lieu de l'ordre naturel. 

Voici un violon: http://jsfiddle.net/LqHst/

Pour contourner ce problème, je me contente de parcourir une boucle pour toucher toutes les couleurs dans le bon ordre.

for(var i = 0; i < 10; i++) {
  colors(i);
}

Soit je ne comprends pas comment cela devrait fonctionner, soit je suis aveugle à mon utilisation incorrecte. J'ai déjà utilisé cette fonctionnalité et je me souviens avoir rencontré le comportement attendu. Je pense donc que je ne fais que mal ou que je fais une hypothèse incorrecte.

24
t.888

Vous avez mal compris l'utilisation de category10.

Comme indiqué dans le document: d3.scale.category10() construit une nouvelle échelle ordinale avec une gamme de dix couleurs catégoriques.

C'est-à-dire: var color = d3.scale.category10() construira une nouvelle échelle ordinale avec un domaine vide et une plage de dix couleurs.

Lorsque vous utilisez l'échelle ordinale:

Si aucun domaine n'est défini, une plage doit être définie explicitement. Ensuite, chaque nouvelle valeur transmise à la fonction scale se verra attribuer une nouvelle valeur à partir de la plage en sortie. En d'autres termes, le domaine sera implicitement déduit de les domaines peuvent donc être construits implicitement}

https://github.com/mbostock/d3/wiki/Ordinal-Scales#wiki-ordinal_domain vous pouvez lire l'API de l'échelle ordinale pour plus d'informations.

Update: une échelle ordinale est une carte, pas un tableau.

Si le domaine n'est pas défini explicitement, le domaine sera construit de manière implicite avec la séquence de clés que vous appelez color (clé).

  var color = d3.scale.category10();

  console.log(color.domain()); // []

  color("aaa");
  console.log(color.domain()); // ["aaa"]

  color("bbb");
  console.log(color.domain());  // ["aaa", "bbb"]

  color("ccc");
  console.log(color.domain()); // ["aaa", "bbb", "ccc"]

Ceci est utile lorsque vous souhaitez simplement attribuer une couleur différente pour différentes grappes et que vous n'avez pas de mappage de couleurs fixe. (Pensez à la situation: lorsque votre programme prend en charge un utilisateur, téléchargez un fichier de données comme source de données.)

Si vous souhaitez mapper chaque catégorie à une couleur spécifique, vous devez définir le domaine explicitement afin que le mappage ne dépende pas de la séquence de clés.

  var color = d3.scale.category10();

  var domain = ["bbb", "ddd", "ccc", "23", "hello"];

  color.domain(domain);

  console.log(color.domain()); // ["bbb", "ddd", "ccc", "23", "hello"] 
  console.log(color.range());  // ["#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd", "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf"] 

  color("ddd"); // "#ff7f0e" : d3 will get index of "ddd" and return range[index]
26
qiu-deqing

Il s'avère que la définition du domaine de la balance résout ce problème. 

var colors = d3.scale.category10().domain([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);

// Now this will return the first item instead of the zeroth.
console.log(colors(1)); 

Ou un peu plus succinctement,

var colors = d3.scale.category10().domain(d3.range(0,10));

Violon mis à jour: http://jsfiddle.net/LqHst/2/

Lorsque l'échelle category10 est créée, elle est créée avec une plage de 10 couleurs et un domaine vide.

var colors = d3.scale.category10();
console.log(colors.range()); // -> ["#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd", "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf"]
console.log(colors.domain()); // -> []

Selon le documentation (et la réponse acceptée), la définition du domaine sur une échelle ordinale est facultative. Lorsqu'aucun domaine n'est défini, ses valeurs sont supposées à partir des appels à la fonction de balance.

var colors = d3.scale.category10();
console.log(colors.domain()); // -> []
console.log(colors(1)); // -> #1f77b4
console.log(colors.domain()); // -> [1]
console.log(colors(0), colors(3), colors(7)); // -> #ff7f0e #2ca02c #d62728
console.log(colors.domain()); // -> [1, 0, 3, 7] 

Ce n'est que si l'index donné n'est pas déjà dans le domaine qu'il est ajouté.

C'est pourquoi la solution de contournement, comme indiqué dans la question initiale, a provoqué le comportement attendu. En parcourant l'échelle via une boucle for, elle interroge celle-ci dans un ordre naturel, en ajoutant des index ordonnés au domaine.

var colors = d3.scale.category10();

for (var i = 0; i < 10; i++) {
    colors(i)
}

console.log(colors.domain()); // -> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

La morale de l'histoire est de définir le domaine de manière explicite, afin d'obtenir un comportement plus prévisible, comme indiqué au début de cette réponse.

24
t.888

J'utilise cette solution pour définir la couleur du graphique généré et de sa légende:

this.color = d3.scale.category20().range()[Math.floor(Math.random()*100%20)];
0
Andriy Boyko