Akshay Shinde
Akshay Shinde

Reputation: 141

Sum of all the stacks of stacked bar chart in d3

I have a stacked bar chart, which has labels in the every stack. Now I wanted to have another label at top of each bar in the graph. I am not able to place a label at the top of the bar, and also I am getting sum of all the values in the chart, where as I want sum for each bar. Here is my code,

var groups = svg.selectAll("g.cost")
  .data(dataset.reverse())
  .enter().append("g")
  .attr("class", "cost")
  .style("fill", function (d, i) { return colors[i]; });
var sum = [0];

var svg = d3.select("svg");
var bar = groups.selectAll("g")
   .data(function (d) { return d; })
.enter().append("g")
  .attr("transform", function (d, i) { return "translate(0," + i * y(d.y0) - y(d.y0 + d.y) + ")"; });


bar.append("rect")
 .attr("x", function (d) { return x(d.x); })
 .attr("y", function (d) { return y(d.y0 + d.y); })
 .attr("height", function (d) { return y(d.y0) - y(d.y0 + d.y); })
 .attr("width", x.rangeBand())


bar.append("text")
    .attr("x", function (d) { return x(d.x); })
  .attr("y", function (d) { return y(d.y0 + d.y); })
    .attr("dy", ".35em")
    .attr('style', 'font-size:13px')
    .text(function (d) { if (d.y != 0) { sum += d.y; return "$" + d.y; } })
    .style('fill', 'black');

bar.append("text")
   .attr("x", function (d) { return x(d.x); })
 .attr("y", function (d) { return y(d.y0 + d.y); })
   .attr("dy", ".35em")
   .attr('style', 'font-size:13px')
   .text( sum)
   .style('fill', 'black'); 

jsfiddle: http://fiddle.jshell.net/1fsm8cst/3/

Upvotes: 3

Views: 5316

Answers (2)

Cool Blue
Cool Blue

Reputation: 6476

Summarising vertically is not natural with the stack layout since it groups in layers, but d3 has plenty of functionality to help. In this case, d3.nest, d3.sum and d3.values (the really nice thing about d3.sum is that it ignores NaN values). With these you can summarise the data by month and then you can use the same coordinate system and scales to position the summarised elements.

var margin = {top: 20, right: 300, bottom: 35, left: 50};

  var width = 760 - margin.left - margin.right,
    height = 400 - margin.top - margin.bottom;



  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 + ")");



  /* Data in strings like it would be if imported from a csv */

  var data = [
    { month: "Jan", MobileCoupon: "430000", Bonus: "240000", Promotions: "200000", Merchandise: "150000" },
    { month: "Feb", MobileCoupon: "250000", Bonus: "440000", Promotions: "200000", Merchandise: "150000" },
    { month: "Mar", MobileCoupon: "350000", Bonus: "180000", Promotions: "200000", Merchandise: "150000" },
  ];

  var parse = d3.time.format("%b").parse;


  // Transpose the data into layers
  var dataset = d3.layout.stack()(["MobileCoupon", "Bonus", "Promotions", "Merchandise"].map(function(fruit) {
    return data.map(function(d) {
      return {x: parse(d.month), y: +d[fruit]};
    });
  }));

  var months = d3.nest()
    .key(function(d){return parse(d.month)})
    .rollup(function(leaves){
      return d3.sum(leaves, function(d) {return d3.sum(d3.values(d))});
    })
    .entries(data);

  // Set x, y and colors
  var x = d3.scale.ordinal()
    .domain(dataset[0].map(function(d) { return d.x; }))
    .rangeRoundBands([10, width-10], 0.35);

  var y = d3.scale.linear()
    .domain([0, d3.max(dataset, function(d) {  return d3.max(d, function(d) { return d.y0 + d.y; });  })])
    .range([height, 0]);

  var colors = ["#3D0000", "#d25c4d", "#f2b447", "#d9d574"];

  // Define and draw axes
  var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left")

    .ticks(5)
    .tickSize(-width, 0, 0)
    .tickFormat( d3.format("$,s") );

  var xAxis = d3.svg.axis()
    .scale(x)
    .orient("bottom")
    .tickFormat(d3.time.format("%b"));

  svg.append("g")
    .attr("class", "y axis")
    .attr("transform", "translate(0,0)")
    .call(yAxis);


  svg.append("g")
    .call(xAxis)
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")
    .call(xAxis);

  // Create groups for each series, rects for each segment
  var groups = svg.selectAll("g.cost")
    .data(dataset)
    .enter().append("g")
    .attr("class", "cost")
    .style("fill", function(d, i) { return colors[i]; });

