Last active
February 14, 2017 18:10
-
-
Save TommyCoin80/9cb0ee20d677bc92ebf18c775982f54d to your computer and use it in GitHub Desktop.
Bin Zooming Histogram
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"> | |
<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