javascriptd3.jsstacked-chartstackedbarseries

D3JS stacked bar-chart over a timescale


My JSON looks like this (with some extra records that I'm not including for brevity)

[{"name":"Jim","category":"TechSupport","month":"8",year:"2012","Date":"2012-08-01T04:00:00.000Z", "TechSupport":2,"Management":0,"Sales":0},
{"name":"Jim","category":"Management","month":"8",year:"2012","Date":"2012-08-01T04:00:00.000Z", "TechSupport":0,"Management":3,"Sales":0}]

My stack looks like this

var stack = d3.stack().keys(["TechSupport", "Management", "Sales"])
var series = stack(data)

The results of my stack look like [[0,2],[0,0]],[[2,2],[3,3]],[[1,1],[3,3]]

My code for displaying the barchart over the timescale looks like this

var groups = mainChart.selectAll("g")
.data(series)
.append("g")

groups.selectAll("rect")
.attr("id","bar")
.attr("fill", function(d){return colors(d.data.category)})
.attr("x", function(d){return xScale(d.data.Date);}}
.attr("y", function(d){return yScale(d[1]);})
.attr("width", 20)
.attr("height" return yScale(d[0]) - yScale(d[1])})

I can get everything to display along the timeline correctly except when I have items that have the same date, those are not stacking and I'm not sure why?


Solution

  • Preparing dataset - forEach()

    In your forEach function I've changed two small errors.

    Changed Techsupport to TechSupport like I mentioned earlier:

    else if(d.category == 'Techsupport')
    

    and by the cattotal of Management I've added a + to it:

    d.Management = +d.cattotal;
    

    Preparing dataset - reduce()

    You have a JSON with several objects based upon the original dataset. This dataset must be reduced based upon the unique date entries. The object values should be combined. Then you have a good JSON for the stack function.

    This can be achieved in several ways. I use a reduce() function like the one below:

    var dataset = [];
    data.reduce(function(res,obj){
       var key = obj.Date;
       if (res[key]){
           res[key].Sales +=obj.Sales;
           res[key].Techsupport +=obj.Techsupport;
           res[key].Management +=obj.Management;
       } else {
           dataset.push(o);
           res[key] = obj;
       }
       return res;
    }, {});
    

    The dataset contains the well prepared data for the stack function. However, I didn't want to replace data with dataset in your entire code, so I easily updated data:

    data = dataset;
    

    You are free to choose otherwise of course.

    Responsive y-axis:

    The y-axis must be automatically adapted to the stacked bars so I changed your yMax like:

    var yMax = d3.max(series[series.length - 1], function(d) { return d[1]; });
    

    Updating drawing the stacked bars and tooltips

    Finally I updated your stacked bars by adjusting the fill attribute. I changed it for both your main and nav chart.

    .attr("fill", function(d){ return colors(d3.select(this.parentNode).datum().key)})
    

    For the tooltips I've updated the Key and Total values:

    div.html("Date: " +d.data.Date + "<br>" + "Category: " + d3.select(this.parentNode).datum().key + "<br>" + "Total:" + (d[1]-d[0]))
    

    In this fiddle you can find a working example.