首页 > 解决方案 > d3 散点图标签

问题描述

我是 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)

标签: d3.jsscatter-plot

解决方案


如评论中所述,如何解析 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 文档中的示例所述:axis.ticks(d3.timeMinute.every(15));. 但是,这不会设置刻度,以便它们包含整个域。

scale.nice()

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

扩展域,使其以漂亮的圆形值开始和结束。此方法通常会修改比例的域,并且可能只会将边界扩展到最接近的舍入值。可选的滴答计数参数允许更好地控制用于扩展边界的步长,确保返回的滴答准确覆盖域。如果域是从数据中计算出来的,比如使用范围,并且可能是不规则的,那么 Nicing 很有用。例如,对于 [0.201479…, 0.996679…] 的域,一个不错的域可能是 [0.2, 1.0]。如果域有两个以上的值,则对域进行微调只会影响第一个值和最后一个值。(文档

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

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 轴来说是理想的。


推荐阅读