D3 折线图:x 折线函数始终呈现 NaN

D3 line chart: x line function always renders NaN

本文关键字:NaN 函数 折线图 折线 D3      更新时间:2023-09-26

好的,对于下面的代码,我的x行函数返回NaN。我不知道为什么。我尝试将时间值更改为 1 到 9 并使用线性刻度,我已将它们转换为时间(这只是为了概念证明,因此可以暂时调整值),并尝试了它们"新日期",没有运气。

我做错了什么?为什么 X 总是 NaN?

记录的数据点之一的示例:

x:d 是 : 2015-06-01 x(新日期(d.Period)) 是 NaNy:d 是 65.54347826086956

jQuery(document).ready(function ($) {

  var margin = {top: 20, right: 30, bottom: 40, left: 50},
  width = 300 - margin.left - margin.right,
  height = 150 - margin.top - margin.bottom;
  var x = d3.time.scale()
    .range([0, width]);
  var y = d3.scale.linear()
    .range([height, 0]);
  var xAxis = d3.svg.axis()
    .scale(x)
    .orient('bottom');
  var yAxis = d3.svg.axis()
    .scale(y)
    .orient('left');
  var color = d3.scale.category10();
  var line = d3.svg.line()
    .x(function(d) {console.log('d is : ', d.Period,' x(new Date(d.Period)) is ', x(new Date(d.Period))); return x(new Date(d.Period)); })
    .y(function(d) {console.log('y:d is ', y(d.Value)); return y(d.Value); })
  var svg = d3.select("#pipeline-chart-render")
    .attr('width', width + margin.left + margin.right)
    .attr('height', height + margin.top + margin.bottom)
    .append('g')
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
  svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")
  // This separates the data into the lines we want, although the data is stored
  // In the same original object.
  var keys = color.domain(d3.keys(data[0].values[0]).filter(function(key) { 
    if (key === 'Amount'
     || key === 'Quantity') {
        return key
    }
  }));
  // This returns the data into two separate objects which can be graphed.
  // In this case, Amount and Quantity.
  var datasets = color.domain().map(function(name) {
    return {
      name: name,
      values: data.map(function(d) {
        return {Period: d.values[0].Time, Value: +d.values[0][name]};
      })
    };
  });
  console.log('datasets is: ', datasets);
  // set the minYDomainValue to zero instead of letting it be a lingering magic number.
  var minDomainValue = 0
  // x.domain([
  //   minDomainValue,
  //   d3.max(datasets, function(c) { return d3.max(c.values, function(v) { console.log(v); return v.Time }); })
  // ])
  x.domain(d3.extent(datasets, function(d) { console.log(d); return new Date(d.values[0].Time); }));
  y.domain([
    minDomainValue,
    // d3.min(datasets, function(c) { return d3.min(c.values, function(v) { return v.Time; }); }),
    d3.max(datasets, function(c) { return d3.max(c.values, function(v) { return v.Value; }); })
  ])
  // Append the x-axis class and move axis around.
  svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")
    .call(xAxis)
  // Append the y-axis class.
  svg.append("g")
    .attr("class", "y axis")
    .call(yAxis)
  var dataset = svg.selectAll('.pipeline')
    .data(datasets);
  console.log(dataset);
  dataset.enter()
    .append('g')
    .attr('class', 'pipeline');
  dataset.append('path')
    .attr('class', 'line')
    .attr('d', function(d) { return line(d.values); })
    .attr("data-legend",function(d) { return d.name})
    .style("stroke", function(d) { return color(d.name); })
  dataset.exit().remove()
});
var data = [
  {
    key: 1,
    values: [
      {
        Amount: 33,
        Quantity: 22,
        Time: '2015-01-01'
      }
    ]
  },
  {
    key: 2,
    values: [
      {
        Amount: 52,
        Quantity: 20,
        Time: '2015-02-01'
      }
    ]
  },
  {
    key: 3,
    values: [
      {
        Amount: 63,
        Quantity: 30,
        Time: '2015-03-01'
      }
    ]
  },
  {
    key: 4,
    values: [
      {
        Amount: 92,
        Quantity: 60,
        Time: '2015-04-01'
      }
    ]
  },
  {
    key: 5,
    values: [
      {
        Amount: 50,
        Quantity: 29,
        Time: '2015-05-01'
      }
    ]
  },
  {
    key: 6,
    values: [
      {
        Amount: 53,
        Quantity: 25,
        Time: '2015-06-01'
      }
    ]
  },
  {
    key: 7,
    values: [
      {
        Amount: 46,
        Quantity: 12,
        Time: '2015-07-01'
      }
    ]
  },
  {
    key: 8,
    values: [
      {
        Amount: 52,
        Quantity: 15,
        Time: '2015-08-01'
      }
    ]
  },
  {
    key: 9,
    values: [
      {
        Amount: 55,
        Quantity: 20,
        Time: '2015-09-01'
      }
    ]
  }
]
// var formatTime = function(date) {
//   var formatter = d3.time.format("%Y-%m").parse;
//   return formatter(date);
// }

要回答您的具体问题,您没有正确设置 x 域,我建议您这样做:

var minDate = d3.min(datasets, function(d0){
  return d3.min(d0.values, function(d1){
    return d1.Period;
  })
}),
maxDate = d3.max(datasets, function(d0){
  return d3.max(d0.values, function(d1){
    return d1.Period;
  })
});
x.domain([minDate, maxDate]);

