3
votes

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/

2
Perhaps you could include a working example with example data. It's very easy to insert a working code snippet directly in your post.Cool Blue
fiddle.jshell.net/1fsm8cst/2. Please find my code in this fiddle.Akshay Shinde
The label values are correct in your fiddle... each label is the value for the individual bar, not the total as you claim.Cool Blue
I want to have another label at the top of each bar which shows the sum of all the stacked rectangles of each bar.Akshay Shinde
I could me missing something but I don't see any attempt by you to do that in the fiddle...Cool Blue

2 Answers

3
votes

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>
0
votes

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