//  var svg = d3.select("svg");
  var bar = groups.selectAll("g")
    .data(function(d) { return d; })
    .enter().append("g")
    .attr("transform", function(d, i) {
      return "translate(" + x(d.x) + ", 0)";
    });

  var sum=0;
  bar.append("rect")
    .attr("y", function(d) { return y(d.y0 + d.y); })
    .attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); })
    .attr("width", x.rangeBand())

  bar.append("text")
    .attr("x", -6)
    .attr("y", function(d) { return y(d.y0 + d.y); })
    .attr("dy", ".35em")
    .text(function(d) {sum+=d.y; return d3.format("$,s")(d.y); });

  columns = svg.append("g")
    .selectAll("text").data(months)
    .enter().append("text")
    .attr("x", function(d){
      return x(d.key) + x.rangeBand()/2
    })
    .attr("y", function (d) {
      return y(d.values);
    })
    .attr("dy", "1.35em")
    .attr('style', 'font-size:13px')
    .text( function (d){
      return d3.format("$,s")(d.values);
    })
    .style({fill: 'black', "text-anchor": "middle"});

  //  svg.call(tip);

  // Draw legend
  var legend = svg.selectAll(".legend")
    .data(colors)
    .enter().append("g")
    .attr("class", "legend")
    .attr("transform", function(d, m) { return "translate(90," + (m+5) * 20 + ")"; });

  legend.append("rect")
    .attr("x", width - 18)
    .attr("width", 18)
    .attr("height", 18)
    .style("fill", function(d, i) {return colors.slice().reverse()[i];});

  legend.append("text")
    .attr("x", width + 5)
    .attr("y", 9)
    .attr("dy", ".35em")
    .style("text-anchor", "start")
    .text(function(d, i) {
      switch (i) {
        case 0: return "Mobile Coupon";
        case 1: return "Bonus";
        case 2: return "Promotions";
        case 3: return "Merchandise";
      }
    });
svg {
      font: 10px sans-serif;
      shape-rendering: crispEdges;
    }

    .axis path,
    .axis line {
      fill: none;
      stroke: black;
      shape-rendering: crispEdges;
    }

    path.domain {
      stroke: none;
    }

    .y .tick line {
      stroke: #ddd;}


    text {
      font: 10px sans-serif;
      text-anchor: end;
    }



    .d3-tip {
      line-height: 1;
      font-weight: bold;
      padding: 12px;
      background: rgba(0, 0, 0, 0.8);
      color: #fff;
      border-radius: 2px;
    }

    /* Creates a small triangle extender for the tooltip */
    .d3-tip:after {
      box-sizing: border-box;
      display: inline;
      font-size: 10px;
      width: 100%;
      line-height: 1;
      color: rgba(0, 0, 0, 0.8);
      content: "\25BC";
      position: absolute;
      text-align: center;
    }

    /* Style northward tooltips differently */
    .d3-tip.n:after {
      margin: -1px 0 0 0;
      top: 100%;
      left: 0;
    }
    .legend
    {
      position: relative;
      top: -401px;
      left: 380px;
    }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

Upvotes: 3

Sandeepedara
Sandeepedara

Reputation: 1

I am currently a beginner in Javascript and D3. Hope my solution serves the purpose : http://jsfiddle.net/sandeepedara/n3ew5sqq/

var margin = {top: 20, right: 300, bottom: 35, left: 50};

var width = 760 - margin.left - margin.right,
    height = 400 - margin.top - margin.bottom;



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 + ")");