为此,请采纳我的下一个建议,停止new Date(疯狂,从一开始就强迫你的时间约会。 我强烈建议您使用d3.time.format,而不是尝试自己进行转换:

var tP = d3.time.format("%Y-%m-%d");
// This returns the data into two separate objects which can be graphed.
// In this case, Amount and Quantity.
var datasets = color.domain().map(function(name) {
  return {
    name: name,
    values: data.map(function(d) {
      return {
        Period: tP.parse(d.values[0].Time), //<-- just convert once!
        Value: +d.values[0][name]
      };
    })
  };
});

然后,您的行函数简化为:

var line = d3.svg.line()
  .x(function(d) {
    return x(d.Period);
  })
  .y(function(d) {
    return y(d.Value);
  });

完整代码:

<!DOCTYPE html>
<html>
<head>
  <script data-require="d3@3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
</head>
<body>
  <svg id="pipeline-chart-render"></div>
  <script>
    var data = [{
      key: 1,
      values: [{
        Amount: 33,
        Quantity: 22,
        Time: '2015-01-01'
      }]
    }, {
      key: 2,
      values: [{
        Amount: 52,
        Quantity: 20,
        Time: '2015-02-01'
      }]
    }, {
      key: 3,
      values: [{
        Amount: 63,
        Quantity: 30,
        Time: '2015-03-01'
      }]
    }, {
      key: 4,
      values: [{
        Amount: 92,
        Quantity: 60,
        Time: '2015-04-01'
      }]
    }, {
      key: 5,
      values: [{
        Amount: 50,
        Quantity: 29,
        Time: '2015-05-01'
      }]
    }, {
      key: 6,
      values: [{
        Amount: 53,
        Quantity: 25,
        Time: '2015-06-01'
      }]
    }, {
      key: 7,
      values: [{
        Amount: 46,
        Quantity: 12,
        Time: '2015-07-01'
      }]
    }, {
      key: 8,
      values: [{
        Amount: 52,
        Quantity: 15,
        Time: '2015-08-01'
      }]
    }, {
      key: 9,
      values: [{
        Amount: 55,
        Quantity: 20,
        Time: '2015-09-01'
      }]
    }]
    var margin = {
        top: 20,
        right: 30,
        bottom: 40,
        left: 50
      },
      width = 300 - margin.left - margin.right,
      height = 150 - margin.top - margin.bottom;
      
    var tP = d3.time.format("%Y-%m-%d");
    var x = d3.time.scale()
      .range([0, width]);
    var y = d3.scale.linear()
      .range([height, 0]);
    var xAxis = d3.svg.axis()
      .scale(x)
      .orient('bottom');
    var yAxis = d3.svg.axis()
      .scale(y)
      .orient('left');
    var color = d3.scale.category10();
    var line = d3.svg.line()
      .x(function(d) {
        
        console.log('d is : ', d.Period, ' x(new Date(d.Period)) is ', x(d.Period));
        return x(d.Period);
      })
      .y(function(d) {
        console.log('y:d is ', y(d.Value));
        return y(d.Value);
      })
    var svg = d3.select("#pipeline-chart-render")
      .attr('width', width + margin.left + margin.right)
      .attr('height', height + margin.top + margin.bottom)
      .append('g')
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    svg.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
    // This separates the data into the lines we want, although the data is stored
    // In the same original object.
    var keys = color.domain(d3.keys(data[0].values[0]).filter(function(key) {
      if (key === 'Amount' || key === 'Quantity') {
        return key
      }
    }));
    // This returns the data into two separate objects which can be graphed.
    // In this case, Amount and Quantity.
    var datasets = color.domain().map(function(name) {
      return {
        name: name,
        values: data.map(function(d) {
          return {
            Period: tP.parse(d.values[0].Time),
            Value: +d.values[0][name]
          };
        })
      };
    });
    // set the minYDomainValue to zero instead of letting it be a lingering magic number.
    var minDomainValue = 0;
    var minDate = d3.min(datasets, function(d0){
      return d3.min(d0.values, function(d1){
        return d1.Period;
      })
    }),
    maxDate = d3.max(datasets, function(d0){
      return d3.max(d0.values, function(d1){
        return d1.Period;
      })
    });
    x.domain([minDate, maxDate]);
    y.domain([
      minDomainValue,
      // d3.min(datasets, function(c) { return d3.min(c.values, function(v) { return v.Time; }); }),
      d3.max(datasets, function(c) {
        return d3.max(c.values, function(v) {
          return v.Value;
        });
      })
    ])
    // Append the x-axis class and move axis around.
    svg.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis)
    // Append the y-axis class.
    svg.append("g")
      .attr("class", "y axis")
      .call(yAxis)
    var dataset = svg.selectAll('.pipeline')
      .data(datasets);
    console.log(dataset);
    dataset.enter()
      .append('g')
      .attr('class', 'pipeline');
    dataset.append('path')
      .attr('class', 'line')
      .attr('d', function(d) {
        return line(d.values);
      })
      .attr("data-legend", function(d) {
        return d.name
      })
      .style("stroke", function(d) {
        return color(d.name);
      })
    dataset.exit().remove()
  </script>
</body>
</html>