d3.scale.category10()的行为不符合预期

t.8*_*888 24 d3.js

当使用d3.scale.category10()生成10种固定颜色时,我遇到了意外的行为.

从开始,我注意到colors.range()返回一个正确排序颜色的数组,根据文档.

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

我的期望是调用颜色(0)将始终返回第0个项目,颜色(1)将返回第一个,依此类推.然而我所观察到的是,如果我第一次调用colors(1),则返回第0个项而不是从该点开始的第一个项.随后调用colors(0)将返回第一个项而不是第0个.因此,似乎返回值绑定到使用索引的顺序,而不是自然顺序.

这是一个小提琴:http://jsfiddle.net/LqHst/

为了解决这个问题,我只是通过一个循环来按正确的顺序触摸所有颜色.

for(var i = 0; i < 10; i++) {
  colors(i);
}
Run Code Online (Sandbox Code Playgroud)

要么我误解了它应该如何工作,要么我对我的错误使用视而不见.我之前使用过这个功能并且记得遇到了预期的行为,所以我认为我只是做错了什么或做出了错误的假设.

小智 26

你误解了它的用法category10.

正如文件所述:d3.scale.category10()构建一个具有十种分类颜色范围的新序数量表.

也就是说:var color = d3.scale.category10()将构建一个具有空域和范围有十种颜色的新序数量表.

使用序数时:

如果未设置域,则必须明确设置范围.然后,传递给scale函数的每个唯一值将从输出范围中分配一个新值; 换句话说,域将从使用中隐式推断.虽然域可能因此被隐式构造,

https://github.com/mbostock/d3/wiki/Ordinal-Scales#wiki-ordinal_domain您可以阅读序数比例的API以获取更多信息.

更新:序数比例是地图,而不是数组.

如果未明确设置域,则域将使用您调用颜色(键)的键序列隐式构造.

  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"]
Run Code Online (Sandbox Code Playgroud)

当您只想为不同的簇指定不同的颜色并且没有固定的颜色映射时,这非常有用.(想想这种情况:当你的程序支持用户上传数据文件作为数据源时.)

如果要将每个类别映射到特定颜色,则必须将域设置为显式,以便映射不依赖于键序列.

  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]
Run Code Online (Sandbox Code Playgroud)


t.8*_*888 24

事实证明,设置比例的域可以解决这个问题.

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)); 
Run Code Online (Sandbox Code Playgroud)

或者更简洁一点,

var colors = d3.scale.category10().domain(d3.range(0,10));
Run Code Online (Sandbox Code Playgroud)

更新小提琴:http://jsfiddle.net/LqHst/2/

创建category10比例时,将使用10种颜色和空域创建.

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

根据文档(和接受的答案),按顺序设置域是可选的.如果未设置域,则通过调用scale函数来假定其值.

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] 
Run Code Online (Sandbox Code Playgroud)

仅当给定索引不在域中时才会添加.

这就是为什么原始问题中所述的解决方法产生了预期的行为.通过for循环逐步调整比例,以自然顺序查询比例,向域添加有序索引.

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]
Run Code Online (Sandbox Code Playgroud)

故事的寓意是明确地设置域,以便获得更可预测的行为,如本答案的顶部所示.