Streamgraph

For continuous data such as time series, a streamgraph can be used in place of stacked bars. This example also demonstrates path transitions to interpolate between different layouts. Streamgraph algorithm, colors, and data generation inspired by Byron and Wattenberg.

Source Code

 1 var n = 20, // number of layers
 2     m = 200, // number of samples per layer
 3     data0 = d3.layout.stack().offset("wiggle")(stream_layers(n, m)),
 4     data1 = d3.layout.stack().offset("wiggle")(stream_layers(n, m)),
 5     color = d3.interpolateRgb("#aad", "#556");
 6 
 7 var width = 960,
 8     height = 500,
 9     mx = m - 1,
10     my = d3.max(data0.concat(data1), function(d) {
11       return d3.max(d, function(d) {
12         return d.y0 + d.y;
13       });
14     });
15 
16 var area = d3.svg.area()
17     .x(function(d) { return d.x * width / mx; })
18     .y0(function(d) { return height - d.y0 * height / my; })
19     .y1(function(d) { return height - (d.y + d.y0) * height / my; });
20 
21 var vis = d3.select("#chart")
22   .append("svg")
23     .attr("width", width)
24     .attr("height", height);
25 
26 vis.selectAll("path")
27     .data(data0)
28   .enter().append("path")
29     .style("fill", function() { return color(Math.random()); })
30     .attr("d", area);
31 
32 function transition() {
33   d3.selectAll("path")
34       .data(function() {
35         var d = data1;
36         data1 = data0;
37         return data0 = d;
38       })
39     .transition()
40       .duration(2500)
41       .attr("d", area);
42 }
 1 /* Inspired by Lee Byron's test data generator. */
 2 function stream_layers(n, m, o) {
 3   if (arguments.length < 3) o = 0;
 4   function bump(a) {
 5     var x = 1 / (.1 + Math.random()),
 6         y = 2 * Math.random() - .5,
 7         z = 10 / (.1 + Math.random());
 8     for (var i = 0; i < m; i++) {
 9       var w = (i / m - y) * z;
10       a[i] += x * Math.exp(-w * w);
11     }
12   }
13   return d3.range(n).map(function() {
14       var a = [], i;
15       for (i = 0; i < m; i++) a[i] = o + o * Math.random();
16       for (i = 0; i < 5; i++) bump(a);
17       return a.map(stream_index);
18     });
19 }
20 
21 /* Another layer generator using gamma distributions. */
22 function stream_waves(n, m) {
23   return d3.range(n).map(function(i) {
24     return d3.range(m).map(function(j) {
25         var x = 20 * j / m - i / 3;
26         return 2 * x * Math.exp(-.5 * x);
27       }).map(stream_index);
28     });
29 }
30 
31 function stream_index(d, i) {
32   return {x: i, y: Math.max(0, d)};
33 }
Copyright © 2012 Mike Bostock
Fork me on GitHub