block by bricedev f80fdbc2c03dbc1ae4e8

Diverging Stacked Bar Chart (even)

Full Screen

I have been using the diverging Stacked Bar Chart exemple by wpoely86 for a while. I tried to make it a bit more dynamic using less hardcoded constants. It works regardless the number of rows and the Likert scale length (odd or even).

Check this other example with an odd length of likert scale and more rows.

index.html

<!DOCTYPE html>
<meta charset="utf-8">
<style>

body {
  font: 10px sans-serif;
}

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

</style>
<body>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script>

var margin = {top: 80, right: 20, bottom: 20, left: 50},
    width = 960 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;

var y = d3.scale.ordinal()
    .rangeRoundBands([0, height], .3);

var x = d3.scale.linear()
    .rangeRound([0, width]);

var xAxis = d3.svg.axis()
    .scale(x)
    .tickFormat(d3.format(",%"))
    .orient("top");

var yAxis = d3.svg.axis()
    .scale(y)
    .tickSize(0)
    .orient("left");

var color = d3.scale.ordinal()
    .range(["#d73027","#f46d43","#fdae61","#a6d96a","#66bd63","#1a9850"]);

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

d3.csv("data.csv", function(error, data) {
  
  var rateNames = d3.keys(data[0]).filter(function(key) { return key !== "rows"; });
  var rowsNames = data.map(function(d) { return d.rows; });
  var neutralIndex = Math.floor(rateNames.length/2);
  
  color.domain(rateNames);
  
  data.forEach(function(row) {
    row.total = d3.sum(rateNames.map(function(name) { return +row[name]; }));
    rateNames.forEach(function(name) { row['relative'+name] = (row.total !==0 ? +row[name]/row.total : 0); });

    var x0 = -1 * d3.sum(rateNames.map(function(name,i) { return i < neutralIndex ? +row['relative'+name] : 0; }));
    if (rateNames.length & 1) x0 += -1 * row['relative' + rateNames[neutralIndex] ]/2;   
    var idx = 0;
    
    row.boxes = rateNames.map(function(name) { 
      return {name: name, x0: x0, x1: x0 += row['relative'+name], total: row.total, absolute: row[name]}; 
    });
  });

  var min = d3.min(data, function(d) { return d.boxes["0"].x0; });
  var max = d3.max(data, function(d) { return d.boxes[d.boxes.length-1].x1; });

  x.domain([min, max]).nice();
  y.domain(rowsNames);

  svg.append("g")
     .attr("class", "x axis")
     .call(xAxis);

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

  var rows = svg.selectAll(".row")
      .data(data)
    .enter().append("g")
      .attr("class", "bar")
      .attr("transform", function(d) { return "translate(0," + y(d.rows) + ")"; })
      .on("mouseover", function(d) {
        svg.selectAll('.y').selectAll('text').filter(function(text) { return text===d.rows; })
            .transition().duration(100).style('font','15px sans-serif');
      })
      .on("mouseout", function(d) {
        svg.selectAll('.y').selectAll('text').filter(function(text) { return text===d.rows; })
            .transition().duration(100).style('font','10px sans-serif');
      });

  var bars = rows.selectAll("rect")
      .data(function(d) { return d.boxes; })
      .enter().append("g");

  bars.append("rect")
      .attr("height", y.rangeBand())
      .attr("x", function(d) { return x(d.x0); })
      .attr("width", function(d) { return x(d.x1) - x(d.x0); })
      .style("fill", function(d) { return color(d.name); });

  bars.append("text")
    .attr("x", function(d) { return x(d.x0); })
    .attr("y", y.rangeBand()/2)
    .attr("dy", "0.5em")
    .attr("dx", "0.5em")
    .style("text-anchor", "begin")
    .text(function(d) { return d.absolute !== 0 && (d.x1-d.x0)>0.04 ? d.absolute : "" });

  svg.append("g")
      .attr("class", "y axis")
    .append("line")
      .attr("x1", x(0))
      .attr("x2", x(0))
      .attr("y2", height);

  var legend = svg.selectAll(".legend")
      .data(rateNames)
    .enter().append("g")
      .attr("class", "legend")
      .attr("transform", function(d, i) { return "translate(" + width/rateNames.length * i + ",-55)"; });

  legend.append("rect")
        .attr("x", 0)
        .attr("width", 18)
        .attr("height", 18)
        .style("fill", color);

  legend.append("text")
        .attr("x", 22)
        .attr("y", 9)
        .attr("dy", ".35em")
        .style("text-anchor", "begin")
        .text(function(d) { return d; });

});

</script>

data.csv

rows,Not at all,Strongly disagree,Disagree,Agree,Strongly agree,Absolutely
row 1,396,430,482,583,720,400
row 2,169,73,299,836,309,824
row 3,435,188,435,601,619,615
row 4,2,543,165,252,994,787
row 5,970,396,14,540,794,391
row 6,616,115,792,947,735,441
row 7,339,389,294,557,494,278
row 8,378,203,319,842,314,105
row 9,789,686,487,54,396,456
row 10,81,176,536,392,91,943
row 11,201,402,524,730,965,519