0

I am using d3 in an Angular 5 app to graph a line, and I can't seem to get my numbers right. I'm getting this error:

Error: <path> attribute d: Expected number, "MNaN,25.384615384…".

I assume it's either something to do with how I'm parsing my dates, or date type, or the domain on my axis?

The d3 code in question looks like:

var x = d3.scaleLinear()
  .range([0, width - 100])
  .domain(<[Date, Date]>d3.extent(this.temp_data, function (d) { return parse(d['date']); }));

var xAxis = d3.axisBottom(x)
  .scale(x)
  .ticks((width + 2) / (height + 2))
  .tickSize(-height)
  .tickPadding(10)
  .tickFormat(d3.timeFormat("%b %d, %H:%M:%S"))

var parseTime = d3.timeParse("%Y-%m-%dT%H:%M:%S.%LZ");

var line = d3.line()
  .x(function (d) { return x(parseTime(d['date'])); })
  .y(function (d) { return y(d['temperature']); });

My data looks like in UTC format:

[{"temperature": "11.0", "date": "2018-10-10 20:36:27 UTC" }.
 {"temperature": "11.2", "date": "2018-10-10 20:34:27 UTC" },
 {"temperature": "10.9", "date": "2018-10-10 20:32:27 UTC" },
 {"temperature": "11.3", "date": "2018-10-10 20:30:27 UTC" },
 {"temperature": "11.0", "date": "2018-10-10 20:28:27 UTC" }]

Where might that error be coming from? Thanks! Any help or thoughts are greatly appreciated!

EDIT:

I know this is slightly different code but I am having the same problem on this codepen HERE.

thanks so much! I've been starting at it too long now and am starting to go in circles

Devstar34
  • 1,027
  • 2
  • 21
  • 47
  • Sorry yes, that was just a copy/paste error! – Devstar34 Oct 23 '18 at 05:38
  • In `.domain(<[Date, Date]>d3.extent(this.temp_data, function (d) { return parse(d['date'])`, what is `parse` doing? – i alarmed alien Oct 23 '18 at 05:47
  • @ialarmedalien I thought I would need it to make the dates readable by the axis, perhaps it's overkill? – Devstar34 Oct 23 '18 at 06:01
  • I just want to make sure that the x axis is setting up a valid domain—if it isn't, it could be what's causing the NaN values. To confirm, the NaN is from the `path` element that makes up the line - correct? – i alarmed alien Oct 23 '18 at 06:08
  • @ialarmedalien I can only assume so. Either that or an incorrect x axis. Because the graph will appear complete, except for the line and the labels on the x axis. – Devstar34 Oct 23 '18 at 06:15
  • Can you create a minimal, complete working example for myself and others to take a look at? Also -- try using `parseTime` when you're setting the domain of the x axis to ensure it isn't that the x axis domain is invalid. – i alarmed alien Oct 23 '18 at 06:55
  • codepen created! It's in the post edit. It's the same problem just slightly different code. Thanks in advance for your help. – Devstar34 Oct 23 '18 at 07:13
  • 1
    Your time parser is not working correctly -- if you log `temp_data`, you'll see the dates are all invalid. – i alarmed alien Oct 23 '18 at 07:29
  • Ah I see, am I not using proper formatting for UTC? I can't find the right alternative – Devstar34 Oct 23 '18 at 07:36
  • the first one also needs to be `timeParse`, not `timeFormat` – i alarmed alien Oct 23 '18 at 07:37

1 Answers1

1
  1. The timeParse assigned to the variable parseTimeUtc is the correct one considering the format of the dates in your data.
  2. Once you parse the dates in the forEach loop, you don't need to parse them again while setting the xAxis or within the line generator function.

    temp_data.forEach(function (d) {
      d.date = parseTime_utc(d.date)
      d.temperature = +d.temperature;
    });
    
  3. All the dates are the same and so I've made slight changes (months) to make the line visible.

  4. Added the following style to the line: fill:none; stroke: steelblue; to make the line visible.

  5. You had a SVG appended within a SVG (copy/paste error I suppose). Anyway, changed #watertemp_graph to a <div></div>.

