d3 error displaying datetime on x-axis line chart

浪尽此生 提交于 2021-02-08 07:17:58

问题


I am implementing a multi-line series chart using d3.js and I am getting an error pointing to my x-axis when trying to plot my dateTime from the data coming in. "Error: attribute d: Expected number, "MNaN,376.88020650…"."

Here is my function

var data =   [{
"Brand": "Toyota", 
"Count": 1800, 
"Time": "2017-04-02 16"},
{
"Brand": "Toyota",
"Count": 1172,
"Time": "2017-04-02 17"},
{
"Brand": "Toyota",
"Count": 2000,
"Time": "2017-04-02 18"},
{
"Brand": "Honda",
"Count": 8765,
"Time": "2017-04-02 16"},
{
"Brand": "Honda",
"Count": 3445,
"Time": "2017-04-02 17"},
{
"Brand": "Honda",
"Count": 1232,
"Time": "2017-04-02 18"}
]

 var dataGroup = d3.nest()    //d3 method that groups data by Brand  
                    .key(function(d) {return d.Brand;})
                    .entries(data);
                console.log(JSON.stringify(dataGroup));

//var color = d3.scale.category10();
                var vis = d3.select("#visualisation"),
                    WIDTH = 1000,
                    HEIGHT = 500,
                    MARGINS = {
                        top: 50,
                        right: 20,
                        bottom: 50,
                        left: 50
                    },
        xScale = d3.scaleLinear().range([MARGINS.left, WIDTH - MARGINS.right]).domain([d3.min(data, function(d) {   //set up x-axis based on data
                        return d.Time;
                    }), d3.max(data, function(d) {
                        return d.Time;
                    })]),

        yScale = d3.scaleLinear().range([HEIGHT - MARGINS.top, MARGINS.bottom]).domain([d3.min(data, function(d) {    //set up y-axis based on data
                        return d.Count;
                    }), d3.max(data, function(d) {
                        return d.Count;
                    })]),

                    xAxis = d3.axisBottom()
                        .scale(xScale),
                    yAxis = d3.axisLeft()
                    .scale(yScale)

                    vis.append("svg:g")
                    .attr("class", "x axis")
                    .attr("transform", "translate(0," + (HEIGHT - MARGINS.bottom) + ")")
                    .call(xAxis);

                vis.append("svg:g")
                    .attr("class", "y axis")
                    .attr("transform", "translate(" + (MARGINS.left) + ",0)")
                    .call(yAxis);

                var lineGen = d3.line()
                    .x(function(d) {
                        return xScale(d.Time);
                    })
                    .y(function(d) {
                        return yScale(d.Count);
                    })
                    .curve(d3.curveBasis);

                dataGroup.forEach(function(d,i) {   //iterate over the dataGroup and create line graph for each brand
                    vis.append('svg:path')
                    .attr('d', lineGen(d.values))
                    .attr('stroke', function(d,j) { 
                            return "hsl(" + Math.random() * 360 + ",100%,50%)";     //random color for each brand line on graph
                    })
                    .attr('stroke-width', 2)
                    .attr('id', 'line_'+d.key)
                    .attr('fill', 'none');

                    lSpace = WIDTH/dataGroup.length;   //define the legend space based on number of brands
                    vis.append("text")
                        .attr("x", (lSpace/2)+i*lSpace)
                        .attr("y", HEIGHT)
                        .style("fill", "black")
                        .attr("class","legend")
                        .on('click',function(){
                            var active   = d.active ? false : true;
                            var opacity = active ? 0 : 1;
                            d3.select("#line_" + d.key).style("opacity", opacity);
                            d.active = active;
                        })
                        .text(d.key);
                });

My dates are in yyyy-mm-dd HH format and what I am trying to accomplish is this for example:

  • "Time": "2017-04-02 16" converted to 'April 02' on the x axis and have the hour (HH) just displayed as a tool tip...etc

Here is a jsfiddle link https://jsfiddle.net/rsov2s2s/ Any help is appreciated.


回答1:


