Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

D3: skip null values in line graph with several lines

I have a dynamic array to show a line graph with several lines. Example:

var data = 
[[{x:2005, y:100}, {x:2007, y:96.5}, {x:2009, y:100.3}, {x:2011, y:102.3}], 
 [{x:2005, y:100}, {x:2007, y:105},  {x:2009, y:102},   {x:2011, y:104}]]

This part of my script will draw the lines:

graph.selectAll("path.line")
.data(data)
.enter().append("path")
.attr("class", "line")
.style("stroke", function(d, i) { return d3.rgb(z(i)); })
.style("stroke-width", 2)
.attr("d", d3.svg.line()
.y(function(d) { return y(d.y); })
.x(function(d,i) { return x(i); }));

(The script I'm using is based on http://cgit.drupalcode.org/d3/tree/libraries/d3.linegraph/linegraph.js)

My problem: the data array is dynamic, I don't know beforehand what's in it. Sometimes the y value for 2005 will be null:

var data = 
[[{x:2005, y:100},  {x:2007, y:96.5}, {x:2009, y:100.3}, {x:2011, y:102.3}], 
 [{x:2005, y:null}, {x:2007, y:105},  {x:2009, y:102},   {x:2011, y:104}]]

How can I make the second line ignore the first object, and start at 2007?

Based on answer 1 this is what I have now, still showing the whole line:

data = 
[[{x:2005, y:100},  {x:2007, y:96.5}, {x:2009, y:100.3}, {x:2011, y:102.3}], 
 [{x:2005, y:null}, {x:2007, y:105},  {x:2009, y:102},   {x:2011, y:104}]];

var validatedInput = function(inptArray) { 
 return inptArray.filter(function(obj) {
  return obj.y != null;
 });
};

graph.selectAll("path.line")
    .data(data, validatedInput)
  .enter().append("path")
    .attr("class", "line")
    .style("stroke", function(d, i) { return d3.rgb(z(i)); })
    .style("stroke-width", 2)
    .attr("d", d3.svg.line()
    .y(function(d) { return y(d.y); })
    .x(function(d,i) { return x(i); }));
like image 572
eindgebruiker Avatar asked Jul 20 '14 22:07

eindgebruiker


2 Answers

In the end I solved this myself, based on the solution here. The trick is to remove the empty values as late as possible, so the positions of all values (points) on the canvas are preserved.

graph.selectAll("path.line")
    .data(data)
  .enter().append("path")
    .attr("class", "line")
    .style("stroke", function(d, i) { return d3.rgb(z(i)); })
    .style("stroke-width", 2)
    .attr("d", d3.svg.line()
    .y(function(d) { return y(d.y); })
    .defined(function(d) { return d.y; }) // Omit empty values.
    .x(function(d,i) { return x(i); }));

This will work for empty values at the start and end of a line.

like image 182
eindgebruiker Avatar answered Nov 14 '22 22:11

eindgebruiker


This should do it:

.data(data, function(inptArray) { 
  return inptArray.filter(function(obj) {
   return obj.y != null;
  }) 
});

it would be better though to write it like this:

var validatedInput = function(inptArray) { 
 return inptArray.filter(function(obj) {
  return obj.y != null;
});

.data(data, validatedInput);

Or you can just format your data object before given it to D3:

var data = data.map(function(obj){
 return obj.filter(function(obj) {
  return obj.y != null;
 })
})
like image 23
sergeyz Avatar answered Nov 14 '22 21:11

sergeyz