|
<!DOCTYPE html> |
|
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> |
|
<head> |
|
<meta charset="utf-8" /> |
|
<title>Bayesian Blocks Algorithm</title> |
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script> |
|
<style> |
|
svg .histbar { |
|
stroke-width: 1; |
|
stroke: #000000; |
|
shape-rendering: crispEdges; |
|
} |
|
svg text { |
|
font: 16px sans-serif; |
|
} |
|
#slidecontainer { |
|
width: 100%; /* Width of the outside container */ |
|
} |
|
|
|
/* The slider itself */ |
|
.slider { |
|
-webkit-appearance: none; /* Override default CSS styles */ |
|
appearance: none; |
|
width: 100%; /* Full-width */ |
|
height: 15px; /* Specified height */ |
|
border-radius: 5px; |
|
background: #d3d3d3; /* Grey background */ |
|
outline: none; /* Remove outline */ |
|
opacity: 0.7; /* Set transparency (for mouse-over effects on hover) */ |
|
-webkit-transition: .2s; /* 0.2 seconds transition on hover */ |
|
transition: opacity .2s; |
|
margin-bottom: 10px; |
|
margin-top: 10px; |
|
margin-left: 5px; |
|
} |
|
|
|
/* Mouse-over effects */ |
|
.slider:hover { |
|
opacity: 1; /* Fully shown on mouse-over */ |
|
} |
|
|
|
.slider::-webkit-slider-thumb { |
|
-webkit-appearance: none; |
|
appearance: none; |
|
width: 25px; |
|
height: 25px; |
|
border-radius: 50%; |
|
background: #4CAF50; |
|
cursor: pointer; |
|
} |
|
|
|
.slider::-moz-range-thumb { |
|
width: 25px; |
|
height: 25px; |
|
border-radius: 50%; |
|
background: #4CAF50; |
|
cursor: pointer; |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<div id="slidecontainer"> |
|
<input type="range" min="1" max="1000" id="timeout" class="slider" value="1000"> |
|
</div> |
|
<svg id="bbsvg" version="1.1" xmlns="http://www.w3.org/2000/svg" width="960" height="450" viewBox="0 0 960 450"> |
|
<g id="bars-g"></g> |
|
<g id="dots-g"></g> |
|
<g> |
|
<rect x="32" y="32" width="112" height="88" rx="16" ry="16" style="fill: #ffffcc; fill-opacity: 0.75;" /> |
|
<text id="outer-loop-text" x="64" y="64"></text> |
|
<text id="inner-loop-text" x="64" y="96"></text> |
|
</g> |
|
</svg> |
|
<script src="interactive-bayesian-blocks.js"></script> |
|
<script> |
|
"use strict"; |
|
|
|
// Generate data |
|
var nPoints = 99; |
|
var samples = []; |
|
for (var i=0; i<nPoints/3; ++i) { |
|
samples.push(d3.random.normal(20, 5)()); |
|
} |
|
for (var i=0; i<nPoints/3; ++i) { |
|
samples.push(d3.random.normal(50, 5)()); |
|
} |
|
for (var i=0; i<nPoints/3; ++i) { |
|
samples.push(d3.random.normal(80, 5)()); |
|
} |
|
|
|
// Set up plot |
|
var mysvg = document.getElementById("bbsvg"); |
|
var myBars = mysvg.getElementById("bars-g"); |
|
var myDots = mysvg.getElementById("dots-g"); |
|
var myILabel = mysvg.getElementById("outer-loop-text"); |
|
var myRLabel = mysvg.getElementById("inner-loop-text"); |
|
var mywidth = mysvg.viewBox.baseVal.width; |
|
var myheight = mysvg.viewBox.baseVal.height; |
|
var dotRadius = 8; |
|
var dotPadding = 4; |
|
var xScale = d3.scale.linear() |
|
.rangeRound([0, mywidth]) |
|
.domain([d3.min(samples) - 1, d3.max(samples) + 1]); |
|
var yScale = d3.scale.linear() |
|
.rangeRound([myheight-2*(dotRadius+dotPadding), 0]) |
|
.clamp(true) |
|
.domain([0, 0.8]); |
|
|
|
// Draw samples |
|
var dots = d3.select(myDots).selectAll("circle").data(samples); |
|
dots.enter().append("circle"); |
|
dots.attr("cx", function(d) { return xScale(d); }) |
|
.attr("cy", myheight - (dotRadius + dotPadding)) |
|
.attr("r", dotRadius) |
|
.attr("opacity", 0.1); |
|
|
|
// Construct algorithm |
|
var ncp_prior = 0.1; |
|
var alg = new BayesianBlocksAlgorithm(samples, ncp_prior); |
|
|
|
// Assign colors to ops |
|
var colors = []; |
|
colors[alg.states.FIT_NULL] = "#cccccc"; |
|
colors[alg.states.FIT_INIT] = "#cccc33"; |
|
colors[alg.states.FIT_IMPROVED] = "#33cc33"; |
|
colors[alg.states.FIT_REGRESSED] = "#cc3333"; |
|
colors[alg.states.FIT_SELECTED] = "#3333cc"; |
|
|
|
// Update timeout and transition duration values |
|
var t = document.getElementById("timeout").value; |
|
var d = t/4; |
|
|
|
function updateViz() { |
|
// Update histogram bins |
|
// Use bin end as key for object constancy |
|
var bars = d3.select(myBars).selectAll("rect").data(alg.bins, function(d) { return d.end; }); |
|
bars.enter().append("rect") |
|
.classed("histbar", true) |
|
.attr("x", function(d) { return xScale(d.start); }) |
|
.attr("width", function(d) { return 0; }) |
|
.attr("y", function(d) { return yScale(d.height); }) |
|
.attr("height", function(d) { return yScale(0) - yScale(d.height); }) |
|
.style("fill", colors[alg.states.FIT_NULL]); |
|
bars.transition().duration(d) |
|
.attr("x", function(d) { return xScale(d.start); }) |
|
.attr("width", function(d) { return xScale(d.end) - xScale(d.start); }) |
|
.attr("y", function(d) { return yScale(d.height); }) |
|
.attr("height", function(d) { return yScale(0) - yScale(d.height); }) |
|
.transition().duration(d) |
|
.style("fill", colors[alg.lastOp]) |
|
.transition().duration(d) |
|
.style("fill", colors[alg.states.FIT_NULL]); |
|
bars.exit().transition().duration(d) |
|
.attr("width", 0) |
|
.remove(); |
|
|
|
// Update status text |
|
d3.select(myILabel).text("R = " + alg.lastI); |
|
d3.select(myRLabel).text("r = " + ((alg.lastR >= 0) ? alg.lastR : alg.bestR)); |
|
} |
|
|
|
// Animate the algorithm |
|
(function animateAlg() { |
|
if (!alg.isFinished()) { |
|
alg.step(); |
|
updateViz(); |
|
t = document.getElementById("timeout").value; |
|
setTimeout(animateAlg, t); |
|
} |
|
})(); |
|
</script> |
|
</body> |
|
</html> |