Skip to content

Instantly share code, notes, and snippets.

@adamwlev
Created January 4, 2017 05:51
Show Gist options
  • Save adamwlev/f7817380c753f8953e1f9446274e66c7 to your computer and use it in GitHub Desktop.
Save adamwlev/f7817380c753f8953e1f9446274e66c7 to your computer and use it in GitHub Desktop.
BayesApp
license: gpl-3.0
.d3-slider {
position: relative;
font-family: Verdana,Arial,sans-serif;
font-size: 1.1em;
border: 1px solid #aaaaaa;
z-index: 2;
}
.d3-slider-horizontal {
height: .8em;
}
.d3-slider-range {
background:#2980b9;
left:0px;
right:0px;
height: 0.8em;
position: absolute;
}
.d3-slider-range-vertical {
background:#2980b9;
left:0px;
right:0px;
position: absolute;
top:0;
}
.d3-slider-vertical {
width: .8em;
height: 100px;
}
.d3-slider-handle {
position: absolute;
width: 1.2em;
height: 1.2em;
border: 1px solid #d3d3d3;
border-radius: 4px;
background: #eee;
background: linear-gradient(to bottom, #eee 0%, #ddd 100%);
z-index: 3;
}
.d3-slider-handle:hover {
border: 1px solid #999999;
}
.d3-slider-horizontal .d3-slider-handle {
top: -.3em;
margin-left: -.6em;
}
.d3-slider-axis {
position: relative;
z-index: 1;
}
.d3-slider-axis-bottom {
top: .8em;
}
.d3-slider-axis-right {
left: .8em;
}
.d3-slider-axis path {
stroke-width: 0;
fill: none;
}
.d3-slider-axis line {
fill: none;
stroke: #aaa;
shape-rendering: crispEdges;
}
.d3-slider-axis text {
font-size: 11px;
}
.d3-slider-vertical .d3-slider-handle {
left: -.25em;
margin-left: 0;
margin-bottom: -.6em;
}
/*
D3.js Slider
Inspired by jQuery UI Slider
Copyright (c) 2013, Bjorn Sandvik - http://blog.thematicmapping.org
BSD license: http://opensource.org/licenses/BSD-3-Clause
*/
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['d3'], factory);
} else if (typeof exports === 'object') {
if (process.browser) {
// Browserify. Import css too using cssify.
require('./d3.slider.css');
}
// Node. Does not work with strict CommonJS, but
// only CommonJS-like environments that support module.exports,
// like Node.
module.exports = factory(require('d3'));
} else {
// Browser globals (root is window)
root.d3.slider = factory(root.d3);
}
}(this, function (d3) {
return function module() {
"use strict";
// Public variables width default settings
var min = 0,
max = 100,
step = 0.01,
animate = true,
orientation = "horizontal",
axis = false,
margin = 50,
value,
active = 1,
scale;
// Private variables
var axisScale,
dispatch = d3.dispatch("slide", "slideend"),
formatPercent = d3.format(".2%"),
tickFormat = d3.format(".0"),
handle1,
handle2 = null,
sliderLength;
function slider(selection) {
selection.each(function() {
// Create scale if not defined by user
if (!scale) {
scale = d3.scale.linear().domain([min, max]);
}
// Start value
value = value || scale.domain()[0];
// DIV container
var div = d3.select(this).classed("d3-slider d3-slider-" + orientation, true);
var drag = d3.behavior.drag();
drag.on('dragend', function () {
dispatch.slideend(d3.event, value);
})
// Slider handle
//if range slider, create two
var divRange;
if ( value.length == 2 ) {
handle1 = div.append("a")
.classed("d3-slider-handle", true)
.attr("xlink:href", "#")
.attr('id', "handle-one")
.on("click", stopPropagation)
.call(drag);
handle2 = div.append("a")
.classed("d3-slider-handle", true)
.attr('id', "handle-two")
.attr("xlink:href", "#")
.on("click", stopPropagation)
.call(drag);
} else {
handle1 = div.append("a")
.classed("d3-slider-handle", true)
.attr("xlink:href", "#")
.attr('id', "handle-one")
.on("click", stopPropagation)
.call(drag);
}
// Horizontal slider
if (orientation === "horizontal") {
div.on("click", onClickHorizontal);
if ( value.length == 2 ) {
divRange = d3.select(this).append('div').classed("d3-slider-range", true);
handle1.style("left", formatPercent(scale(value[ 0 ])));
divRange.style("left", formatPercent(scale(value[ 0 ])));
drag.on("drag", onDragHorizontal);
var width = 100 - parseFloat(formatPercent(scale(value[ 1 ])));
handle2.style("left", formatPercent(scale(value[ 1 ])));
divRange.style("right", width+"%");
drag.on("drag", onDragHorizontal);
} else {
handle1.style("left", formatPercent(scale(value)));
drag.on("drag", onDragHorizontal);
}
sliderLength = parseInt(div.style("width"), 10);
} else { // Vertical
div.on("click", onClickVertical);
drag.on("drag", onDragVertical);
if ( value.length == 2 ) {
divRange = d3.select(this).append('div').classed("d3-slider-range-vertical", true);
handle1.style("bottom", formatPercent(scale(value[ 0 ])));
divRange.style("bottom", formatPercent(scale(value[ 0 ])));
drag.on("drag", onDragVertical);
var top = 100 - parseFloat(formatPercent(scale(value[ 1 ])));
handle2.style("bottom", formatPercent(scale(value[ 1 ])));
divRange.style("top", top+"%");
drag.on("drag", onDragVertical);
} else {
handle1.style("bottom", formatPercent(scale(value)));
drag.on("drag", onDragVertical);
}
sliderLength = parseInt(div.style("height"), 10);
}
if (axis) {
createAxis(div);
}
function createAxis(dom) {
// Create axis if not defined by user
if (typeof axis === "boolean") {
axis = d3.svg.axis()
.ticks(Math.round(sliderLength / 100))
.tickFormat(tickFormat)
.orient((orientation === "horizontal") ? "bottom" : "right");
}
// Copy slider scale to move from percentages to pixels
axisScale = scale.copy().range([0, sliderLength]);
axis.scale(axisScale);
// Create SVG axis container
var svg = dom.append("svg")
.classed("d3-slider-axis d3-slider-axis-" + axis.orient(), true)
.on("click", stopPropagation);
var g = svg.append("g");
// Horizontal axis
if (orientation === "horizontal") {
svg.style("margin-left", -margin + "px");
svg.attr({
width: sliderLength + margin * 2,
height: margin
});
if (axis.orient() === "top") {
svg.style("top", -margin + "px");
g.attr("transform", "translate(" + margin + "," + margin + ")");
} else { // bottom
g.attr("transform", "translate(" + margin + ",0)");
}
} else { // Vertical
svg.style("top", -margin + "px");
svg.attr({
width: margin,
height: sliderLength + margin * 2
});
if (axis.orient() === "left") {
svg.style("left", -margin + "px");
g.attr("transform", "translate(" + margin + "," + margin + ")");
} else { // right
g.attr("transform", "translate(" + 0 + "," + margin + ")");
}
}
g.call(axis);
}
function onClickHorizontal() {
if (!value.length) {
var pos = Math.max(0, Math.min(sliderLength, d3.event.offsetX || d3.event.layerX));
moveHandle(stepValue(scale.invert(pos / sliderLength)));
}
}
function onClickVertical() {
if (!value.length) {
var pos = sliderLength - Math.max(0, Math.min(sliderLength, d3.event.offsetY || d3.event.layerY));
moveHandle(stepValue(scale.invert(pos / sliderLength)));
}
}
function onDragHorizontal() {
if ( d3.event.sourceEvent.target.id === "handle-one") {
active = 1;
} else if ( d3.event.sourceEvent.target.id == "handle-two" ) {
active = 2;
}
var pos = Math.max(0, Math.min(sliderLength, d3.event.x));
moveHandle(stepValue(scale.invert(pos / sliderLength)));
}
function onDragVertical() {
if ( d3.event.sourceEvent.target.id === "handle-one") {
active = 1;
} else if ( d3.event.sourceEvent.target.id == "handle-two" ) {
active = 2;
}
var pos = sliderLength - Math.max(0, Math.min(sliderLength, d3.event.y))
moveHandle(stepValue(scale.invert(pos / sliderLength)));
}
function stopPropagation() {
d3.event.stopPropagation();
}
});
}
// Move slider handle on click/drag
function moveHandle(newValue) {
var currentValue = value.length ? value[active - 1]: value;
if (currentValue !== newValue) {
var oldPos = formatPercent(scale(stepValue(currentValue))),
newPos = formatPercent(scale(stepValue(newValue))),
position = (orientation === "horizontal") ? "left" : "bottom";
if ( value.length === 2) {
value[ active - 1 ] = newValue;
if (d3.event) {
dispatch.slide(d3.event, value );
};
} else {
if (d3.event) {
dispatch.slide(d3.event.sourceEvent || d3.event, value = newValue);
};
}
if ( value[ 0 ] >= value[ 1 ] ) return;
if ( active === 1 ) {
if (value.length === 2) {
(position === "left") ? divRange.style("left", newPos) : divRange.style("bottom", newPos);
}
if (animate) {
handle1.transition()
.styleTween(position, function() { return d3.interpolate(oldPos, newPos); })
.duration((typeof animate === "number") ? animate : 250);
} else {
handle1.style(position, newPos);
}
} else {
var width = 100 - parseFloat(newPos);
var top = 100 - parseFloat(newPos);
(position === "left") ? divRange.style("right", width + "%") : divRange.style("top", top + "%");
if (animate) {
handle2.transition()
.styleTween(position, function() { return d3.interpolate(oldPos, newPos); })
.duration((typeof animate === "number") ? animate : 250);
} else {
handle2.style(position, newPos);
}
}
}
}
// Calculate nearest step value
function stepValue(val) {
if (val === scale.domain()[0] || val === scale.domain()[1]) {
return val;
}
var valModStep = (val - scale.domain()[0]) % step,
alignValue = val - valModStep;
if (Math.abs(valModStep) * 2 >= step) {
alignValue += (valModStep > 0) ? step : -step;
}
return alignValue;
}
// Getter/setter functions
slider.min = function(_) {
if (!arguments.length) return min;
min = _;
return slider;
};
slider.max = function(_) {
if (!arguments.length) return max;
max = _;
return slider;
};
slider.step = function(_) {
if (!arguments.length) return step;
step = _;
return slider;
};
slider.animate = function(_) {
if (!arguments.length) return animate;
animate = _;
return slider;
};
slider.orientation = function(_) {
if (!arguments.length) return orientation;
orientation = _;
return slider;
};
slider.axis = function(_) {
if (!arguments.length) return axis;
axis = _;
return slider;
};
slider.margin = function(_) {
if (!arguments.length) return margin;
margin = _;
return slider;
};
slider.value = function(_) {
if (!arguments.length) return value;
if (value) {
moveHandle(stepValue(_));
};
value = _;
return slider;
};
slider.scale = function(_) {
if (!arguments.length) return scale;
scale = _;
return slider;
};
d3.rebind(slider, dispatch, "on");
return slider;
}
}));
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link href="mystyle.css" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="d3.slider.css" />
<link rel="shortcut icon" href="#" />
<title>Bayes' Theorem</title>
</head>
<body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="d3.slider.js"></script>
<div id="sliders1" class="wrapper">
<h2 class="feature">Prior Mean: <span id="pmean"></span></h2>
<div id="pmean_slider"></div>
</div>
<div id="selector1" class="wrapper">
<h2 class="feature1">Precision of Estimate: </h2>
</div>
<div id="sliders2" class="wrapper">
<h2 class="feature">Prior Std. Deviation: <span id="pstd"></span></h2>
<div id="pstd_slider"></div>
</div>
<div id="selector2" class="wrapper">
<h2 class="feature1">Precision of Estimate: </h2>
</div>
<div id="sliders" class="wrapper">
<h2 class="feature">True Mean: <span id="tmean"></span></h2>
<div id="tmean_slider"></div>
<h2 class="feature">True Std. Deviation: <span id="tstd"></span></h2>
<div id="tstd_slider"></div>
</div>
<div id="sliders" class="wrapper">
<h2 class="feature">Number of points: <span id="n"></span></h2>
<div id="n_slider"></div>
</div>
<div id="run" class="button">
<input name="runButton"
type="button"
value="Run Updates"
onclick="run()" />
</div>
<div id="clear" class="button">
<input name="clearButton"
type="button"
value="Clear"
onclick="clearPoints()" />
</div>
<div id="chartId" class="wrapper">
</div>
<a href="url">Blog Post</a>
<script>
var current_pmean = 3;
d3.select('#pmean').text(current_pmean);
var current_mprec = 1;
var current_pstd = 5;
d3.select('#pstd').text(current_pstd);
var current_sprec = 1;
var current_tmean = -2;
d3.select('#tmean').text(current_tmean);
var current_tstd = 3;
d3.select('#tstd').text(current_tstd);
var current_n = 20;
d3.select('#n').text(current_n);
var prec_options = [0,0.5,1,1.5,2.0];
var select1 = d3.select('#selector1').append('select')
.attr('class','select')
.on('change',function(){current_mprec = +d3.select('#selector1').select('select')
.property('value');})
var options1 = select1.selectAll('option').data(prec_options)
.enter().append('option')
.text(function (d) { return d; });
select1.property('value',current_mprec);
var select2 = d3.select('#selector2').append('select')
.attr('class','select')
.on('change',function(){current_sprec = +d3.select('#selector2').select('select')
.property('value');})
var options2 = select2.selectAll('option').data(prec_options)
.enter().append('option')
.text(function (d) { return d; });
select2.property('value',current_sprec);
function gaussian(x,mu,sigma) {
var gaussianConstant = 1 / Math.sqrt(2 * Math.PI),
x = (x - mu) / sigma;
return gaussianConstant * Math.exp(-.5 * x * x) / sigma;
};
function getData(mu,sigma) {
data = [];
for (var i = -1000; i < 1500; i++) {
p = gaussian(i/50,mu,sigma);
el = {
"i": i/50,
"p": p
}
data.push(el);
};
return data;
};
var margin = {
top: 30,
right: 20,
bottom: 40,
left: 50
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.linear()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.linear()
.range(['blue','red']);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line = d3.svg.line()
.x(function(d) {
return x(d.i);
})
.y(function(d) {
return y(d.p);
});
var svg = d3.select("#chartId").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 + ")");
function plot_background(){
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
};
function indexOfMin(arr) {
if (arr.length === 0) {
return -1;
}
var min = 10000;
var minIndex = arr.length-1;
for (var i = 0; i < arr.length; i++) {
if (isFinite(arr[i]) & (arr[i] < min)) {
minIndex = i;
min = arr[i];
}
}
return minIndex;
};
function plot(means,variances,points){
var ind = indexOfMin(variances);
data = getData(means[ind],Math.pow(variances[ind],.5))
var max_p = Math.max(...data.map(function(d) {return d.p;}));
x.domain(d3.extent(data, function(d) {
return d.i;
}));
y.domain(d3.extent(data, function(d) {
return d.p;
}));
color.domain([0,current_n]);
svg.selectAll("g.y.axis")
.call(yAxis);
svg.selectAll("g.x.axis")
.call(xAxis);
svg.append("line")
.style("stroke","black")
.style("stroke-dasharray", ("3, 3"))
.attr("x1", x(current_tmean))
.attr("y1", y(max_p))
.attr("x2", x(current_tmean))
.attr("y2", y(0));
(function() {
// Define a variable
var i = 0,
action = function() {
if (i < means.length) {
if (i > 0){
var circle = svg.append("circle").style("fill",function(d) {return color(i-1);})
.attr('fill-opacity', 0.6)
.attr("cx", x(points[i-1]))
.attr("cy", y(max_p))
.attr("r", 7);
circle
.transition()
.duration(360)
.attr("cy", height);
}
data = getData(means[i],Math.pow(variances[i],.5));
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line)
.attr("id", "graph")
.attr("fill", "none")
.attr("stroke",function(d) {return color(i)})
.attr("stroke-width","2");
i++;
setTimeout(action, 500);
}
};
setTimeout(action, 500);
})();
//console.log(means);
//console.log(variances);
};
plot_background();
// from http://bl.ocks.org/mbostock/4349187
function normal(mean,std) {
var x = 0,
y = 0,
rds, c;
do {
x = Math.random() * 2 - 1;
y = Math.random() * 2 - 1;
rds = x * x + y * y;
} while (rds == 0 || rds > 1);
c = Math.sqrt(-2 * Math.log(rds) / rds); // Box-Muller transform
return mean + std * x * c; // throw away extra sample y * c
};
function run() {
var m0 = current_pmean;
var w0 = current_mprec;
var e0 = Math.pow(current_pstd,2);
var v0 = 1/current_sprec;
var r0 = 2*Math.pow(e0,2)/v0 + 2;
var s0 = 2*e0 * (Math.pow(e0,2)/v0 + 1);
var m0 = [m0];
var w0 = [w0];
var r0 = [r0];
var s0 = [s0];
var ds = [];
var variance = [s0[0]/(r0[0]-2)*(1+1/w0[0])];
var i = 0;
do {
d = normal(current_tmean,current_tstd);
ds.push(d);
m0.push((d + w0[i]*m0[i]) / (1 + w0[i]));
w0.push(w0[i]+1);
r0.push(r0[i]+1);
s0.push(s0[i] + Math.pow(d-m0[i],2) * (1+w0[i]/(1+w0[i])));
variance.push(s0[i]/(r0[i]-2)*(1+1/w0[i]))
i += 1;
}
while (i<current_n);
plot(m0,variance,ds);
};
function clearPoints() {
svg.selectAll("#graph").remove();
svg.selectAll("circle").remove();
svg.selectAll("line").remove();
};
<!-- SLIDERS -->
d3.select('#pmean_slider')
.call(
d3.slider()
.value(current_pmean)
.min(-7.0)
.max(7.0)
.step(0.5)
.axis(true)
.on("slide", function(evt,value) {
d3.select('#pmean').text(value);
current_pmean = value;
}
)
);
d3.select('#pstd_slider')
.call(
d3.slider()
.value(current_pstd)
.min(0.5)
.max(8.0)
.step(0.5)
.axis(true)
.on("slide", function(evt,value) {
d3.select('#pstd').text(value);
current_pstd = value;
}
)
);
d3.select('#tmean_slider')
.call(
d3.slider()
.value(current_tmean)
.min(-7.0)
.max(7.0)
.step(0.5)
.axis(true)
.on("slide", function(evt,value) {
d3.select('#tmean').text(value);
current_tmean = value;
}
)
);
d3.select('#tstd_slider')
.call(
d3.slider()
.value(current_tstd)
.min(0.5)
.max(8.0)
.step(0.5)
.axis(true)
.on("slide", function(evt,value) {
d3.select('#tstd').text(value);
current_tstd = value;
}
)
);
d3.select('#n_slider')
.call(
d3.slider()
.value(current_n)
.min(1.0)
.max(100.0)
.step(1)
.axis(true)
.on("slide", function(evt,value) {
d3.select('#n').text(value);
current_n = value;
}
)
);
</script>
</body>
</html>
#chartId {
margin-left: auto;
margin-right: auto;
display: flex;
}
.feature {
font-size: .9em;
margin: 30px 0 5px 0;
width: 300px;
}
.feature1 {
font-size: .8em;
margin: 5px 0 5px 0;
width: 300px;
}
.wrapper {
width: 800px;
margin-left: auto;
margin-right: auto;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: geometricPrecision;
}
#selector1 {
width: 800px;
margin-left: auto;
margin-right: auto;
margin: 30px 0 0px 0;
padding-left: 300px;
}
#selector2 {
width: 800px;
margin-left: auto;
margin-right: auto;
margin: 30px 0 0px 0;
padding-left: 300px;
}
#run {
margin: 40px 0 0px 0;
padding-left: 850px;
display: flex;
}
#clear {
padding-left: 850px;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment