block by bricedev a7c02c493896a33d5f37

Diverging Stacked Bar Chart (odd)

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 even 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(["#ca0020","#f4a582","#d5d5d5","#92c5de","#0571b0"]);

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,Strongly disagree,Disagree,Medium,Agree,Strongly agree
row 1,24,294,594,1927,376
row 2,2,2,0,7,0
row 3,2,0,2,4,2
row 4,0,2,1,7,6
row 5,0,1,3,16,4
row 6,1,1,2,9,3
row 7,0,0,1,4,0
row 8,0,0,0,0,2