d3 散点图标签

d3 scatterplot Labels

我是 d3 和前端的新手 你能看看我的轴上没有显示最大值吗?就像没有 2015 年或 Y 尺度上没有 39.50 甚至 d3.max 看到它们在数组中。

https://codepen.io/DeanWinchester88/pen/QWgGMja

const xScale = d3.scaleLinear()
                      .domain([ d3.min(years), d3.max(years)  ])
                      .range([paddings.paddingLeft,w - paddings.paddingRight - paddings.paddingLeft]);
      const xAxis = d3.axisBottom(xScale).tickFormat(d3.format("d"));
      svg.append("g")
                     .attr("transform", "translate(0," + (h - paddings.paddingLeft) + ")")
                      .attr("id","x-axis")
                      .call(xAxis);
  const yScale = d3.scaleLinear()
                      .domain([ parseInt(d3.min(timings)), parseInt(d3.max(timings))])
                      .range([h - paddings.paddingBottom, paddings.paddingTop]);
  const yAxis = d3.axisLeft(yScale).tickFormat( (d) =>  `(${d.toFixed(2)}`);
      svg.append("g")
       .attr("transform", "translate(" + paddings.paddingLeft + ",0)")
        .attr("id","y-axis")
       .call(yAxis)

如评论中所述,您如何解析 y 轴域存在问题:使用 parseInt 无法捕获完整域。这只会影响轴线延伸的距离,而不影响刻度线是否捕获整个域。

默认刻度数

至于刻度,D3 轴优先考虑干净的数字和理想的刻度数(如果我没记错的话,默认情况下为 10——尽管这个数字更像是一个建议而不是限制)。此外,轴生成器 考虑轴的渲染大小。

如果我们有一个从 1.999 到 9.999 的域,我们会看到我们的刻度为 2、3、4、5、6、7、8、9,如上所述优先考虑计数和圆度:

var scale = d3.scaleLinear()
  .domain([1.999,9.999])
  .range([20,290])
  
console.log(scale.ticks());

d3.select("body")
  .append("svg")
  .attr("transform","translate(0,50)")
  .call(d3.axisBottom(scale));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

轴延伸到9.999,只是超出了最后一个刻度,看起来有点奇怪

虽然这比 1.999、2.999、3.999... 的刻度在数量和文本量方面更清晰,但它没有显示域的范围。

在某些情况下,增加刻度数 (axis.ticks(idealNumberOfTicks)) 会产生预期的结果,在处理由代表年份的整数之类的域组成的域时,这可能是正确的,如您的情况:.ticks(d3.max(years)-d3.min(years));,这应该为每年创建刻度,因为在给定所需刻度数的情况下,整数被认为是合适的整数。

指定特定刻度

我们可以使用 axis.tickValues([values]) 指定要用作刻度的值,这将设置轴中使用的刻度。对于您的年轴,这非常简单,我们希望每个独特的年份都有一个刻度,因此我们只需要创建一个包含每一年的数组:

 .tickValues(d3.range(d3.min(years),d3.max(years))); 

事情并不总是这么简单。使用上面代码片段中的示例比例(更类似于您的 y 比例),我们可以使用 scale.ticks() 提供的刻度并添加两个新刻度,但这意味着重叠或不规则间距的可能性很高将手动报价与自动生成的报价相结合:

var scale = d3.scaleLinear()
  .domain([1.999,9.999])
  .range([20,290])
 
var ticks = [1.999,...scale.ticks(),9.999];

var axis = d3.axisBottom(scale)
  .tickValues(ticks)
  .tickFormat(d3.format(".3f"))
  
var svg  = d3.select("body")
  .append("svg")
  .attr("transform","translate(0,60)")
  .call(axis);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

我们可以使它更复杂以更好地设置刻度值,但它非常依赖于数据,通常会导致非圆形刻度值,并且可能通过使用 scale.nice() 更好地解决,如下所述。

如果丢弃自动生成的刻度,尤其是在处理时间或日期时,另一种选择是使用 d3.time 要求每隔指定的时间间隔进行一次刻度,如 d3-axis docs 例如:axis.ticks(d3.timeMinute.every(15));。但是,这不会设置刻度,因此它们包含整个域。

scale.nice()

然而,有一个比手动添加或覆盖自动刻度更好的选择,这是使用 scale.nice(),它:

Extends the domain so that it starts and ends on nice round values. This method typically modifies the scale’s domain, and may only extend the bounds to the nearest round value. An optional tick count argument allows greater control over the step size used to extend the bounds, guaranteeing that the returned ticks will exactly cover the domain. Nicing is useful if the domain is computed from data, say using extent, and may be irregular. For example, for a domain of [0.201479…, 0.996679…], a nice domain might be [0.2, 1.0]. If the domain has more than two values, nicing the domain only affects the first and last value. (docs)

以上面的例子为例,我们得到:

var scale = d3.scaleLinear()
  .domain([1.999,9.999])
  .range([20,290])
  .nice()
  
console.log(scale.ticks());

d3.select("body")
  .append("svg")
  .attr("transform","translate(0,50)")
  .call(d3.axisBottom(scale));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

这是我为您的 y 轴提供的解决方案,同时指定特定刻度或使用 axis.ticks() 指定刻度数是您 x 轴的理想选择。