block by milroc 3063069

Area encoding to help a friend

Full Screen

Code to help Walter Gunter Jr. (@wegunterjr) with learning d3.js. based on: http://jsfiddle.net/wegunterjr/r92zG/

This is a variation of a stacked bar chart, where an additional dimension is mapped to width of the section. This additional dimension causes unnecessary complexity for the user.

Normal bar charts use a 1-dimensional area encoding (or redundantly encodes position and area), namely that the bars are all comparable in size, but fixed to one dimension.

By adding a second dimension (regardless of whether it is aesthetically pleasing), the usefulness of the encoding is still mapped to the area. Namely that the reader will interpret the smaller area as meaningful. Therefore it is recommended to map these dimensions with data that have a directly proportional relationship.

index.html

<!DOCTYPE html>
  <head>
    <meta charset="utf-8">
    <link type="text/css" rel="stylesheet" href="style.css"/>
  </head>
  <body>
    <div id="chart"></div>
    <script src="//mbostock.github.com/d3/d3.js?2.7.2"></script>
    <script src="src.js"></script>
  </body>
</html>

src.js

// Inspired by Lee Byron's test data generator.
function bumpLayer(n, o) {

  function bump(a) {
    var x = 1 / (.1 + Math.random()),
        y = 2 * Math.random() - .5,
        z = 10 / (.1 + Math.random());
    for (var i = 0; i < n; i++) {
      var w = (i / n - y) * z;
      a[i] += x * Math.exp(-w * w);
    }
  }

  var a = [], i;
  for (i = 0; i < n; ++i) a[i] = o + o * Math.random();
  for (i = 0; i < 5; ++i) bump(a);
  return a.map(function(d, i) { return {x: i, y: d, w: Math.random()*100}; });
}

var n = 4, // number of layers
    m = 5, // number of samples per layer
    stack = d3.layout.stack(),
    layers = stack(d3.range(n).map(function() { return bumpLayer(m, .1); })),
    yGroupMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y; }); }),
    yStackMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y0 + d.y; }); }),
    wGroupMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.w; })});

var margin = {top: 40, right: 10, bottom: 20, left: 10},
    width = 600 - margin.left - margin.right,
    height = 600 - margin.top - margin.bottom;

var x = d3.scale.ordinal()
    .domain(d3.range(m))
    .rangeRoundBands([0, width], .08);

var y = d3.scale.linear()
    .domain([0, yStackMax])
    .range([height, 0]);

var color = d3.scale.category10()
    .domain(d3.range(n - 1))
    .range(["#EA8825", "#FCBC6E", "#FF5800", "#F58025"])

var xAxis = d3.svg.axis()
    .scale(x)
    .tickSize(0)
    .tickPadding(6)
    .orient("bottom");

var svg = d3.select("#chart").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 + ")");

var layer = svg.selectAll(".layer")
    .data(layers)
  .enter().append("g")
    .attr("class", "layer")
    .style("fill", function(d, i) { return color(i); });

var rect = layer.selectAll("rect")
    .data(function(d) { return d; })
  .enter().append("rect")
    .attr("x", function(d) { return x(d.x) + (x.rangeBand() * (1 - d.w / wGroupMax)) / 2; })
    // .attr("x", function(d) { return x(d.x); })
    .attr("y", height)
    .attr("width", function(d) { return x.rangeBand() * d.w / wGroupMax; })
    .attr("height", 0);

rect.transition()
    .delay(function(d, i) { return i * 10; })
    .attr("y", function(d) { return y(d.y0 + d.y); })
    .attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); });

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

style.css

body {
  background-color: steelblue;
  color: white;
}

svg text {
  fill: white;
  font-size: 16px;
}