【问题标题】:Creating a categorical line chart in D3.js (V4)在 D3.js (V4) 中创建分类折线图
【发布时间】:2017-01-02 12:30:00
【问题描述】:

相对是 D3.js 的新手,我正在可视化我的 busdatasimple.json 文件中的“PassengersIn”和“PassengersOut”值。作为参考,其中一个 JSON 对象如下所示;

  {
    "BusNo": "1",
    "Date": "21 November 2016",
    "Time": "09:10:34 AM",
    "Destination": "Pier 50",
    "Longitude": "-122.383262",
    "Latitude": "37.773644",
    "PassengersIn": "8",
    "PassengersOut": "1"
  }

我现在正在尝试使用折线图上的两条线将PassengersIn 和PassengersOut 与Destination 绘制成图表。我在轴上苦苦挣扎,因为 x 只有 2 个刻度,而 y 没有缩放到我的数据。如下图所示;

我的代码如下。我已经删除了不相关的 Google Maps 和 jQuery。

//Setting The Dimensions Of The Canvas
var margin = {top: 20, right: 20, bottom: 70, left: 40},
    width = 650 - margin.left - margin.right,
    height = 350 - margin.top - margin.bottom;

//Setting X & Y Ranges
var x = d3.scaleOrdinal().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);

//Define The Axes
var xAxis = d3.axisBottom().scale(x);
var yAxis = d3.axisLeft().scale(y).ticks(10);

//Add The SVG Element
var svg = d3.select("body").append("svg")
    .attr("width", width + margin.left + margin.right + 50)
    .attr("height", height + margin.top + margin.bottom + 200)
    .attr("class", "svg")
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

//Load Data From JSON
d3.json("busdatasimple.json", function(error, data) {
  //Functions for Y-Axis Grid Lines
  function yGridLines() {
    return d3.axisLeft().scale(y).ticks(5);
  }

  //Adding the Y-Axis Grid Lines
  svg.append("g")
      .attr("class", "grid-lines")
      .call(yGridLines().tickSize(-width, 0, 0).tickFormat(""));

  //Adding Y-Axis
  svg.append("g")
      .attr("class", "y axis").call(yAxis)
    .append("text")
      .attr("transform", "rotate(-90)")
      .attr("y", 5)
      .attr("dy", ".71em")
      .style("text-anchor", "end")
      .text("Passengers In");
  
   //Adding X-Axis (Added to the end of the code so the label show over bottom bars)
   svg.append("g")
     .attr("class", "x axis")
     .attr("transform", "translate(0," + height + ")")
     .call(xAxis)
   .selectAll("text")
     .style("text-anchor", "middle")
     //.attr("dx", "-.8em")
     .attr("dy", "-.55em")
     .attr("transform", "translate(-5, 15)")
     .attr("font-family", "Arial")
     .attr("font-weight", "bold")
     .attr("font-size", "1.1em");

    x.domain(data.map(function(d){return d.Destination;}));
    y.domain([d3.min(data, function(d){return d.PassengersIn;}), d3.max(data, function(d)     {return d.PassengersIn;})]);

    var line = d3.line()
      .x(function(d){return x(d.Destination);})
      .y(function(d){return y(d.PassengersIn);});

    svg.append("path").datum(data)
      .attr("class", "line")
      .attr("d", function(d){return d.PassengersIn;})
      .attr("stroke", "green")
      .attr("stroke-width", 2);
});

我设法找到了一些使用分类序数比例的示例,但是,它们都使用 d3.js 的 v3,并且在无数次阅读 v4 API 之后,我仍然无法弄清楚。

【问题讨论】:

  • 很遗憾,您的两个链接都已损坏。
  • @KevinGhaboosi 我会尽快将它们重新上传到我的网站并修复链接。

标签: javascript json d3.js linechart


【解决方案1】:

你想要一个分类尺度,没错,但你不想要一个序数尺度

从 v3 到 v4 有很多变化。在 v3 中,您可以将 .rangeBands、.rangeRoundBands、.rangePoints 和 .rangeRoundPoints 设置为您的序数比例,因此可以接受连续范围。不再是:在 D3 v4 中,您拥有全新的 scaleBandscalePoint

在 v4 中,以 常规 序数比例(即scaleOrdinal):

如果指定了范围,则将序数刻度的范围设置为指定的值数组。域中的第一个元素将映射到范围中的第一个元素,第二个域值映射到第二个范围值,依此类推。如果范围中的元素少于域中的元素,则比例尺将重用范围开头的值。 (强调我的)

因此,在scaleOrdinal 中,范围需要具有与域相同的长度(元素数)。

话虽如此,您需要一个点刻度 (scalePoint)。带刻度和点刻度...

...除了输出范围是连续和数字之外,类似于序数刻度。 (强调我的)

查看这个sn-p,看看控制台,比较两个刻度:

var destinations = ["foo", "bar", "baz", "foobar", "foobaz"];

var scale1 = d3.scaleOrdinal()
	.range([0, 100])
	.domain(destinations);
	
var scale2 = d3.scalePoint()
	.range([0, 100])
	.domain(destinations);
	
destinations.forEach(d=>{
console.log(d + " in an ordinal scale: " + scale1(d) + " / in a point scale: " + scale2(d))
})
<script src="https://d3js.org/d3.v4.min.js"></script>

关于你的 y 轴问题:

在调用轴之前设置 y 比例的域。所以,而不是这个:

svg.append("g")
    .attr("class", "y axis").call(yAxis);

y.domain([d3.min(data, function(d){
    return d.PassengersIn;
}), d3.max(data, function(d){
    return d.PassengersIn;
})]);

改变顺序:

y.domain([d3.min(data, function(d){
    return d.PassengersIn;
}), d3.max(data, function(d){
    return d.PassengersIn;
})]);//set the domain first!

svg.append("g")
    .attr("class", "y axis").call(yAxis);

【讨论】:

  • 精彩的解释,我现在了解差异以及为什么我应该使用 scalePoint()。为什么y.domain([d3.min(data, function(d){return d.PassengersIn;}), d3.max(data, function(d){return d.PassengersIn;})]); 不能缩放我的 y 轴?
  • 我必须承认我完全忽略了你的 y 轴问题,只关注序数比例的情况。但是您的问题很容易解决:在调用轴之前设置 y 比例的域。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多