Skip to content

Instantly share code, notes, and snippets.

@TommyCoin80
Last active February 14, 2017 18:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TommyCoin80/9cb0ee20d677bc92ebf18c775982f54d to your computer and use it in GitHub Desktop.
Save TommyCoin80/9cb0ee20d677bc92ebf18c775982f54d to your computer and use it in GitHub Desktop.
Bin Zooming Histogram
<!DOCTYPE html>
<meta charset="utf-8">
<head>
<link href='https://fonts.googleapis.com/css?family=Play' rel='stylesheet' type='text/css'>
<style>
body {
margin:auto;
font-family: 'Play', sans-serif;
font-size:100%;
cursor: default;
}
#dashboard {
width:100%;
}
text {
font-family: 'Play', sans-serif;
pointer-events: none;
}
rect {
fill:#428bca;
}
.zoomButton{
font-size:80px;
fill:white;
}
.zoomButton.plus rect{
fill:#5cb85c;
}
.zoomButton.minus rect{
fill:#d9534f;
}
.active {
opacity:1;
transition: opacity 1000ms;
}
.inactive {
opacity:.1;
transition: opacity 1000ms;
}
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<body>
<div id="dashboard"></div>
<script>
var margin = {top:50,left:50,bottom:50,right:75},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
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 + ")");
var data = d3.range(1000).map(d3.randomNormal(80,10));
data.sort(function(a,b) { return a-b;});
var x = d3.scaleLinear()
.domain(d3.extent(data))
.range([0,width]);
var ticks = { req: { min:5, max:40}, got:[]};
(function() {
var t = 0;
d3.range(ticks.req.min,ticks.req.max+1,1).forEach(function(d) {
if( x.ticks(d).length > t) {
t = x.ticks(d).length;
ticks.got.push(x.ticks(d).length)
}
});
})();
ticks.def = ticks.got[Math.round(ticks.got.length/2)-1];
ticks.cur = ticks.def;
var bins = binData(data,x,ticks.def);
var y = d3.scaleLinear()
.range([height,0])
.domain([0,d3.max(bins.map(function(d) { return d.count}))]);
var xAxis = d3.axisBottom(x),
yAxis = d3.axisLeft(y);
svg.selectAll(".bar")
.data(bins)
.enter()
.append("rect")
.attr("class","bar")
.attr("width", x(bins[0].max) - x(bins[0].min) - 2)
.attr("height", function(d) { return height - y(d.count) })
.attr("x", function(d) { return x(d.min)})
.attr("y", function(d) { return y(d.count)});
svg.append("g")
.attr("class","axis x")
.attr("transform","translate(0," + height + ")")
.call(xAxis)
svg.append("g")
.attr("class","axis y")
.call(yAxis);
var plusButton = svg.append("g")
.attr("class","zoomButton plus")
.attr("transform","translate(" + width + ",0)")
.classed("active", ticks.got.indexOf(ticks.cur)<(ticks.got.length-1))
.classed("inactive", ticks.got.indexOf(ticks.cur)==(ticks.got.length-1))
.on("click",zoom)
plusButton.append("rect")
.attr("width",50)
.attr("height",50)
plusButton.append("text")
.attr("x",25)
.attr("y", 50)
.text("+")
.attr("text-anchor","middle")
var minusButton = svg.append("g")
.attr("class","zoomButton minus")
.attr("transform","translate(" + width + "," + 55+ ")")
.classed("active", ticks.got.indexOf(ticks.cur)>0)
.classed("inactive", ticks.got.indexOf(ticks.cur)==0)
.on("click", zoom)
minusButton.append("rect")
.attr("width",50)
.attr("height",50)
minusButton.append("text")
.attr("x",25)
.attr("y", 50)
.attr("dy",-2)
.text("-")
.attr("text-anchor","middle");
var duration = 1000;
function binData(dat, s, t) {
var b = [];
s.ticks(t).forEach(function(d,i) {
if(i < x.ticks(t).length -1) {
b.push({bin:i,min:d,max:x.ticks(t)[i+1]})
}
});
b.forEach(function(d) {
d.count = dat.filter(function(e) { return e >= d.min && e < d.max}).length;
})
return b;
}
function zoom() {
if(d3.select(this).classed("inactive")) { return 0;}
if(d3.select(this).classed("plus")) {
ticks.cur = ticks.got[ticks.got.indexOf(ticks.cur) + 1]
} else {
ticks.cur = ticks.got[ticks.got.indexOf(ticks.cur) - 1]
}
minusButton.classed("active", ticks.got.indexOf(ticks.cur)>0)
.classed("inactive", ticks.got.indexOf(ticks.cur)==0)
plusButton.classed("active", ticks.got.indexOf(ticks.cur)<(ticks.got.length-1))
.classed("inactive", ticks.got.indexOf(ticks.cur)==(ticks.got.length-1))
svg.select(".axis.x")
.transition()
.duration(duration)
.call(xAxis.ticks(ticks.cur))
var newBins = binData(data,x,ticks.cur);
y.domain([0,d3.max(newBins.map(function(d) { return d.count}))]);
svg.select(".axis.y")
.transition()
.duration(duration)
.call(yAxis);
svg.selectAll(".bar")
.transition()
.duration(duration)
.attr("height", function(d) { return height - y(0) })
.attr("y", function(d) { return y(0)})
.transition()
.remove();
svg.selectAll(null)
.data(newBins)
.enter()
.append("rect")
.attr("class","bar")
.attr("width", x(newBins[0].max) - x(newBins[0].min) - 2)
.attr("height", function(d) { return height - y(0) })
.attr("y", function(d) { return y(0)})
.attr("x", function(d) { return x(d.min)})
.transition()
.duration(duration)
.attr("height", function(d) { return height - y(d.count) })
.attr("y", function(d) { return y(d.count)});
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment