|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<style> |
|
body { |
|
font-family: sans-serif |
|
} |
|
|
|
h2 { |
|
text-align: center; |
|
} |
|
|
|
.bar rect { |
|
fill: steelblue; |
|
stroke: darkblue; |
|
} |
|
|
|
.bar text { |
|
fill: #fff; |
|
font: 10px sans-serif; |
|
} |
|
|
|
</style> |
|
<body> |
|
<script src="//d3js.org/d3.v4.min.js"></script> |
|
<script> |
|
|
|
//var data = d3.range(1000).map(d3.randomBates(10)); |
|
var data = d3.range(10000).map(d3.randomNormal()).sort(d3.ascending); |
|
|
|
var formatCount = d3.format(",.0f"); |
|
|
|
var margin = {top: 10, right: 30, bottom: 30, left: 30}, |
|
width = 940 - margin.left - margin.right, |
|
height = 300 - margin.top - margin.bottom; |
|
|
|
var dataDomain = [d3.min(data), d3.max(data)]; |
|
|
|
var x = d3.scaleLinear() |
|
.domain([d3.min(data), d3.max(data)]) |
|
.rangeRound([0, width]) |
|
// make x domain start and end nice |
|
.nice(); |
|
|
|
var numBins = 42; |
|
|
|
// histogram thresholds for (roughly) equi depth histogram |
|
function equiDepthThresholds() { |
|
var binBounds = []; |
|
var depth = Math.floor(data.length / numBins); |
|
for(var j=0; j<data.length; j+=depth) { |
|
binBounds.push(data[j]); |
|
} |
|
return binBounds; |
|
} |
|
|
|
var binBounds = [ |
|
{ |
|
thresholds: d3.range(numBins).map(d3.randomUniform(x.domain()[0], x.domain()[1])), |
|
name: 'uniform random' |
|
}, |
|
{ |
|
thresholds: d3.range(numBins).map(d3.randomNormal(x.domain()[0], x.domain()[1])), |
|
name: 'normal' |
|
}, |
|
{ |
|
thresholds: x.ticks(numBins), |
|
name: 'equi width' |
|
}, |
|
{ |
|
thresholds: equiDepthThresholds(), |
|
name: 'equi depth' |
|
} |
|
] |
|
|
|
binBounds.forEach(function(bounds) { |
|
var thresholds = bounds.thresholds; |
|
|
|
// add the data domain bounds to the thresholds |
|
thresholds.concat(dataDomain) |
|
thresholds.sort(d3.ascending); |
|
|
|
var bins = d3.histogram() |
|
.domain(x.domain()) |
|
.thresholds(thresholds) |
|
(data); |
|
|
|
// the value is the height |
|
bins = bins.map(function(d) { |
|
d.value = d.length / (d.x1 - d.x0); |
|
return d; |
|
}) |
|
|
|
var y = d3.scaleLinear() |
|
.domain([0, d3.max(bins, function(d) { return d.value; })]) |
|
.range([height, 0]); |
|
|
|
var body = d3.select("body"); |
|
|
|
body.append("h2").html(bounds.name) |
|
|
|
var svg = 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 + ")"); |
|
|
|
var bar = svg.selectAll(".bar") |
|
.data(bins) |
|
.enter().append("g") |
|
.attr("class", "bar") |
|
.attr("transform", function(d) { return "translate(" + x(d.x0) + "," + y(d.value) + ")"; }); |
|
|
|
bar.append("rect") |
|
.attr("x", 1) |
|
.attr("width", function(d) { return x(d.x1) - x(d.x0); }) |
|
.attr("height", function(d) { return height - (y(d.value)); }); |
|
|
|
bar.append("text") |
|
.attr("dy", ".75em") |
|
.attr("y", 6) |
|
.attr("x", function(d) { return (x(d.x1) - x(d.x0)) / 2; }) |
|
.attr("text-anchor", "middle") |
|
.text(function(d) { return formatCount(d.length); }); |
|
|
|
svg.append("g") |
|
.attr("class", "axis axis--x") |
|
.attr("transform", "translate(0," + height + ")") |
|
.call(d3.axisBottom(x)); |
|
}); |
|
|
|
</script> |