Last active
August 29, 2015 13:57
-
-
Save jthomassie/9405166 to your computer and use it in GitHub Desktop.
Histogram: grouped, stacked, normalized
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<style> | |
body { | |
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; | |
font-size: 13px; | |
margin: auto; | |
position: relative; | |
width: 960px; | |
color: #333; | |
} | |
text { | |
font: 10px sans-serif; | |
fill: #666; | |
} | |
.axis path, | |
.axis line { | |
fill: none; | |
stroke: #666; | |
shape-rendering: crispEdges; | |
} | |
.form { | |
position: relative; | |
float: right; | |
right: 10px; | |
top: 10px; | |
margin: 40px 40px 0 0; | |
} | |
label{ | |
margin: 6px; | |
} | |
</style> | |
<div class="form"> | |
<label><input type="radio" name="mode" value="grouped" checked> Grouped</label> | |
<label><input type="radio" name="mode" value="stacked"> Stacked</label> | |
<label><input type="radio" name="mode" value="normalized"> Normalized</label> | |
</div> | |
<div id="chart"></div> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script> | |
var n = 5, // number of layers | |
m = 65; // number of samples per layer | |
var transitionDuration = 250; // transition duration | |
var stack = d3.layout.stack(), | |
layers = stack(d3.range(n).map(function() { return bumpLayer(m, 0.6); })), | |
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; }); }); | |
console.log(layers); | |
var margin = {top: 40, right: 10, bottom: 20, left: 30}, | |
width = 960 - margin.left - margin.right, | |
height = 350 - margin.top - margin.bottom; | |
var color = d3.scale.linear() | |
.domain([0, n - 1]) | |
.range(["#ffc014", "#990000"]); | |
var x = d3.scale.ordinal() | |
.domain(d3.range(m)) | |
.rangeRoundBands([0, width], 0.1); | |
var y = d3.scale.linear() | |
.domain([0, yGroupMax]) | |
.range([height, 0]); | |
var xAxis = d3.svg.axis() | |
.scale(x) | |
.tickSize(0) | |
.tickPadding(6) | |
.orient("bottom"); | |
var yAxis = d3.svg.axis() | |
.scale(y) | |
.tickSize(0) | |
.tickPadding(6) | |
.orient("left"); | |
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, i, j) { | |
return x(d.x) + x.rangeBand() / n * j; | |
}) | |
.attr("y", height) | |
.attr("width", x.rangeBand() / n) | |
.attr("height", 0); | |
rect.transition() | |
.duration(transitionDuration) | |
.attr("y", function(d) { return y(d.y); }) | |
.attr("height", function(d) { return height - y(d.y); }); | |
svg.append("g") | |
.attr("class", "x axis") | |
.attr("transform", "translate(0," + height + ")") | |
.call(xAxis); | |
svg.append("g") | |
.attr("class", "y axis") | |
.call(yAxis); | |
d3.selectAll("input").on("change", change); | |
function change() { | |
if (this.value === "grouped") { | |
transitionGrouped(); | |
} | |
if (this.value === "stacked") { | |
transitionStacked(); | |
} | |
if (this.value === "normalized") { | |
transitionNormalized(); | |
} | |
} | |
function transitionGrouped() { | |
y.domain([0, yGroupMax]).nice(); | |
rect.transition() | |
.duration(transitionDuration) | |
.attr("x", function(d, i, j) { | |
return x(d.x) + x.rangeBand() / n * j; | |
}) | |
.attr("width", x.rangeBand() / n) | |
.transition() | |
.attr("y", function(d) { | |
return y(d.y); | |
}) | |
.attr("height", function(d) { | |
return height - y(d.y); | |
}); | |
// update the y-axis | |
svg.select('g.y.axis') | |
.transition() | |
.duration(transitionDuration) | |
.call(yAxis); | |
} | |
function transitionStacked() { | |
y.domain([0, yStackMax]).nice(); | |
rect.transition() | |
.duration(transitionDuration) | |
.attr("y", function(d) { | |
return y(d.y0 + d.y); | |
}) | |
.attr("height", function(d) { | |
return y(d.y0) - y(d.y0 + d.y); | |
}) | |
.transition() | |
.attr("x", function(d) { | |
return x(d.x); | |
}) | |
.attr("width", x.rangeBand()); | |
// update the y-axis | |
svg.select('g.y.axis') | |
.transition() | |
.duration(transitionDuration) | |
.call(yAxis); | |
} | |
function transitionNormalized() { | |
function total(i) { | |
var t = 0; | |
for (var j = 0; j < n; j++) { | |
t = t + layers[j][i].y; | |
} | |
return t; | |
} | |
y.domain([0, 100]); | |
rect.transition() | |
.duration(transitionDuration) | |
.attr("y", function(d,i,j) { | |
var t = total(i); | |
var yy = y((d.y0 + d.y)/t*100); | |
return yy; | |
}) | |
.attr("height", function(d,i) { | |
var t = total(i); | |
var hh = y(d.y0/t*100) - y((d.y0 + d.y)/t*100); | |
return hh; | |
}) | |
.transition() | |
.attr("x", function(d) { | |
return x(d.x); | |
}) | |
.transition() | |
.attr("x", function(d) { | |
return x(d.x); | |
}) | |
.attr("width", x.rangeBand()); | |
// update the y-axis | |
svg.select('g.y.axis') | |
.transition() | |
.duration(transitionDuration) | |
.call(yAxis); | |
} | |
// random data generator - inspired by Lee Byron's test data generator | |
function bumpLayer(n, o) { | |
function bump(a) { | |
var x = 1 / ( 0.1 + Math.random() ), | |
y = 2 * Math.random() - 0.5, | |
z = 10 / ( 0.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: Math.max(0, d)}; | |
}); | |
} | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment