Jason Heppler
Jason Heppler

Reputation: 716

Am I mapping or nesting this data incorrectly?

I'm looking for some pointers on how I can fix how my data is being visualized as a bar chart. I've created a small multiples visualization of data that appears over time. Because of the nature of the way the data was collected, the data shares certain attributes. By way of example, you can see an excerpt of the .csv below where group and date are repeated:

group,date,totals
acid,1641,8438
acid,1641,0
acid,1641,0
beef,1641,977.85
beef,1641,0
beef,1641,0
beef,1641,164
beef,1641,5.25
bird,1641,121
bird,1641,0
bird,1641,12
bird,1641,1558
bird,1641,729
bird,1641,1680
bird,1641,0
bird,1641,343
bird,1641,3
bird,1641,2092
bird,1641,0
bird,1641,0
bird,1641,107
bird,1641,2679
bird,1641,167
bird,1641,649
boar,1641,41
boar,1641,13
cheese,1641,13
cheese,1641,22
cheese,1641,1071
cheese,1641,17195

(You can see the entire dataset here.)

The problem I'm running in to is wherever a date is shared, instead of summing the totals column it stacks the data on top of one another. For example, see:

Bar charts

What I'm struggling with is having the data displayed as a single bar chart. (I have a secondary problem with the y-axis not scaling correctly). I've tried to use nest to fix the problem of the years (this worked for groups) but I can't seem to get it with date.

Here's my code, thus far:

var margin = {top: 20, right: 30, bottom: 30, left: 50},
    width = 400 - margin.right - margin.left,
    height = 150 - margin.top - margin.bottom,
    parse = d3.time.format("%Y").parse;

// scales
var x = d3.time.scale().range([0, width]),
    y = d3.scale.linear().range([0, height]),
    yscaled = d3.scale.linear().range([height, 0]);

// load data
d3.csv("revised.csv", function(error, data) {

  // nest values by group
  var groups = d3.nest()
      .key(function(d) { return d.group; })
      .entries(data);

  // parse dates and numbers, assume values are sorted by date
  // compute the maximum totals per group
  groups.forEach(function(s) {
    s.values.forEach(function(d) { d.date = parse(d.date); d.totals = +d.totals; });
    s.maxtotals = d3.max(s.values, function(d) { return d.totals; });
  });

  // compute the minimum and maximum date across groups
  x.domain([
    d3.min(groups, function(s) { return s.values[0].date; }),
    d3.max(groups, function(s) { return s.values[s.values.length - 1].date; })
  ]);
  // y.domain([0, d3.max(data, function(d) { return d.totals; })]);

  // add an SVG element for each group
  var svg = d3.select("body").selectAll("g")
      .data(groups)
    .enter().append("svg")
      .attr("width", width + margin.left + margin.right)
      .attr("height", height + margin.top + margin.bottom);

  var canvas_g = svg.append("g")
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
      .each(function(d) {
          var g = d3.select(this);
          g.append("g");
        // .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

        y.domain([0, d3.max(d.values, function(r) { return r.totals; })]);
        yscaled.domain([0, d3.max(d.values, function(r) { return r.totals; })]);

      // Draw the charts.

        g.selectAll("rect.aevents")
          .data(d.values)
        .enter().append("rect")
          .attr("height", function(p) {return y(p.totals)})
          .attr("width", 5)
          .attr("x", function(p, q) {return x(p.date)})
          .attr("y", function(p) {return height - y(p.totals)})
          // .on("click", function(p) {console.log(p.date)})
          .attr("class", "bar");
      });

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

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

  // add a small label for the group name
  svg.append("text")
      .attr("x", width + 40)
      .attr("y", height + 15)
      .attr("text-anchor", "end")
      .text(function(d) { return d.key; });

});

Is there a different way I should loop through the data? Or, do I need to restructure the data somehow? Thank you in advance!

Upvotes: 1

Views: 4508

Answers (1)

Christopher Chiche
Christopher Chiche

Reputation: 15335

First you can read this answer that will help you if you want to know more about d3.nest().

For your problem, a good method can be to first nest the elements using d3.nest() and then to map the groups to their desired form. Which gives:

function formatData(inputData){ 
    var array = d3.nest()
        .key(function(d){return d.group})
        .key(function(d){return d.date})
        .entries(inputData)
        .map(function(d){
            var group = d.key
            var values = d.values.map(function(dd){
                var date = dd.key
                var total = 0
                dd.values.forEach(function(ddd){
                    total = total + ddd.totals        
                })
                return {date:date, totals:total}
            })
            return {'group':group, 'values':values}
        })
    obj = {}
    array.forEach(function(d){
        obj[d.group] = d.values
    })
    return obj
}

with inputData being the data loaded using d3.csv(). This way you can get the best of nest() and `map()``

If you want to draw the bar chart only for beef you can now do:

var beefData = formatData(myInput).beef

jsFiddle: http://jsfiddle.net/chrisJamesC/FJmMD/3/

Upvotes: 3

Related Questions