D3中序数比例尺和线性比例尺的区别

43
var xScale = d3.scale.ordinal().domain([0, d3.max(data)]).rangeRoundBands([0, w], .1);
var yScale = d3.scale.linear().domain([0, data.length]).range([h, 0]);

我对D3中何时使用序数线性比例尺感到困惑。

从API文档中发现以下内容,但仍不太清楚...如果有人能帮忙,将不胜感激。

ordinal(x)

给定输入域中的值x,返回相应的输出范围中的值。

如果范围是明确指定的(例如由range指定,但不包括rangeBands、rangeRoundBands或rangePoints),并且给定值x不在比例尺的定义域中,则x会隐式添加到该域中;给定相同值x 的后续调用将从范围中返回相同的值y。

d3.scale.linear()

使用默认域[0,1]和默认范围[0,1]构建新的线性比例尺。因此,默认线性比例尺等同于数字的恒等函数;例如linear(0.5)返回0.5。


1
线性比例尺将在输入值之间进行插值,而序数比例尺则不会。 - Lars Kotthoff
@LarsKotthoff,您能否提供一些示例? - Bill
文档中实际上有一个例子,即 linear(0.5) - Lars Kotthoff
3个回答

90
关于序数尺度
序数尺度拥有离散的定义域,例如一组名称或类别。
序数尺度的值必须可强制转换为字符串,并且定义域值的字符串版本唯一地识别相应的范围值。
因此,作为示例,序数尺度的定义域可能包含名称,如下:
var ordinalScale = d3.scale.ordinal()
        .domain(['Alice', 'Bob'])
        .range([0, 100]);

ordinalScale('Alice'); // 0
ordinalScale('Bob'); // 100

请注意所有的值都是字符串。它们不能被插值。在“Alice”和“Bob”之间是什么?我不知道。D3也不知道。
现在,关于定量比例尺(例如线性比例尺):
定量比例尺具有连续的定义域,例如实数集或日期。
例如,您可以构建以下比例尺:
var linearScale = d3.scale.linear()
        .domain([0, 10])
        .range([0, 100]);

linearScale(0); // 0
linearScale(5); // 50
linearScale(10); // 100

请注意,即使我们没有在域中明确指定5,D3也能够插值它。
查看这个jsfiddle,以查看上述代码的运行情况。

5
哇哦... 你应该写d3 API文档,太棒了,完全有意义。非常感谢 :) - Bill
1
如果您的域中有超过2个项,例如['Alice', 'Bob', 'Carl'],那么您可能需要使用rangePoints而不是range - Tony Day
1
你能否不使用“离散域”、“量化刻度”、“插值”、“可强制转换”和“域”等术语来解释一下这个问题,以便我们这些普通人能够理解呢?或者至少给它们下一个定义。并非所有人都有数学背景。 - dthree
2
你好@dthree,很抱歉没有表达清楚。这些术语经常在D3文档和API中使用。我假设读者已经熟悉它们的含义。我也没有数学背景,但阅读有关函数域的内容是一个很好的起点,并帮助我更好地理解这些术语和D3本身。 - Oleg
非常有帮助,谢谢。然而,D3似乎确实知道Alice和Bob之间的关系。例如,AlicePointFive为0,而AlicePointSeven为1(请参见更新的fiddle)。对这些属性背后的逻辑有任何解释吗? - sc28

3

好的,我们可以从使用相同数据来比较它们的差异开始学习它(我正在使用d3 v4版本),假设我们有以下数据,使用ordinallinear比例尺:

const data = [1, 2, 3, 4, 5];

const scaleLinear = d3.scaleLinear()
  .domain([0, Math.max(...data)]).range([1, 100]);

const scaleOrdinal = d3.scaleOrdinal()
  .domain(data).range(['one', 'two', 'three', 'four', 'five']);

现在我们开始调用它们来查看结果:
scaleLinear(1); //20
scaleOrdinal(1); //one

scaleLinear(2); //40
scaleOrdinal(2); //two

scaleLinear(5); //100
scaleOrdinal(5); //five

看一下我们得到的函数和结果,如你所见,在普通函数中,我们将数据映射到我们的范围内,而在线性函数中,我们将其拉伸到范围内,因此在这些情况下,例如scaleLinear(1)将返回20……我们的域最大值为100,100除以5等于20,因此scaleLinear(1)20scaleLinear(2)40……。
但正如您所看到的,scaleOrdinal(1)被映射到范围内的数组,因此它等于onescaleOrdinal(2)等于two……
所以这就是您可以使用这些比例尺的方法,scaleLinear对于许多事情都很有用,包括在页面上呈现比例尺,但是scaleOrdinal更适用于获取有序数据,这就是文档中的解释:

# d3.scaleLinear() <>

构造一个新的连续比例尺,其中单位域为[0,1],单位范围为[0,1],默认插值器和禁用夹紧。线性比例尺是连续定量数据的一个很好的默认选择,因为它们保留了比例差异。每个范围值y都可以表示为域值x的函数:y = mx + b。


d3.scaleOrdinal([range]) <>

构造一个新的有序比例尺,其中包含一个空域和指定的范围。如果未指定范围,则默认为空数组;直到定义了非空范围,否则有序比例尺始终返回undefined。

此外,这是d3深入使用同时使用顺序和线性刻度的很好的示例:

var myData = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']

var linearScale = d3.scaleLinear()
  .domain([0, 11])
  .range([0, 600]);

var ordinalScale = d3.scaleOrdinal()
  .domain(myData)
  .range(['black', '#ccc', '#ccc']);

d3.select('#wrapper')
  .selectAll('text')
  .data(myData)
  .enter()
  .append('text')
  .attr('x', function(d, i) {
    return linearScale(i);
  })
  .text(function(d) {
    return d;
  })
  .style('fill', function(d) {
    return ordinalScale(d);
  });
body {
  font-family: "Helvetica Neue", Helvetica, sans-serif;
  font-size: 14px;
  color: #333;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.2/d3.min.js"></script>

<svg width="800" height="60">
   <g id="wrapper" transform="translate(100, 40)">
   </g>
</svg>


3
在D3.js中,比例尺会将一个数从定义域转化到值域。对于线性比例尺来说,定义域是一个连续变量,其取值范围没有限制,可以转化为连续的值域。对于序数比例尺,其定义域是离散的,例如一年中的月份,可能的取值范围有限且可以排序,但不是连续的。GitHub上的API文档可能比我讲得更清楚。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接