Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
506 views
in Technique[技术] by (71.8m points)

d3.js - d3.scale.category10() not behaving as expected

I'm encountering unexpected behavior when using d3.scale.category10() to generate 10 fixed colors.

Starting out, I note that colors.range() returns an array of properly ordered colors, according to the documentation.

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

My expectation is that calling colors(0) will always return the zeroth item, colors(1) the first, and so on. However what I'm observing is that if I first call colors(1), the zeroth item is returned instead of the first from that point onward. Subsequently calling colors(0) will return the first item instead of the zeroth. So it appears that the return value is bound to the order that the indices are used, instead of the natural order.

Here's a fiddle: http://jsfiddle.net/LqHst/

To work around this, I'm just stepping through a loop to touch all the colors in the correct order.

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

Either I'm misunderstanding how this should work, or I'm blind to my incorrect usage. I've used this feature before and remember encountering the expected behavior, so I think I'm just doing something wrong or making an incorrect assumption.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You misunderstand the usage of category10.

As the document mentioned: d3.scale.category10() constructs a new ordinal scale with a range of ten categorical colors.

That is to say: var color = d3.scale.category10() will construct a new ordinal scale with empty domain and range with ten colors.

When you use the ordinal scale:

If no domain is set, a range must be set explicitly. Then, each unique value that is passed to the scale function will be assigned a new value from the output range; in other words, the domain will be inferred implicitly from usage. Although domains may thus be constructed implicitly,

https://github.com/mbostock/d3/wiki/Ordinal-Scales#wiki-ordinal_domain you can read the API of ordinal scale for more information.

Update: an ordinal-scale is a map, not an array.

If domain is not set explicit, the domain will be construct implicit with the key sequence you invoke color(key).

  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"]

This is useful when you just want to assign a different color for different clusters, and don't have a fixed color mapping. (Think about the situation: when your program support user upload data file as the data source.)

If you want to map each category to specific color, you have to set the domain explicit so that the mapping is not depend on the sequence of keys.

  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]

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...