Search code examples
d3.jsrollup

Using nest and rollup to create a line chart with a mean in d3 v4


I'm using trying to use nest and rollup to create a line chart in d3 v4 to display an average score over a number of days. I've exhausted all the tutorials and stackoverflow answers and no matter what I try, I can't seem to get the line to display.

I've attached the code below, and would be extremely grateful if anyone could help.

// set the dimensions and margins of the graph
var margin = {top: 20, right: 20, bottom: 30, left: 50},
    width = 960 - margin.left - margin.right,
    height = 400 - margin.top - margin.bottom;

// parse the date / time
var parseTime = d3.timeParse("%d/%m");

// append the svg object to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
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 + ")");

// Get the data
d3.csv("csv/formdata.csv", function(error, data) {
  if (error) throw error;

  // format the data
  data.forEach(function(d) {
      d.date = parseTime(d.date);
      d.scale = +d.scale;
  });

var dataNest = d3.nest()
 .key(function(d) {return d.date;})
 .rollup (function(v) { return {
   averagescale: d3.mean(v, function(d) {return d.scale; })
 }; })
 .entries(data)

 console.log(dataNest)

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

 // define the line
 var valueline = d3.line()
     .x(function(d) { return x(d.date); })
     .y(function(d) { return y(d.averagescale); });

  // Scale the range of the data
  x.domain(d3.extent(data, function(d) { return d.date; }));
  y.domain([0, d3.max(dataNest, function(d) { return d.averagescale; })]);

  // Add the valueline path.
  svg.append("path")
      .data(dataNest)
      .attr("class", "line")
      .attr('d', function(d) { return valueline (d.averagescale); })

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

  // Add the Y Axis
  svg.append("g")
      .call(d3.axisLeft(y));

});

csv looks as below

date,grade,scale
10/05,vs,7
10/05,vs,2
11/05,vs,3
11/05,vs,6
12/05,vs,8
12/05,vs,2
13/05,vs,3
13/05,vs,6


Solution

  • Problem: Since you rolled up.value will hold the averagescale.

    y.domain([0, d3.max(dataNest, function(d) { return d.averagescale; })]);
    

    It should have been:

    y.domain([0, d3.max(dataNest, function(d) { return d.value.averagescale; })]);
    

    Subsequently the line function will also need to be changed:

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

    It should be:

     var valueline = d3.line()
         .x(function(d) { return x(new Date(d.key)); })
         .y(function(d) { return y(d.value.averagescale); });
    

    working code here