D3.js使用序数比例尺而非线性比例尺

4
为什么制作有序条形图时使用这种方法比线性比例尺更好?我听到的论点是,这可以保持数据集的顺序,但如果将单个数据的索引号设置为x坐标,不也可以做到这一点吗?
dataset = [ 5, 10, 13, 19, 21, 25, 22, 18, 15, 13];

var xScale = d3.scale.ordinal()
    .domain(d3.range(dataset.length))
1个回答

2
你应该使用序数尺度而不是线性尺度的原因很简单,尽管很多人会犯这个错误:
条形图本质上由代表分类变量的条形组成。这意味着条形位于代表定性变量的标签上方。当我说“很多人犯了这个错误”时,我指的是条形图和直方图之间的区别:两者都使用矩形来编码数据,但在直方图中,与条形图不同,标签代表一个定量变量。每个月至少有半打人在S.O.上发布关于直方图(实际上是条形图)或关于条形图(实际上是直方图)的问题。
所以,鉴于你的数据:
dataset = [5, 10, 13, 19, 21, 25, 22, 18, 15, 13];

第一根长条表示5,第二根长条表示10,以此类推。 长条中数值之间的差别是数量级的(例如,“10是5的两倍”),但长条之间的差别是质量上的。
因此,假设我们使用每个个体数据的索引号来标记此条形图中的长条(点击“运行代码片段”):

var w = 300,
  h = 200,
  padding = 20;
var svg = d3.select("body")
  .append("svg")
  .attr("width", w)
  .attr("height", h);

dataset = [5, 10, 13, 19, 21, 25, 22, 18, 15, 13];

var xScale = d3.scaleBand()
  .range([30, w])
  .domain(d3.range(dataset.length))
  .padding(0.2);

var yScale = d3.scaleLinear()
  .range([h - padding, padding])
  .domain([0, d3.max(dataset)]);

var bars = svg.selectAll("foo")
  .data(dataset)
  .enter()
  .append("rect")
  .attr("x", (d, i) => xScale(i))
  .attr("width", xScale.bandwidth())
  .attr("height", d => h - padding - yScale(d))
  .attr("y", d => yScale(d))
  .attr("fill", "teal");

var gX = svg.append("g")
  .attr("transform", "translate(0," + (h - padding) + ")")
  .call(d3.axisBottom(xScale));
  
var gY = svg.append("g")
  .attr("transform", "translate(30,0)")
  .call(d3.axisLeft(yScale));
<script src="https://d3js.org/d3.v4.min.js"></script>

我们在水平轴上看到数字0到9。现在来到重要的部分:这些数字实际上不是数字,它们是定性变量。您有条形图编号0、条形图编号1、条形图编号2……但是条形之间的差异(指条形本身而不是其值)是定性的,而不是数量的(在这个意义上,4不是2乘以2)。它们只是符号,就好像我们为标签使用了“A”、“B”、“C”等等。
当然,你可以简单地对数据进行排序,以显示升序或降序的条形图,但那会根本改变每个条形与其值之间的关系。如果您使用对象数组,可以保持关系。例如,看一下下面的代码片段:条形图已经排序,但是每个条形图的分类变量与原始数据相同

var w = 300,
  h = 200,
  padding = 20;
var svg = d3.select("body")
  .append("svg")
  .attr("width", w)
  .attr("height", h);

dataset = [5, 10, 13, 19, 21, 25, 22, 18, 15, 13];

var data = [];

dataset.forEach((d,i)=>data.push({index: i, value:d}));

data.sort((a,b)=>d3.descending(a.value, b.value));

var xScale = d3.scaleBand()
  .range([30, w])
  .domain(data.map(d=>d.index))
  .padding(0.2);

var yScale = d3.scaleLinear()
  .range([h - padding, padding])
  .domain([0, d3.max(data, d=>d.value)]);

var bars = svg.selectAll("foo")
  .data(data)
  .enter()
  .append("rect")
  .attr("x", (d) => xScale(d.index))
  .attr("width", xScale.bandwidth())
  .attr("height", d => h - padding - yScale(d.value))
  .attr("y", d => yScale(d.value))
  .attr("fill", "teal");

var gX = svg.append("g")
  .attr("transform", "translate(0," + (h - padding) + ")")
  .call(d3.axisBottom(xScale));
  
var gY = svg.append("g")
  .attr("transform", "translate(30,0)")
  .call(d3.axisLeft(yScale));
<script src="https://d3js.org/d3.v4.min.js"></script>

因此,这就是为什么我们使用顺序刻度(定义分类变量)创建条形图的原因。

2
你应该使用序数比例尺而不是线性比例尺的原因很简单,尽管很多人都会犯这个错误:条形图本质上由代表分类变量的条形组成。我现在非常惭愧,因为我也是那些人之一。我希望我能给它点赞,就像10次那样。 - Arthur Tarasov
@ArthurTarasov 不要太苛刻自己了:当我说“很多”时,我真的是指很多,甚至连书籍作者都会犯错。 - Gerardo Furtado

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