Snippet:

var temp_data = [
{"temperature": "11.0", "date": "2018-08-22T14:53:37.267Z" },
 {"temperature": "11.2", "date": "2018-07-22T14:53:37.267Z" },
 {"temperature": "10.9", "date": "2018-08-22T14:53:37.267Z" },
 {"temperature": "11.3", "date": "2018-05-22T14:53:37.267Z" },
 {"temperature": "11.0", "date": "2018-08-22T14:53:37.267Z" }]

var parseDate = d3.timeParse("%Y-%m-%d %X"); //27-May-12 16:00:00. This is used for D3JS parsing
var formatTime = d3.timeFormat("%Y-%m-%d %X");
 var parseTime_utc = d3.timeParse("%Y-%m-%dT%H:%M:%S.%LZ");
var formatDate_utc = d3.timeFormat("%b %d, %Y %H:%M:%S");
// var parse = parseDate(moment.utc(d.date).format("YYYY-MM-DD HH:mm:ss"))

temp_data.forEach(function (d) {
        d.date = parseTime_utc(d.date)
        d.temperature = +d.temperature;
});

var margin = { top: 30, right: 10, bottom: 50, left: 70 },
      width = 600 - margin.left - margin.right,
      height = 300 - margin.top - margin.bottom;

    // Parse the date / time
    

    // Set the ranges
    var x = d3.scaleTime().range([0, width]);
    var y = d3.scaleLinear().range([height, 0]);

    // Define the axes
    var xAxis = d3.axisBottom(x)
        .ticks(5);

    var yAxis = d3.axisLeft(y)
      .ticks(5);

    // Define the line
    var valueline = d3.line()
      .x(function (d) { return x(d['date']); })
      .y(function (d) { return y(d['temperature']); });


    // Adds the svg canvas
    var svg = d3.select("#" + "watertemp_graph")
      .append("svg")
      .attr("width", width + margin.left + margin.right)
      .attr("height", height + margin.top + margin.bottom)
      .append("g")
      .attr("transform",
        "translate(" + margin.left + "," + margin.top + ")");



    // Scale the range of the data
    x.domain(d3.extent(temp_data, function (d) { return d.date; }));
    y.domain(d3.extent(temp_data, function (d) { return d['temperature']; }));

    // Add the valueline path.
    svg.append("path")
      .attr("class", "line")
      .attr("d", valueline(temp_data)).style('fill', 'none').style('stroke', 'steelblue');


    // Add the X Axis
    svg.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis);

    // Add the Y Axis
    svg.append("g")
      .attr("class", "y axis")
      .call(yAxis);
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="watertemp_graph">
 
  </div>

Updated codepen: https://codepen.io/anon/pen/bmmbzw

Also, if you could let us know why you have the other timeParsers and timeFormats, maybe I can help you with using those as well.

Hope this helps.

UPDATE: Need of a flexible parser?

As stated in the docs by Mike: If a more flexible parser is desired, try multiple formats sequentially until one returns non-null and my suggestion would be to be to further validate the non-null value (date) by following this answer here.

Shashank
  • 5,570
  • 1
  • 11
  • 17
  • Thank you so much! This helps. My last question is what I might do differently if my dates are formatted like "2018-10-10 20:28:27 UTC" rather than what's on the codepen. Doing something like 'd3.timeParse("%Y-%m-%dT%H:%M:%S.%LZ")' will make them null. (that's why I have the other parsing/format variables, I'm not sure which I'd need for each date format) – Devstar34 Oct 23 '18 at 15:29
  • Sure. Updated the post on how to get/write a flexible parser. – Shashank Oct 23 '18 at 16:08
  • Great thank you so much! I didn't realize a guess-and-check type method was rather conventional :) – Devstar34 Oct 23 '18 at 16:45
  • You're welcome. Yes it is. I use that a lot too to add flexibility. – Shashank Oct 23 '18 at 17:30