Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

D3 line graph with arbitrarily many lines (and a specific data format)

I'm having some trouble with D3 and I'm hitting my wit's end. Essentially I have a time series graph with arbitrarily many lines and the source data can't be modified for convenience before hand (but it can be manipulated client-side).

The data is formatted thusly (with arbitrarily many labels):

object = [
{
  "_id": "2012-08-01T05:00:00",
  "value": {
    "label1": 1.1208746110529344,
    "label2": 0.00977592175310571
  }
},
{
  "_id": "2012-08-15T05:00:00",
  "value": {
    "label1": 0.7218920737863477,
    "label2": 0.6250727456677252
  },
  ....

I've tried something like:

var vis = d3.select.(element)
    .append("svg:svg")
    .attr("width", width)
    .attr("height", height)
    .append("svg:g");

var line = d3.svg.line()
    .x(function(data) {return x(new Date(data._id));})
    .y(function(data) {return y(data.value);});


vis.append("svg:path")
    .attr("d", line(object))
    .attr("stroke", "black");

Which seems unable to access the correct value via the y accessor as I get an "error: problem parsing" and a lot of "NaNL3.384615384615385,NaNL6.76923076923077,NaNL10.153846153846155". However if I hardcode the label value via something like:

.y(function(data) {return y(data.value.label1);});

It works just fine, but only for one line. Could anyone offer help?

like image 582
jshwlkr Avatar asked Aug 22 '12 22:08

jshwlkr


2 Answers

I'd start by transforming the data into parallel arrays of the same format:

var series = ["label1", "label2"].map(function(name) {
  return data.map(function(d) {
    return {date: new Date(d._id), value: d.value[name]};
  });
});

(You can use d3.keys to compute the series names automatically, rather than hard-coding them as above.) Now series is an array of arrays. A single series, such as label1, will look something like this:

{date: new Date(2012, 7, 1, 5, 0, 0), value: 1.1208746110529344},
{date: new Date(2012, 7, 15, 5, 0, 0), value: 0.7218920737863477},
…

Since they have the same format, you can use the same line generator for all series:

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

And likewise the nested array lends itself well to a data-join to create the needed path elements:

svg.selectAll(".line")
    .data(series)
  .enter().append("path")
    .attr("class", "line")
    .attr("d", line);
like image 124
mbostock Avatar answered Nov 09 '22 11:11

mbostock


Try incorporating parseFloat to make absolutely sure that you are converting to floats and not to ints implicitly. If that still doesn't work, try taking substrings of your strings (chop off some precision) until it does work.

like image 25
Chris Morley Avatar answered Nov 09 '22 11:11

Chris Morley