Search code examples
javascriptcolorsd3.jsscale

Multi-colored time-series in D3.js


I have data that looks the following:

series = [{group: 0 ,time: 1, value: 20}, {group:0, time: 2, value: 18},
{group: 1 ,time: 1, value: 10}, {group:1, time: 2, value: 15},
{group: 2 ,time: 1, value: 5}, {group:1, time: 2, value: 10}];

I'm using a scale like so:

var color = d3.scale.category10();

When I'm binding to the data, I reference the stroke style:

svg.selectAll(".line")
    .data(series)
    .enter().append("path")
    .attr("class", "line")
    .attr("d", line)
    .style("stroke", function(d) {return color(d.group)});   <-----------

After rendering, it seems to default to the same color. Inspecting the element yields the stroke color as:

style="stroke: #1f77b4;"

Obviously, I'm not referencing the group value properly, but I'm not sure how to proceed. In the past, I've worked with D3 using force directed networks and have had no problem colorizing according to scales.

Below is the full example:

// data
var series = [
    [{group: 0 ,time: 1, value: 20}, {group:0, time: 2, value: 18}, {group:0, time: 3, value: 15}, {group:0, time: 4, value: 10}, {group:0, time: 5, value: 7}, {group:0, time: 6, value: 4}, {group:0, time: 7, value: 0}, {group:0, time: 8, value: 0}],
    [{group: 1, time: 1, value: 0}, {group:1, time: 2, value: 2}, {group:1, time: 3, value: 5}, {group:1, time: 4, value: 10}, {group:1, time: 5, value: 13}, {group:1, time: 6, value: 16}, {group:1, time: 7, value: 20}, {group:1, time: 8, value: 15}],
    [{group: 2, time: 1, value: 0}, {group:2, time: 2, value: 0}, {group:2, time: 3, value: 0}, {group:2, time: 4, value: 0}, {group:2, time: 5, value: 0}, {group:2, time: 6, value: 0}, {group:2, time: 7, value: 0}, {group:2, time: 8, value: 5}],
    [{group: 3, time: 1, value: 0}, {group:3, time: 2, value: 5}, {group:3, time: 3, value: 5}, {group:3, time: 4, value: 5}, {group:3, time: 5, value: 5}, {group:3, time: 6, value:      6}, {group:3, time: 7, value: 6}, {group:3, time: 8, value: 6}]
];

var n = series[0].length;

var color = d3.scale.category10();

// canvas margins
var margin = {top: 10, right: 10, bottom: 20, left: 40},
    width = 400 - margin.left - margin.right,
    height = 200 - margin.top - margin.bottom;

// x scale
var x = d3.scale.linear()
    .domain([1, n])
    .range([0, width]);

// y scale
var y = d3.scale.linear()
    .domain([-1, 20])
    .range([height, 0]);

// the lines
var line = d3.svg.line()
    .interpolate("basis")
    .x(function(d) { return x(d.time); })
    .y(function(d) { return y(d.value); });

// the svg canvas
var svg = d3.select("body").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 + ")");

// append lines to canvas
svg.selectAll(".line")
    .data(series)
    .enter().append("path")
    .attr("class", "line")
    .attr("d", line)
    .style("stroke", function(d) {return color(d.group)});


//x-axis
svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")
    .call(d3.svg.axis().scale(x).orient("bottom"));

//y-axis
svg.append("g")
    .attr("class", "y axis")
    .call(d3.svg.axis().scale(y).orient("left"));

Solution

  • It seems your data is oddly-shaped in that the group property is actually a part of the child array and repeated for every element. A quick hack would be to just read it from the first element of the array:

    .style("stroke", function(d) { return colorLines(d[0].group) });
    

    But you may want to look at restructuring your data or using d3.nest to reformat it for you.

    As a general debugging approach, even though most d3 examples are using inline functions, there's nothing to stop you from making them multi-line and adding console.log statements inside each one to figure out the shape of the data and why it's not what you expect. For example:

    .style("stroke", function(d) {
      console.log('data passed to color function', d);
      console.log('d.group', d.group);
      return color(d.group)
    }