In your data objects, Time is only a string. Thus, you`ll have to parse it into an actual date:

data.forEach(function(d){
    d.Time = d3.timeParse("%Y-%m-%d %H")(d.Time)
});

In this function, d3.timeParse uses "%Y-%m-%d %H" as a specifier, which matches the structure of your strings.

After that, don't forget to change the xScale from scaleLinear to scaleTime.

Here is your code with those changes only:

var data = [{
  "Brand": "Toyota",
  "Count": 1800,
  "Time": "2017-04-02 16"
}, {
  "Brand": "Toyota",
  "Count": 1172,
  "Time": "2017-04-02 17"
}, {
  "Brand": "Toyota",
  "Count": 2000,
  "Time": "2017-04-02 18"
}, {
  "Brand": "Honda",
  "Count": 8765,
  "Time": "2017-04-02 16"
}, {
  "Brand": "Honda",
  "Count": 3445,
  "Time": "2017-04-02 17"
}, {
  "Brand": "Honda",
  "Count": 1232,
  "Time": "2017-04-02 18"
}];

data.forEach(function(d) {
  d.Time = d3.timeParse("%Y-%m-%d %H")(d.Time)
});

var dataGroup = d3.nest() //d3 method that groups data by Brand  
  .key(function(d) {
    return d.Brand;
  })
  .entries(data);

//var color = d3.scale.category10();
var vis = d3.select("#visualisation"),
  WIDTH = 1000,
  HEIGHT = 500,
  MARGINS = {
    top: 50,
    right: 20,
    bottom: 50,
    left: 50
  },
  xScale = d3.scaleTime().range([MARGINS.left, WIDTH - MARGINS.right]).domain([d3.min(data, function(d) { //set up x-axis based on data
    return d.Time;
  }), d3.max(data, function(d) {
    return d.Time;
  })]),

  yScale = d3.scaleLinear().range([HEIGHT - MARGINS.top, MARGINS.bottom]).domain([d3.min(data, function(d) { //set up y-axis based on data
    return d.Count;
  }), d3.max(data, function(d) {
    return d.Count;
  })]),

  xAxis = d3.axisBottom()
  .scale(xScale),
  yAxis = d3.axisLeft()
  .scale(yScale)

vis.append("svg:g")
  .attr("class", "x axis")
  .attr("transform", "translate(0," + (HEIGHT - MARGINS.bottom) + ")")
  .call(xAxis);

vis.append("svg:g")
  .attr("class", "y axis")
  .attr("transform", "translate(" + (MARGINS.left) + ",0)")
  .call(yAxis);

var lineGen = d3.line()
  .x(function(d) {
    return xScale(d.Time);
  })
  .y(function(d) {
    return yScale(d.Count);
  })
  .curve(d3.curveBasis);

dataGroup.forEach(function(d, i) { //iterate over the dataGroup and create line graph for each brand
  vis.append('svg:path')
    .attr('d', lineGen(d.values))
    .attr('stroke', function(d, j) {
      return "hsl(" + Math.random() * 360 + ",100%,50%)"; //random color for each brand line on graph
    })
    .attr('stroke-width', 2)
    .attr('id', 'line_' + d.key)
    .attr('fill', 'none');

  lSpace = WIDTH / dataGroup.length; //define the legend space based on number of brands
  vis.append("text")
    .attr("x", (lSpace / 2) + i * lSpace)
    .attr("y", HEIGHT)
    .style("fill", "black")
    .attr("class", "legend")
    .on('click', function() {
      var active = d.active ? false : true;
      var opacity = active ? 0 : 1;
      d3.select("#line_" + d.key).style("opacity", opacity);
      d.active = active;
    })
    .text(d.key);
});
   .axis path {
   fill: none;
   stroke: #777;
   shape-rendering: crispEdges;
 }
 
 .axis text {
   font-family: Lato;
   font-size: 13px;
 }
 
 .legend {
   font-size: 14px;
   font-weight: bold;
   cursor: pointer;
<title>D3 Test</title>
<script src="https://d3js.org/d3.v4.js"></script>

<body>
  <svg id="visualisation" width="1000" height="600"></svg>
  <script src="InitChart.js"></script>
</body>


来源:https://stackoverflow.com/questions/43772059/d3-error-displaying-datetime-on-x-axis-line-chart

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!