/* Data in strings like it would be if imported from a csv */

var data = [
  { month: "Jan", MobileCoupon: "430000", Bonus: "240000", Promotions: "200000", Merchandise: "150000" },
  { month: "Feb", MobileCoupon: "250000", Bonus: "440000", Promotions: "200000", Merchandise: "150000" },
  { month: "Mar", MobileCoupon: "350000", Bonus: "180000", Promotions: "200000", Merchandise: "150000" },
];

for (var key in data) {
var sum=0;
   if (data.hasOwnProperty(key)) {
       var obj = data[key];

        for (var prop in obj) {

          // important check that this is objects own property 
          // not from prototype prop inherited
          if(obj.hasOwnProperty(prop)){ 
                if(prop=="month"){console.log("month");}
                else{         
                 sum = sum + parseInt(obj[prop]);
                 obj.sum = sum;
            }
          }
       }
    }
}

var parse = d3.time.format("%b").parse;


// Transpose the data into layers
var dataset = d3.layout.stack()(["MobileCoupon", "Bonus", "Promotions", "Merchandise","sum"].map(function(fruit) {
  return data.map(function(d) {
    return {x: parse(d.month), y: +d[fruit]};
  });
}));


// Set x, y and colors
var x = d3.scale.ordinal()
  .domain(dataset[0].map(function(d) { return d.x; }))
  .rangeRoundBands([10, width-10], 0.35);

var y = d3.scale.linear()
  .domain([0, d3.max(dataset, function(d) {  return d3.max(d, function(d) { return d.y0 + d.y; });  })])
  .range([height, 0]);

var colors = ["#3D0000", "#d25c4d", "#f2b447", "#d9d574"];

// Define and draw axes
var yAxis = d3.svg.axis()
  .scale(y)
  .orient("left")

  .ticks(5)
 .tickSize(-width, 0, 0)
  .tickFormat( function(d) { return "$" + d } );

var xAxis = d3.svg.axis()
  .scale(x)
  .orient("bottom")
  .tickFormat(d3.time.format("%b"));

svg.append("g")
  .attr("class", "y axis")
  .attr("transform", "translate(0,0)")
  .call(yAxis);


svg.append("g")
.call(xAxis)
  .attr("class", "x axis")
  .attr("transform", "translate(0," + height + ")");
  //.call(xAxis);


// Create groups for each series, rects for each segment 
var groups = svg.selectAll("g.cost")
  .data(dataset)
  .enter().append("g")
  .attr("class", "cost")
  .style("fill", function(d, i) { return colors[i]; });




  var svg = d3.select("svg");
  var bar = groups.selectAll("g")
     .data(function(d) { return d; })
  .enter().append("g")
    .attr("transform", function(d, i) { return "translate(0," + i * y(d.y0) - y(d.y0 + d.y) + ")"; });


 bar.append("rect")
  .attr("x", function(d) { return x(d.x); })
  .attr("y", function(d) { return y(d.y0 + d.y); })
  .attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); })
  .attr("width", x.rangeBand())


bar.append("text")
    .attr("x", function(d) { return x(d.x); })
  .attr("y", function(d) { return y(d.y0 + d.y); })
    .attr("dy", ".35em")
    .text(function(d) { return d.y; });

  svg.call(tip);





// Draw legend
var legend = svg.selectAll(".legend")
  .data(colors)
  .enter().append("g")
  .attr("class", "legend")
  .attr("transform", function(d, m) { return "translate(90," + (m+5) * 20 + ")"; });

legend.append("rect")
  .attr("x", width - 18)
  .attr("width", 18)
  .attr("height", 18)
  .style("fill", function(d, i) {return colors.slice().reverse()[i];});

legend.append("text")
  .attr("x", width + 5)
  .attr("y", 9)
  .attr("dy", ".35em")
  .style("text-anchor", "start")
  .text(function(d, i) { 
    switch (i) {
      case 0: return "Mobile Coupon";
      case 1: return "Bonus";
      case 2: return "Promotions";
      case 3: return "Merchandise";
    }
  });

Upvotes: 0

Related Questions