|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
|
|
<style> |
|
|
|
html, body, svg { |
|
margin: 0; |
|
padding: 0; |
|
width: 100%; |
|
height: 100%; |
|
} |
|
|
|
rect { |
|
fill: none; |
|
stroke: black; |
|
} |
|
|
|
line { |
|
stroke: black; |
|
} |
|
|
|
line.derived { |
|
stroke: #ddd; |
|
/*visibility: hidden;*/ |
|
stroke-dasharray: 2,2; |
|
} |
|
|
|
line.diagonal { |
|
stroke-dasharray: 2,2; |
|
stroke: #ddd; |
|
visibility: hidden; |
|
} |
|
|
|
/*line.diagonal.derived { |
|
visibility: hidden; |
|
}*/ |
|
|
|
text { |
|
text-anchor: middle; |
|
font-family: sans-serif; |
|
} |
|
|
|
text.derived { |
|
fill: #ddd; |
|
} |
|
|
|
</style> |
|
|
|
<body> |
|
|
|
<svg></svg> |
|
|
|
</body> |
|
|
|
<script src="https://d3js.org/d3.v4.min.js"></script> |
|
<script> |
|
|
|
var width = 300, |
|
height = 300, |
|
x = d3.scaleLinear().range([0,width]), |
|
y = d3.scaleLinear().range([0,height]) |
|
|
|
var p_a = Math.random(), |
|
p_b_given_a = Math.random(), |
|
p_b_given_not_a = Math.random(), |
|
p_b, |
|
p_a_given_b, |
|
p_a_given_not_b |
|
|
|
var variables = [ |
|
{ |
|
name: "P(A)", |
|
axis: 0, |
|
side: 0, |
|
level: 1, |
|
derived: false, |
|
derivation: () => (p_b * p_a_given_b) + ((1-p_b) * p_a_given_not_b), |
|
value: function(_) { return arguments.length ? p_a = _ : p_a } |
|
}, |
|
{ |
|
name: "P(B|A)", |
|
axis: 1, |
|
side: 0, |
|
level: 0, |
|
derived: false, |
|
derivation: () => (p_a_given_b * p_b) / p_a, |
|
value: function(_) { return arguments.length ? p_b_given_a = _ : p_b_given_a } |
|
}, |
|
{ |
|
name: "P(B|¬A)", |
|
axis: 1, |
|
side: 1, |
|
level: 0, |
|
derived: false, |
|
derivation: () => ((1-p_a_given_b) * p_b) / (1-p_a), |
|
value: function(_) { return arguments.length ? p_b_given_not_a = _ : p_b_given_not_a } |
|
}, |
|
{ |
|
name: "P(B)", |
|
axis: 1, |
|
side: 0, |
|
level: 1, |
|
derived: true, |
|
derivation: () => (p_a * p_b_given_a) + ((1-p_a) * p_b_given_not_a), |
|
value: function(_) { return arguments.length ? p_b = _ : p_b } |
|
}, |
|
{ |
|
name: "P(A|B)", |
|
axis: 0, |
|
side: 0, |
|
level: 0, |
|
derived: true, |
|
derivation: () => (p_b_given_a * p_a) / p_b, |
|
value: function(_) { return arguments.length ? p_a_given_b = _ : p_a_given_b } |
|
}, |
|
{ |
|
name: "P(A|¬B)", |
|
axis: 0, |
|
side: 1, |
|
level: 0, |
|
derived: true, |
|
derivation: () => ((1-p_b_given_a) * p_a) / (1-p_b), |
|
value: function(_) { return arguments.length ? p_a_given_not_b = _ : p_a_given_not_b } |
|
} |
|
] |
|
|
|
var svg = d3.select("svg") |
|
.append("g") |
|
.attr("transform", `translate(${innerWidth/2 - width/2}, ${innerHeight/2 - height/2})`) |
|
|
|
var p_a_line_diagonal = svg.append("line") |
|
var p_b_line_diagonal = svg.append("line") |
|
|
|
var label = svg.selectAll("text.label") |
|
.data(variables) |
|
.enter() |
|
.append("text") |
|
.classed("label", true) |
|
|
|
var line = svg.selectAll("line.varline") |
|
.data(variables) |
|
.enter() |
|
.append("line") |
|
.classed("varline", true) |
|
|
|
var rect = svg.append("rect") |
|
|
|
function renderLine(selection) { |
|
selection.each(function(d) { |
|
var sel = d3.select(this) |
|
.classed("derived", d.derived) |
|
|
|
if(d.axis===0) { |
|
// P(A), y-axis (horizontal lines, moving along vertical axis) |
|
sel |
|
.attr("x1", x(d.level ? -0.1 : d.side ? p_b : 0)) |
|
.attr("x2", x(d.level ? 1.1 : d.side ? 1 : p_b)) |
|
.attr("y1", y(d.value())) |
|
.attr("y2", y(d.value())) |
|
|
|
} else if(d.axis===1) { |
|
// P(B), x-axis (vertical lines, moving along horizontal axis) |
|
sel |
|
.attr("x1", x(d.value())) |
|
.attr("x2", x(d.value())) |
|
.attr("y1", y(d.level ? -0.1 : d.side ? p_a : 0)) |
|
.attr("y2", y(d.level ? 1.1 : d.side ? 1 : p_a)) |
|
|
|
} |
|
}) |
|
} |
|
|
|
function renderLabel(selection) { |
|
selection.each(function(d) { |
|
var sel = d3.select(this).text(`${d.name} = ${d.value().toFixed(2)}`) |
|
.classed("derived", d.derived) |
|
|
|
if(d.derived) { |
|
sel |
|
.style("cursor", "pointer") |
|
.on("click", () => { |
|
variables.forEach(d => d.derived = !d.derived) |
|
render() |
|
}) |
|
} |
|
|
|
if(d.axis===0) { |
|
// P(A), y-axis (vertical) |
|
sel |
|
.style("text-anchor", d.side ? "start" : "end") |
|
.attr("x", d.side ? x(1) : 0) |
|
.attr("y", y(d.value())) |
|
.attr("dx", (d.side ? 1 : -1) * (2 * d.level + 1) + "em") |
|
.attr("dy", ".25em") |
|
|
|
if(!d.derived) { |
|
sel |
|
.style("cursor", "ns-resize") |
|
.call(d3.drag().on("drag", function(d,i) { |
|
var val = Math.max(Math.min(y.invert(d3.event.y),1),0) |
|
d.value(val) |
|
d3.select(this).attr("y", y(d.value())) |
|
render() |
|
})) |
|
} |
|
|
|
} else if(d.axis===1) { |
|
// P(B), x-axis (horizontal) |
|
sel |
|
.style("text-anchor", "middle") |
|
.attr("x", x(d.value())) |
|
.attr("y", d.side ? y(1) : 0) |
|
.attr("dx", 0) |
|
.attr("dy", .4 + (d.side ? 1 : -1) * (2 * d.level + 1) + "em") |
|
|
|
if(!d.derived) { |
|
sel |
|
.style("cursor", "ew-resize") |
|
.call(d3.drag().on("drag", function(d,i) { |
|
var val = Math.max(Math.min(x.invert(d3.event.x),1),0) |
|
d.value(val) |
|
d3.select(this).attr("x", x(d.value())) |
|
render() |
|
})) |
|
} |
|
} |
|
}) |
|
} |
|
|
|
render() |
|
|
|
function render() { |
|
|
|
label.data().filter(d => d.derived).forEach(d => d.value(d.derivation())) |
|
|
|
// cov(X, Y) = E[XY] - E[X]E[Y] |
|
var covariance = (p_a * p_b_given_a) - (p_a * p_b) |
|
// console.log(covariance) |
|
// ??? |
|
|
|
x = x.range([0,width]) |
|
y = y.range([0,height]) |
|
|
|
rect |
|
.attr("width", width) |
|
.attr("height", height) |
|
|
|
label.call(renderLabel) |
|
line.call(renderLine) |
|
|
|
var lineEqA = getLineFunctionFromPoints( |
|
[p_b / 2, p_a_given_b], |
|
[(p_b + 1)/2, p_a_given_not_b] |
|
) |
|
|
|
var lineEqB = getLineFunctionFromPoints( |
|
[p_a / 2, p_b_given_a], |
|
[(p_a + 1)/2, p_b_given_not_a] |
|
) |
|
|
|
p_a_line_diagonal |
|
.attr("x1", x(0)) |
|
.attr("x2", x(1)) |
|
.attr("y1", y(lineEqA(0))) |
|
.attr("y2", y(lineEqA(1))) |
|
.classed("diagonal", true) |
|
.classed("derived", true) |
|
|
|
p_b_line_diagonal |
|
.attr("x1", x(lineEqB(0))) |
|
.attr("x2", x(lineEqB(1))) |
|
.attr("y1", y(0)) |
|
.attr("y2", y(1)) |
|
.classed("diagonal", true) |
|
} |
|
|
|
function getLineFunctionFromPoints(a,b) { |
|
var m = (b[1]-a[1])/(b[0]-a[0]) |
|
return x => m*(x - a[0]) + a[1] |
|
} |
|
|
|
</script> |