|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<title>Bayes Rule</title> |
|
<style> |
|
|
|
body { |
|
margin: 0; |
|
|
|
font: 13px Palatino, serif; |
|
text-align: center; |
|
} |
|
|
|
main { |
|
display: flex; |
|
justify-content: center; |
|
min-height: 510px; |
|
min-width: 800px; |
|
} |
|
|
|
#details { |
|
margin-top: 20px; |
|
width: 250px; |
|
height: 510px; |
|
|
|
text-align: left; |
|
|
|
display: flex; |
|
flex-direction: column; |
|
} |
|
|
|
#example { |
|
flex: 1; |
|
} |
|
|
|
#example li { |
|
margin-left: -2em; |
|
} |
|
|
|
#example ul div { |
|
margin-left: 0.5em; |
|
} |
|
|
|
address { |
|
font-style: normal; |
|
} |
|
|
|
/** Colors **/ |
|
|
|
.posterior { |
|
color: hsl(120, 95%, 38%); |
|
fill: hsl(120, 95%, 38%); |
|
stroke: hsl(120, 95%, 38%); |
|
} |
|
|
|
.prior { |
|
color: hsl(0, 95%, 38%); |
|
fill: hsl(0, 95%, 38%); |
|
stroke: hsl(0, 95%, 38%); |
|
} |
|
|
|
.sensitivity { |
|
color: hsl(200, 95%, 38%); |
|
fill: hsl(200, 95%, 38%); |
|
stroke: hsl(200, 95%, 38%); |
|
} |
|
|
|
.specificity { |
|
color: hsl(280, 95%, 38%); |
|
fill: hsl(280, 95%, 38%); |
|
stroke: hsl(280, 95%, 38%); |
|
} |
|
|
|
/** Grid **/ |
|
|
|
.grid .tick { |
|
stroke: lightgrey; |
|
} |
|
|
|
.axis path, |
|
.axis line { |
|
fill: none; |
|
stroke: grey; |
|
} |
|
|
|
/** Backing **/ |
|
|
|
.hit-zone { |
|
stroke: none; |
|
fill: none; |
|
pointer-events: visible; |
|
} |
|
|
|
/** Guide **/ |
|
|
|
.guide { |
|
stroke: grey; |
|
stroke-width: 1; |
|
stroke-dasharray: 6, 4; |
|
} |
|
|
|
.guide text { |
|
fill: grey; |
|
stroke: none; |
|
} |
|
|
|
/** Series **/ |
|
|
|
.series path { |
|
fill: none; |
|
stroke-width: 2; |
|
stroke-linecap: round; |
|
} |
|
|
|
.series circle { |
|
stroke: none; |
|
} |
|
|
|
/** Slider **/ |
|
|
|
.slider .track { |
|
fill: none; |
|
stroke-width: 4; |
|
stroke-linecap: round; |
|
} |
|
|
|
.slider .track.maximum { |
|
stroke: lightgrey; |
|
} |
|
|
|
.slider .thumb { |
|
stroke-width: 0.5; |
|
stroke: none; |
|
} |
|
|
|
/** Cursor **/ |
|
|
|
main svg { |
|
cursor: default; |
|
} |
|
|
|
.disable-user-select { |
|
-webkit-touch-callout: none; |
|
-webkit-user-select: none; |
|
-khtml-user-select: none; |
|
-moz-user-select: none; |
|
-ms-user-select: none; |
|
user-select: none; |
|
} |
|
|
|
</style> |
|
|
|
<header> |
|
<h1>Interactive Bayes Rule</h1> |
|
<h2>See how |
|
<span class="posterior">posterior</span> probablity depends on the |
|
<span class="prior">prior</span>, |
|
<span class="sensitivity">sensitivity</span>, and |
|
<span class="specificity">specificity</span>. |
|
</h2> |
|
|
|
<!-- |
|
Equation generated from $P(A|B)=\frac{P(B|A)P(A)}{P(B|A)P(A) + (1 - P(\neg{B}|\neg{A})(1 - P(A))}$ via <http://www.tlhiv.org/ltxpreview/> colored and resized. |
|
--> |
|
<div id="equation"> |
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="774px" height="84px" viewBox="0 0 187 17" version="1.1"> |
|
<defs> |
|
<g> |
|
<symbol overflow="visible" id="glyph0-0"> |
|
<path style="stroke:none;" d="M 3.015625 -3.15625 L 4.71875 -3.15625 C 6.125 -3.15625 7.515625 -4.1875 7.515625 -5.296875 C 7.515625 -6.078125 6.859375 -6.8125 5.546875 -6.8125 L 2.328125 -6.8125 C 2.140625 -6.8125 2.03125 -6.8125 2.03125 -6.625 C 2.03125 -6.5 2.109375 -6.5 2.3125 -6.5 C 2.4375 -6.5 2.625 -6.484375 2.734375 -6.484375 C 2.90625 -6.453125 2.953125 -6.4375 2.953125 -6.3125 C 2.953125 -6.28125 2.953125 -6.25 2.921875 -6.125 L 1.578125 -0.78125 C 1.484375 -0.390625 1.46875 -0.3125 0.671875 -0.3125 C 0.515625 -0.3125 0.40625 -0.3125 0.40625 -0.125 C 0.40625 0 0.515625 0 0.546875 0 C 0.828125 0 1.53125 -0.03125 1.8125 -0.03125 C 2.03125 -0.03125 2.25 -0.015625 2.453125 -0.015625 C 2.671875 -0.015625 2.890625 0 3.09375 0 C 3.171875 0 3.296875 0 3.296875 -0.203125 C 3.296875 -0.3125 3.203125 -0.3125 3.015625 -0.3125 C 2.65625 -0.3125 2.375 -0.3125 2.375 -0.484375 C 2.375 -0.546875 2.390625 -0.59375 2.40625 -0.65625 Z M 3.734375 -6.125 C 3.828125 -6.46875 3.84375 -6.5 4.28125 -6.5 L 5.234375 -6.5 C 6.0625 -6.5 6.59375 -6.234375 6.59375 -5.546875 C 6.59375 -5.15625 6.390625 -4.296875 6 -3.9375 C 5.5 -3.484375 4.90625 -3.40625 4.46875 -3.40625 L 3.0625 -3.40625 Z M 3.734375 -6.125 "/> |
|
</symbol> |
|
<symbol overflow="visible" id="glyph0-1"> |
|
<path style="stroke:none;" d="M 1.78125 -1.140625 C 1.390625 -0.484375 1 -0.34375 0.5625 -0.3125 C 0.4375 -0.296875 0.34375 -0.296875 0.34375 -0.109375 C 0.34375 -0.046875 0.40625 0 0.484375 0 C 0.75 0 1.0625 -0.03125 1.328125 -0.03125 C 1.671875 -0.03125 2.015625 0 2.328125 0 C 2.390625 0 2.515625 0 2.515625 -0.1875 C 2.515625 -0.296875 2.4375 -0.3125 2.359375 -0.3125 C 2.140625 -0.328125 1.890625 -0.40625 1.890625 -0.65625 C 1.890625 -0.78125 1.953125 -0.890625 2.03125 -1.03125 L 2.796875 -2.296875 L 5.296875 -2.296875 C 5.3125 -2.09375 5.453125 -0.734375 5.453125 -0.640625 C 5.453125 -0.34375 4.9375 -0.3125 4.734375 -0.3125 C 4.59375 -0.3125 4.5 -0.3125 4.5 -0.109375 C 4.5 0 4.609375 0 4.640625 0 C 5.046875 0 5.46875 -0.03125 5.875 -0.03125 C 6.125 -0.03125 6.765625 0 7.015625 0 C 7.0625 0 7.1875 0 7.1875 -0.203125 C 7.1875 -0.3125 7.09375 -0.3125 6.953125 -0.3125 C 6.34375 -0.3125 6.34375 -0.375 6.3125 -0.671875 L 5.703125 -6.890625 C 5.6875 -7.09375 5.6875 -7.140625 5.515625 -7.140625 C 5.359375 -7.140625 5.3125 -7.0625 5.25 -6.96875 Z M 2.984375 -2.609375 L 4.9375 -5.90625 L 5.265625 -2.609375 Z M 2.984375 -2.609375 "/> |
|
</symbol> |
|
<symbol overflow="visible" id="glyph0-2"> |
|
<path style="stroke:none;" d="M 1.59375 -0.78125 C 1.5 -0.390625 1.46875 -0.3125 0.6875 -0.3125 C 0.515625 -0.3125 0.421875 -0.3125 0.421875 -0.109375 C 0.421875 0 0.515625 0 0.6875 0 L 4.25 0 C 5.828125 0 7 -1.171875 7 -2.15625 C 7 -2.875 6.421875 -3.453125 5.453125 -3.5625 C 6.484375 -3.75 7.53125 -4.484375 7.53125 -5.4375 C 7.53125 -6.171875 6.875 -6.8125 5.6875 -6.8125 L 2.328125 -6.8125 C 2.140625 -6.8125 2.046875 -6.8125 2.046875 -6.609375 C 2.046875 -6.5 2.140625 -6.5 2.328125 -6.5 C 2.34375 -6.5 2.53125 -6.5 2.703125 -6.484375 C 2.875 -6.453125 2.96875 -6.453125 2.96875 -6.3125 C 2.96875 -6.28125 2.953125 -6.25 2.9375 -6.125 Z M 3.09375 -3.65625 L 3.71875 -6.125 C 3.8125 -6.46875 3.828125 -6.5 4.25 -6.5 L 5.546875 -6.5 C 6.421875 -6.5 6.625 -5.90625 6.625 -5.46875 C 6.625 -4.59375 5.765625 -3.65625 4.5625 -3.65625 Z M 2.65625 -0.3125 C 2.515625 -0.3125 2.5 -0.3125 2.4375 -0.3125 C 2.328125 -0.328125 2.296875 -0.34375 2.296875 -0.421875 C 2.296875 -0.453125 2.296875 -0.46875 2.359375 -0.640625 L 3.046875 -3.421875 L 4.921875 -3.421875 C 5.875 -3.421875 6.078125 -2.6875 6.078125 -2.265625 C 6.078125 -1.28125 5.1875 -0.3125 4 -0.3125 Z M 2.65625 -0.3125 "/> |
|
</symbol> |
|
<symbol overflow="visible" id="glyph1-0"> |
|
<path style="stroke:none;" d="M 3.296875 2.390625 C 3.296875 2.359375 3.296875 2.34375 3.125 2.171875 C 1.890625 0.921875 1.5625 -0.96875 1.5625 -2.5 C 1.5625 -4.234375 1.9375 -5.96875 3.171875 -7.203125 C 3.296875 -7.328125 3.296875 -7.34375 3.296875 -7.375 C 3.296875 -7.453125 3.265625 -7.484375 3.203125 -7.484375 C 3.09375 -7.484375 2.203125 -6.796875 1.609375 -5.53125 C 1.109375 -4.4375 0.984375 -3.328125 0.984375 -2.5 C 0.984375 -1.71875 1.09375 -0.515625 1.640625 0.625 C 2.25 1.84375 3.09375 2.5 3.203125 2.5 C 3.265625 2.5 3.296875 2.46875 3.296875 2.390625 Z M 3.296875 2.390625 "/> |
|
</symbol> |
|
<symbol overflow="visible" id="glyph1-1"> |
|
<path style="stroke:none;" d="M 2.875 -2.5 C 2.875 -3.265625 2.765625 -4.46875 2.21875 -5.609375 C 1.625 -6.828125 0.765625 -7.484375 0.671875 -7.484375 C 0.609375 -7.484375 0.5625 -7.4375 0.5625 -7.375 C 0.5625 -7.34375 0.5625 -7.328125 0.75 -7.140625 C 1.734375 -6.15625 2.296875 -4.578125 2.296875 -2.5 C 2.296875 -0.78125 1.9375 0.96875 0.703125 2.21875 C 0.5625 2.34375 0.5625 2.359375 0.5625 2.390625 C 0.5625 2.453125 0.609375 2.5 0.671875 2.5 C 0.765625 2.5 1.671875 1.8125 2.25 0.546875 C 2.765625 -0.546875 2.875 -1.65625 2.875 -2.5 Z M 2.875 -2.5 "/> |
|
</symbol> |
|
<symbol overflow="visible" id="glyph1-2"> |
|
<path style="stroke:none;" d="M 6.84375 -3.265625 C 7 -3.265625 7.1875 -3.265625 7.1875 -3.453125 C 7.1875 -3.65625 7 -3.65625 6.859375 -3.65625 L 0.890625 -3.65625 C 0.75 -3.65625 0.5625 -3.65625 0.5625 -3.453125 C 0.5625 -3.265625 0.75 -3.265625 0.890625 -3.265625 Z M 6.859375 -1.328125 C 7 -1.328125 7.1875 -1.328125 7.1875 -1.53125 C 7.1875 -1.71875 7 -1.71875 6.84375 -1.71875 L 0.890625 -1.71875 C 0.75 -1.71875 0.5625 -1.71875 0.5625 -1.53125 C 0.5625 -1.328125 0.75 -1.328125 0.890625 -1.328125 Z M 6.859375 -1.328125 "/> |
|
</symbol> |
|
<symbol overflow="visible" id="glyph2-0"> |
|
<path style="stroke:none;" d="M 1.578125 -7.125 C 1.578125 -7.296875 1.578125 -7.484375 1.390625 -7.484375 C 1.1875 -7.484375 1.1875 -7.296875 1.1875 -7.125 L 1.1875 2.140625 C 1.1875 2.3125 1.1875 2.5 1.390625 2.5 C 1.578125 2.5 1.578125 2.3125 1.578125 2.140625 Z M 1.578125 -7.125 "/> |
|
</symbol> |
|
<symbol overflow="visible" id="glyph3-0"> |
|
<path style="stroke:none;" d="M 2.375 -2.15625 L 3.640625 -2.15625 C 4.8125 -2.15625 5.8125 -2.90625 5.8125 -3.671875 C 5.8125 -4.265625 5.234375 -4.765625 4.28125 -4.765625 L 1.84375 -4.765625 C 1.703125 -4.765625 1.625 -4.765625 1.625 -4.609375 C 1.625 -4.515625 1.703125 -4.515625 1.859375 -4.515625 C 1.96875 -4.515625 2 -4.515625 2.125 -4.5 C 2.265625 -4.484375 2.265625 -4.46875 2.265625 -4.390625 C 2.265625 -4.390625 2.265625 -4.34375 2.25 -4.25 L 1.328125 -0.578125 C 1.25 -0.296875 1.25 -0.25 0.703125 -0.25 C 0.578125 -0.25 0.5 -0.25 0.5 -0.09375 C 0.5 -0.09375 0.5 0 0.609375 0 C 0.8125 0 1.3125 -0.03125 1.515625 -0.03125 C 1.625 -0.03125 1.875 -0.03125 2 -0.015625 C 2.125 -0.015625 2.3125 0 2.4375 0 C 2.484375 0 2.578125 0 2.578125 -0.15625 C 2.578125 -0.25 2.5 -0.25 2.359375 -0.25 C 2.359375 -0.25 2.21875 -0.25 2.09375 -0.265625 C 1.9375 -0.28125 1.9375 -0.296875 1.9375 -0.375 C 1.9375 -0.375 1.9375 -0.421875 1.96875 -0.515625 Z M 2.875 -4.265625 C 2.9375 -4.484375 2.9375 -4.515625 3.234375 -4.515625 L 4.046875 -4.515625 C 4.703125 -4.515625 5.09375 -4.3125 5.09375 -3.8125 C 5.09375 -3.609375 5.015625 -3.046875 4.671875 -2.734375 C 4.421875 -2.515625 4.015625 -2.390625 3.515625 -2.390625 L 2.40625 -2.390625 Z M 2.875 -4.265625 "/> |
|
</symbol> |
|
<symbol overflow="visible" id="glyph3-1"> |
|
<path style="stroke:none;" d="M 1.328125 -0.546875 C 1.265625 -0.3125 1.25 -0.25 0.703125 -0.25 C 0.59375 -0.25 0.5 -0.25 0.5 -0.109375 C 0.5 0 0.578125 0 0.703125 0 L 3.40625 0 C 4.578125 0 5.484375 -0.796875 5.484375 -1.484375 C 5.484375 -1.984375 5.03125 -2.40625 4.28125 -2.484375 C 5.125 -2.640625 5.828125 -3.171875 5.828125 -3.765625 C 5.828125 -4.296875 5.296875 -4.765625 4.390625 -4.765625 L 1.859375 -4.765625 C 1.71875 -4.765625 1.625 -4.765625 1.625 -4.609375 C 1.625 -4.515625 1.71875 -4.515625 1.859375 -4.515625 C 1.859375 -4.515625 2 -4.515625 2.125 -4.5 C 2.265625 -4.484375 2.28125 -4.46875 2.28125 -4.390625 C 2.28125 -4.390625 2.28125 -4.34375 2.25 -4.25 Z M 2.4375 -2.578125 L 2.859375 -4.265625 C 2.921875 -4.484375 2.921875 -4.515625 3.21875 -4.515625 L 4.28125 -4.515625 C 4.984375 -4.515625 5.15625 -4.046875 5.15625 -3.78125 C 5.15625 -3.21875 4.515625 -2.578125 3.578125 -2.578125 Z M 2.109375 -0.25 C 1.890625 -0.25 1.875 -0.265625 1.875 -0.328125 C 1.875 -0.328125 1.875 -0.359375 1.90625 -0.46875 L 2.390625 -2.375 L 3.875 -2.375 C 4.515625 -2.375 4.78125 -1.9375 4.78125 -1.546875 C 4.78125 -0.84375 4.0625 -0.25 3.21875 -0.25 Z M 2.109375 -0.25 "/> |
|
</symbol> |
|
<symbol overflow="visible" id="glyph3-2"> |
|
<path style="stroke:none;" d="M 1.4375 -0.84375 C 1.1875 -0.453125 0.96875 -0.28125 0.5625 -0.25 C 0.484375 -0.25 0.390625 -0.25 0.390625 -0.109375 C 0.390625 -0.03125 0.453125 0 0.5 0 C 0.671875 0 0.90625 -0.03125 1.09375 -0.03125 C 1.3125 -0.03125 1.609375 0 1.8125 0 C 1.84375 0 1.953125 0 1.953125 -0.15625 C 1.953125 -0.25 1.859375 -0.25 1.828125 -0.25 C 1.78125 -0.265625 1.53125 -0.265625 1.53125 -0.453125 C 1.53125 -0.546875 1.59375 -0.65625 1.625 -0.71875 L 2.1875 -1.59375 L 4.1875 -1.59375 L 4.34375 -0.4375 C 4.328125 -0.359375 4.28125 -0.25 3.875 -0.25 C 3.78125 -0.25 3.6875 -0.25 3.6875 -0.09375 C 3.6875 -0.0625 3.703125 0 3.796875 0 C 4 0 4.5 -0.03125 4.703125 -0.03125 C 4.828125 -0.03125 4.984375 -0.015625 5.109375 -0.015625 C 5.234375 -0.015625 5.375 0 5.5 0 C 5.59375 0 5.640625 -0.0625 5.640625 -0.140625 C 5.640625 -0.25 5.5625 -0.25 5.453125 -0.25 C 5.046875 -0.25 5.03125 -0.3125 5.015625 -0.46875 L 4.390625 -4.78125 C 4.375 -4.921875 4.359375 -4.96875 4.234375 -4.96875 C 4.09375 -4.96875 4.0625 -4.90625 4 -4.8125 Z M 2.359375 -1.84375 L 3.8125 -4.125 L 4.140625 -1.84375 Z M 2.359375 -1.84375 "/> |
|
</symbol> |
|
<symbol overflow="visible" id="glyph4-0"> |
|
<path style="stroke:none;" d="M 2.46875 -5.21875 C 1.15625 -4.296875 0.796875 -2.8125 0.796875 -1.75 C 0.796875 -0.765625 1.09375 0.765625 2.46875 1.734375 C 2.53125 1.734375 2.609375 1.734375 2.609375 1.65625 C 2.609375 1.609375 2.59375 1.59375 2.546875 1.546875 C 1.609375 0.703125 1.28125 -0.46875 1.28125 -1.734375 C 1.28125 -3.625 2 -4.546875 2.5625 -5.0625 C 2.59375 -5.09375 2.609375 -5.109375 2.609375 -5.140625 C 2.609375 -5.21875 2.53125 -5.21875 2.46875 -5.21875 Z M 2.46875 -5.21875 "/> |
|
</symbol> |
|
<symbol overflow="visible" id="glyph4-1"> |
|
<path style="stroke:none;" d="M 0.625 -5.21875 C 0.578125 -5.21875 0.5 -5.21875 0.5 -5.140625 C 0.5 -5.109375 0.515625 -5.09375 0.5625 -5.03125 C 1.15625 -4.484375 1.828125 -3.546875 1.828125 -1.75 C 1.828125 -0.296875 1.375 0.8125 0.625 1.484375 C 0.5 1.609375 0.5 1.609375 0.5 1.65625 C 0.5 1.6875 0.515625 1.734375 0.578125 1.734375 C 0.671875 1.734375 1.328125 1.28125 1.796875 0.40625 C 2.09375 -0.171875 2.296875 -0.921875 2.296875 -1.734375 C 2.296875 -2.71875 2 -4.25 0.625 -5.21875 Z M 0.625 -5.21875 "/> |
|
</symbol> |
|
<symbol overflow="visible" id="glyph4-2"> |
|
<path style="stroke:none;" d="M 3.21875 -1.578125 L 5.359375 -1.578125 C 5.453125 -1.578125 5.609375 -1.578125 5.609375 -1.734375 C 5.609375 -1.921875 5.453125 -1.921875 5.359375 -1.921875 L 3.21875 -1.921875 L 3.21875 -4.0625 C 3.21875 -4.140625 3.21875 -4.3125 3.0625 -4.3125 C 2.890625 -4.3125 2.890625 -4.15625 2.890625 -4.0625 L 2.890625 -1.921875 L 0.75 -1.921875 C 0.65625 -1.921875 0.484375 -1.921875 0.484375 -1.75 C 0.484375 -1.578125 0.640625 -1.578125 0.75 -1.578125 L 2.890625 -1.578125 L 2.890625 0.5625 C 2.890625 0.65625 2.890625 0.828125 3.046875 0.828125 C 3.21875 0.828125 3.21875 0.65625 3.21875 0.5625 Z M 3.21875 -1.578125 "/> |
|
</symbol> |
|
<symbol overflow="visible" id="glyph4-3"> |
|
<path style="stroke:none;" d="M 2.328125 -4.4375 C 2.328125 -4.625 2.328125 -4.625 2.125 -4.625 C 1.671875 -4.1875 1.046875 -4.1875 0.765625 -4.1875 L 0.765625 -3.9375 C 0.921875 -3.9375 1.390625 -3.9375 1.765625 -4.125 L 1.765625 -0.578125 C 1.765625 -0.34375 1.765625 -0.25 1.078125 -0.25 L 0.8125 -0.25 L 0.8125 0 C 0.9375 0 1.796875 -0.03125 2.046875 -0.03125 C 2.265625 -0.03125 3.140625 0 3.296875 0 L 3.296875 -0.25 L 3.03125 -0.25 C 2.328125 -0.25 2.328125 -0.34375 2.328125 -0.578125 Z M 2.328125 -4.4375 "/> |
|
</symbol> |
|
<symbol overflow="visible" id="glyph5-0"> |
|
<path style="stroke:none;" d="M 1.359375 -4.953125 C 1.359375 -5.0625 1.359375 -5.21875 1.1875 -5.21875 C 1.015625 -5.21875 1.015625 -5.0625 1.015625 -4.953125 L 1.015625 1.46875 C 1.015625 1.578125 1.015625 1.734375 1.171875 1.734375 C 1.359375 1.734375 1.359375 1.578125 1.359375 1.46875 Z M 1.359375 -4.953125 "/> |
|
</symbol> |
|
<symbol overflow="visible" id="glyph5-1"> |
|
<path style="stroke:none;" d="M 5.1875 -1.578125 C 5.296875 -1.578125 5.46875 -1.578125 5.46875 -1.734375 C 5.46875 -1.921875 5.296875 -1.921875 5.1875 -1.921875 L 1.03125 -1.921875 C 0.921875 -1.921875 0.75 -1.921875 0.75 -1.75 C 0.75 -1.578125 0.90625 -1.578125 1.03125 -1.578125 Z M 5.1875 -1.578125 "/> |
|
</symbol> |
|
<symbol overflow="visible" id="glyph5-2"> |
|
<path style="stroke:none;" d="M 4.828125 -2.25 C 4.828125 -2.484375 4.78125 -2.515625 4.546875 -2.515625 L 0.8125 -2.515625 C 0.703125 -2.515625 0.53125 -2.515625 0.53125 -2.34375 C 0.53125 -2.171875 0.703125 -2.171875 0.8125 -2.171875 L 4.484375 -2.171875 L 4.484375 -0.859375 C 4.484375 -0.765625 4.484375 -0.59375 4.640625 -0.59375 C 4.828125 -0.59375 4.828125 -0.75 4.828125 -0.859375 Z M 4.828125 -2.25 "/> |
|
</symbol> |
|
</g> |
|
</defs> |
|
<g id="surface1"> |
|
<g class="posterior"> |
|
<use xlink:href="#glyph0-0" x="-0.283984" y="10.86484"/> |
|
</g> |
|
<g class="posterior"> |
|
<use xlink:href="#glyph1-0" x="7.501176" y="10.86484"/> |
|
</g> |
|
<g class="posterior"> |
|
<use xlink:href="#glyph0-1" x="11.371096" y="10.86484"/> |
|
</g> |
|
<g class="posterior"> |
|
<use xlink:href="#glyph2-0" x="18.841016" y="10.86484"/> |
|
</g> |
|
<g class="posterior"> |
|
<use xlink:href="#glyph0-2" x="21.603906" y="10.86484"/> |
|
</g> |
|
<g class="posterior"> |
|
<use xlink:href="#glyph1-1" x="29.667966" y="10.86484"/> |
|
</g> |
|
<g> |
|
<use xlink:href="#glyph1-2" x="36.300783" y="10.86484"/> |
|
</g> |
|
<g class="sensitivity"> |
|
<use xlink:href="#glyph3-0" x="94.035966" y="6.03203"/> |
|
</g> |
|
<g class="sensitivity"> |
|
<use xlink:href="#glyph4-0" x="100.182836" y="6.03203"/> |
|
</g> |
|
<g class="sensitivity"> |
|
<use xlink:href="#glyph3-1" x="103.296896" y="6.03203"/> |
|
</g> |
|
<g class="sensitivity"> |
|
<use xlink:href="#glyph5-0" x="109.659786" y="6.03203"/> |
|
</g> |
|
<g class="sensitivity"> |
|
<use xlink:href="#glyph3-2" x="112.026976" y="6.03203"/> |
|
</g> |
|
<g class="sensitivity"> |
|
<use xlink:href="#glyph4-1" x="118.021116" y="6.03203"/> |
|
</g> |
|
<g class="prior"> |
|
<use xlink:href="#glyph3-0" x="121.135176" y="6.03203"/> |
|
</g> |
|
<g class="prior"> |
|
<use xlink:href="#glyph4-0" x="127.282046" y="6.03203"/> |
|
</g> |
|
<g class="prior"> |
|
<use xlink:href="#glyph3-2" x="130.396106" y="6.03203"/> |
|
</g> |
|
<g class="prior"> |
|
<use xlink:href="#glyph4-1" x="136.390246" y="6.03203"/> |
|
</g> |
|
<path style="fill:none;stroke-width:4.05;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 480.195312 86.328125 L 1855.117188 86.328125 " transform="matrix(0.1,0,0,-0.1,0,17)"/> |
|
<g class="sensitivity"> |
|
<use xlink:href="#glyph3-0" x="48.0191" y="14.30312"/> |
|
</g> |
|
<g class="sensitivity"> |
|
<use xlink:href="#glyph4-0" x="54.16597" y="14.30312"/> |
|
</g> |
|
<g class="sensitivity"> |
|
<use xlink:href="#glyph3-1" x="57.28003" y="14.30312"/> |
|
</g> |
|
<g class="sensitivity"> |
|
<use xlink:href="#glyph5-0" x="63.64292" y="14.30312"/> |
|
</g> |
|
<g class="sensitivity"> |
|
<use xlink:href="#glyph3-2" x="66.01011" y="14.30312"/> |
|
</g> |
|
<g class="sensitivity"> |
|
<use xlink:href="#glyph4-1" x="72.00425" y="14.30312"/> |
|
</g> |
|
<g class="prior"> |
|
<use xlink:href="#glyph3-0" x="75.11831" y="14.30312"/> |
|
</g> |
|
<g class="prior"> |
|
<use xlink:href="#glyph4-0" x="81.26518" y="14.30312"/> |
|
</g> |
|
<g class="prior"> |
|
<use xlink:href="#glyph3-2" x="84.37924" y="14.30312"/> |
|
</g> |
|
<g class="prior"> |
|
<use xlink:href="#glyph4-1" x="90.37338" y="14.30312"/> |
|
</g> |
|
<g> |
|
<use xlink:href="#glyph4-2" x="93.487385" y="14.30312"/> |
|
</g> |
|
<g> |
|
<use xlink:href="#glyph4-0" x="99.607382" y="14.30312"/> |
|
</g> |
|
<g> |
|
<use xlink:href="#glyph4-3" x="102.721382" y="14.30312"/> |
|
</g> |
|
<g> |
|
<use xlink:href="#glyph5-1" x="106.69058" y="14.30312"/> |
|
</g> |
|
<g class="specificity"> |
|
<use xlink:href="#glyph3-0" x="112.91871" y="14.30312"/> |
|
</g> |
|
<g class="specificity"> |
|
<use xlink:href="#glyph4-0" x="119.06558" y="14.30312"/> |
|
</g> |
|
<g class="specificity"> |
|
<use xlink:href="#glyph5-2" x="122.17964" y="14.30312"/> |
|
</g> |
|
<g class="specificity"> |
|
<use xlink:href="#glyph3-1" x="127.55269" y="14.30312"/> |
|
</g> |
|
<g class="specificity"> |
|
<use xlink:href="#glyph5-0" x="133.91558" y="14.30312"/> |
|
</g> |
|
<g class="specificity"> |
|
<use xlink:href="#glyph5-2" x="136.282584" y="14.30312"/> |
|
</g> |
|
<g class="specificity"> |
|
<use xlink:href="#glyph3-2" x="141.65542" y="14.30312"/> |
|
</g> |
|
<g class="specificity"> |
|
<use xlink:href="#glyph4-1" x="147.64956" y="14.30312"/> |
|
</g> |
|
<g> |
|
<use xlink:href="#glyph4-0" x="150.763559" y="14.30312"/> |
|
</g> |
|
<g> |
|
<use xlink:href="#glyph4-3" x="153.877558" y="14.30312"/> |
|
</g> |
|
<g> |
|
<use xlink:href="#glyph5-1" x="157.84646" y="14.30312"/> |
|
</g> |
|
<g class="prior"> |
|
<use xlink:href="#glyph3-0" x="164.07459" y="14.30312"/> |
|
</g> |
|
<g class="prior"> |
|
<use xlink:href="#glyph4-0" x="170.22146" y="14.30312"/> |
|
</g> |
|
<g class="prior"> |
|
<use xlink:href="#glyph3-2" x="173.33552" y="14.30312"/> |
|
</g> |
|
<g class="prior"> |
|
<use xlink:href="#glyph4-1" x="179.32966" y="14.30312"/> |
|
</g> |
|
<g> |
|
<use xlink:href="#glyph4-1" x="182.443659" y="14.30312"/> |
|
</g> |
|
</g> |
|
</svg> |
|
</div> |
|
|
|
</header> |
|
<main> |
|
<script> |
|
// Directly include the 10% of d3 <http://d3js.org/> that we use. |
|
d3 = function() { |
|
var d3 = { |
|
version: "3.3.11" |
|
}; |
|
var d3_document = document, d3_documentElement = d3_document.documentElement, d3_window = window; |
|
try { |
|
d3_array(d3_documentElement.childNodes)[0].nodeType; |
|
} catch (e) { |
|
d3_array = function(list) { |
|
var i = list.length, array = new Array(i); |
|
while (i--) array[i] = list[i]; |
|
return array; |
|
}; |
|
} |
|
try { |
|
d3_document.createElement("div").style.setProperty("opacity", 0, ""); |
|
} catch (error) { |
|
var d3_element_prototype = d3_window.Element.prototype, d3_element_setAttribute = d3_element_prototype.setAttribute, d3_element_setAttributeNS = d3_element_prototype.setAttributeNS, d3_style_prototype = d3_window.CSSStyleDeclaration.prototype, d3_style_setProperty = d3_style_prototype.setProperty; |
|
d3_element_prototype.setAttribute = function(name, value) { |
|
d3_element_setAttribute.call(this, name, value + ""); |
|
}; |
|
d3_element_prototype.setAttributeNS = function(space, local, value) { |
|
d3_element_setAttributeNS.call(this, space, local, value + ""); |
|
}; |
|
d3_style_prototype.setProperty = function(name, value, priority) { |
|
d3_style_setProperty.call(this, name, value + "", priority); |
|
}; |
|
} |
|
var abs = Math.abs; |
|
d3.range = function(start, stop, step) { |
|
if (arguments.length < 3) { |
|
step = 1; |
|
if (arguments.length < 2) { |
|
stop = start; |
|
start = 0; |
|
} |
|
} |
|
if ((stop - start) / step === Infinity) throw new Error("infinite range"); |
|
var range = [], k = d3_range_integerScale(abs(step)), i = -1, j; |
|
start *= k, stop *= k, step *= k; |
|
if (step < 0) while ((j = start + step * ++i) > stop) range.push(j / k); else while ((j = start + step * ++i) < stop) range.push(j / k); |
|
return range; |
|
}; |
|
function d3_range_integerScale(x) { |
|
var k = 1; |
|
while (x * k % 1) k *= 10; |
|
return k; |
|
} |
|
function d3_class(ctor, properties) { |
|
try { |
|
for (var key in properties) { |
|
Object.defineProperty(ctor.prototype, key, { |
|
value: properties[key], |
|
enumerable: false |
|
}); |
|
} |
|
} catch (e) { |
|
ctor.prototype = properties; |
|
} |
|
} |
|
d3.map = function(object) { |
|
var map = new d3_Map(); |
|
if (object instanceof d3_Map) object.forEach(function(key, value) { |
|
map.set(key, value); |
|
}); else for (var key in object) map.set(key, object[key]); |
|
return map; |
|
}; |
|
function d3_Map() {} |
|
d3_class(d3_Map, { |
|
has: function(key) { |
|
return d3_map_prefix + key in this; |
|
}, |
|
get: function(key) { |
|
return this[d3_map_prefix + key]; |
|
}, |
|
set: function(key, value) { |
|
return this[d3_map_prefix + key] = value; |
|
}, |
|
remove: function(key) { |
|
key = d3_map_prefix + key; |
|
return key in this && delete this[key]; |
|
}, |
|
forEach: function(f) { |
|
for (var key in this) { |
|
if (key.charCodeAt(0) === d3_map_prefixCode) { |
|
f.call(this, key.substring(1), this[key]); |
|
} |
|
} |
|
} |
|
}); |
|
var d3_map_prefix = "\x00", d3_map_prefixCode = d3_map_prefix.charCodeAt(0); |
|
function d3_vendorSymbol(object, name) { |
|
if (name in object) return name; |
|
name = name.charAt(0).toUpperCase() + name.substring(1); |
|
for (var i = 0, n = d3_vendorPrefixes.length; i < n; ++i) { |
|
var prefixName = d3_vendorPrefixes[i] + name; |
|
if (prefixName in object) return prefixName; |
|
} |
|
} |
|
var d3_vendorPrefixes = [ "webkit", "ms", "moz", "Moz", "o", "O" ]; |
|
d3.event = null; |
|
function d3_eventPreventDefault() { |
|
d3.event.preventDefault(); |
|
} |
|
function d3_eventSource() { |
|
var e = d3.event, s; |
|
while (s = e.sourceEvent) e = s; |
|
return e; |
|
} |
|
d3.requote = function(s) { |
|
return s.replace(d3_requote_re, "\\$&"); |
|
}; |
|
var d3_requote_re = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g; |
|
var d3_subclass = {}.__proto__ ? function(object, prototype) { |
|
object.__proto__ = prototype; |
|
} : function(object, prototype) { |
|
for (var property in prototype) object[property] = prototype[property]; |
|
}; |
|
function d3_selection(groups) { |
|
d3_subclass(groups, d3_selectionPrototype); |
|
return groups; |
|
} |
|
var d3_select = function(s, n) { |
|
return n.querySelector(s); |
|
}, d3_selectAll = function(s, n) { |
|
return n.querySelectorAll(s); |
|
}, d3_selectMatcher = d3_documentElement[d3_vendorSymbol(d3_documentElement, "matchesSelector")], d3_selectMatches = function(n, s) { |
|
return d3_selectMatcher.call(n, s); |
|
}; |
|
if (typeof Sizzle === "function") { |
|
d3_select = function(s, n) { |
|
return Sizzle(s, n)[0] || null; |
|
}; |
|
d3_selectAll = function(s, n) { |
|
return Sizzle.uniqueSort(Sizzle(s, n)); |
|
}; |
|
d3_selectMatches = Sizzle.matchesSelector; |
|
} |
|
d3.selection = function() { |
|
return d3_selectionRoot; |
|
}; |
|
var d3_selectionPrototype = d3.selection.prototype = []; |
|
d3_selectionPrototype.select = function(selector) { |
|
var subgroups = [], subgroup, subnode, group, node; |
|
selector = d3_selection_selector(selector); |
|
for (var j = -1, m = this.length; ++j < m; ) { |
|
subgroups.push(subgroup = []); |
|
subgroup.parentNode = (group = this[j]).parentNode; |
|
for (var i = -1, n = group.length; ++i < n; ) { |
|
if (node = group[i]) { |
|
subgroup.push(subnode = selector.call(node, node.__data__, i, j)); |
|
if (subnode && "__data__" in node) subnode.__data__ = node.__data__; |
|
} else { |
|
subgroup.push(null); |
|
} |
|
} |
|
} |
|
return d3_selection(subgroups); |
|
}; |
|
function d3_selection_selector(selector) { |
|
return typeof selector === "function" ? selector : function() { |
|
return d3_select(selector, this); |
|
}; |
|
} |
|
d3_selectionPrototype.selectAll = function(selector) { |
|
var subgroups = [], subgroup, node; |
|
selector = d3_selection_selectorAll(selector); |
|
for (var j = -1, m = this.length; ++j < m; ) { |
|
for (var group = this[j], i = -1, n = group.length; ++i < n; ) { |
|
if (node = group[i]) { |
|
subgroups.push(subgroup = d3_array(selector.call(node, node.__data__, i, j))); |
|
subgroup.parentNode = node; |
|
} |
|
} |
|
} |
|
return d3_selection(subgroups); |
|
}; |
|
function d3_selection_selectorAll(selector) { |
|
return typeof selector === "function" ? selector : function() { |
|
return d3_selectAll(selector, this); |
|
}; |
|
} |
|
var d3_nsPrefix = { |
|
svg: "http://www.w3.org/2000/svg", |
|
xhtml: "http://www.w3.org/1999/xhtml", |
|
xlink: "http://www.w3.org/1999/xlink", |
|
xml: "http://www.w3.org/XML/1998/namespace", |
|
xmlns: "http://www.w3.org/2000/xmlns/" |
|
}; |
|
d3.ns = { |
|
prefix: d3_nsPrefix, |
|
qualify: function(name) { |
|
var i = name.indexOf(":"), prefix = name; |
|
if (i >= 0) { |
|
prefix = name.substring(0, i); |
|
name = name.substring(i + 1); |
|
} |
|
return d3_nsPrefix.hasOwnProperty(prefix) ? { |
|
space: d3_nsPrefix[prefix], |
|
local: name |
|
} : name; |
|
} |
|
}; |
|
d3_selectionPrototype.attr = function(name, value) { |
|
if (arguments.length < 2) { |
|
if (typeof name === "string") { |
|
var node = this.node(); |
|
name = d3.ns.qualify(name); |
|
return name.local ? node.getAttributeNS(name.space, name.local) : node.getAttribute(name); |
|
} |
|
for (value in name) this.each(d3_selection_attr(value, name[value])); |
|
return this; |
|
} |
|
return this.each(d3_selection_attr(name, value)); |
|
}; |
|
function d3_selection_attr(name, value) { |
|
name = d3.ns.qualify(name); |
|
function attrNull() { |
|
this.removeAttribute(name); |
|
} |
|
function attrNullNS() { |
|
this.removeAttributeNS(name.space, name.local); |
|
} |
|
function attrConstant() { |
|
this.setAttribute(name, value); |
|
} |
|
function attrConstantNS() { |
|
this.setAttributeNS(name.space, name.local, value); |
|
} |
|
function attrFunction() { |
|
var x = value.apply(this, arguments); |
|
if (x == null) this.removeAttribute(name); else this.setAttribute(name, x); |
|
} |
|
function attrFunctionNS() { |
|
var x = value.apply(this, arguments); |
|
if (x == null) this.removeAttributeNS(name.space, name.local); else this.setAttributeNS(name.space, name.local, x); |
|
} |
|
return value == null ? name.local ? attrNullNS : attrNull : typeof value === "function" ? name.local ? attrFunctionNS : attrFunction : name.local ? attrConstantNS : attrConstant; |
|
} |
|
d3_selectionPrototype.classed = function(name, value) { |
|
if (arguments.length < 2) { |
|
if (typeof name === "string") { |
|
var node = this.node(), n = (name = name.trim().split(/^|\s+/g)).length, i = -1; |
|
if (value = node.classList) { |
|
while (++i < n) if (!value.contains(name[i])) return false; |
|
} else { |
|
value = node.getAttribute("class"); |
|
while (++i < n) if (!d3_selection_classedRe(name[i]).test(value)) return false; |
|
} |
|
return true; |
|
} |
|
for (value in name) this.each(d3_selection_classed(value, name[value])); |
|
return this; |
|
} |
|
return this.each(d3_selection_classed(name, value)); |
|
}; |
|
function d3_selection_classedRe(name) { |
|
return new RegExp("(?:^|\\s+)" + d3.requote(name) + "(?:\\s+|$)", "g"); |
|
} |
|
function d3_selection_classed(name, value) { |
|
name = name.trim().split(/\s+/).map(d3_selection_classedName); |
|
var n = name.length; |
|
function classedConstant() { |
|
var i = -1; |
|
while (++i < n) name[i](this, value); |
|
} |
|
function classedFunction() { |
|
var i = -1, x = value.apply(this, arguments); |
|
while (++i < n) name[i](this, x); |
|
} |
|
return typeof value === "function" ? classedFunction : classedConstant; |
|
} |
|
function d3_selection_classedName(name) { |
|
var re = d3_selection_classedRe(name); |
|
return function(node, value) { |
|
if (c = node.classList) return value ? c.add(name) : c.remove(name); |
|
var c = node.getAttribute("class") || ""; |
|
if (value) { |
|
re.lastIndex = 0; |
|
if (!re.test(c)) node.setAttribute("class", d3_collapse(c + " " + name)); |
|
} else { |
|
node.setAttribute("class", d3_collapse(c.replace(re, " "))); |
|
} |
|
}; |
|
} |
|
d3_selectionPrototype.style = function(name, value, priority) { |
|
var n = arguments.length; |
|
if (n < 3) { |
|
if (typeof name !== "string") { |
|
if (n < 2) value = ""; |
|
for (priority in name) this.each(d3_selection_style(priority, name[priority], value)); |
|
return this; |
|
} |
|
if (n < 2) return d3_window.getComputedStyle(this.node(), null).getPropertyValue(name); |
|
priority = ""; |
|
} |
|
return this.each(d3_selection_style(name, value, priority)); |
|
}; |
|
function d3_selection_style(name, value, priority) { |
|
function styleNull() { |
|
this.style.removeProperty(name); |
|
} |
|
function styleConstant() { |
|
this.style.setProperty(name, value, priority); |
|
} |
|
function styleFunction() { |
|
var x = value.apply(this, arguments); |
|
if (x == null) this.style.removeProperty(name); else this.style.setProperty(name, x, priority); |
|
} |
|
return value == null ? styleNull : typeof value === "function" ? styleFunction : styleConstant; |
|
} |
|
d3_selectionPrototype.property = function(name, value) { |
|
if (arguments.length < 2) { |
|
if (typeof name === "string") return this.node()[name]; |
|
for (value in name) this.each(d3_selection_property(value, name[value])); |
|
return this; |
|
} |
|
return this.each(d3_selection_property(name, value)); |
|
}; |
|
function d3_selection_property(name, value) { |
|
function propertyNull() { |
|
delete this[name]; |
|
} |
|
function propertyConstant() { |
|
this[name] = value; |
|
} |
|
function propertyFunction() { |
|
var x = value.apply(this, arguments); |
|
if (x == null) delete this[name]; else this[name] = x; |
|
} |
|
return value == null ? propertyNull : typeof value === "function" ? propertyFunction : propertyConstant; |
|
} |
|
d3_selectionPrototype.text = function(value) { |
|
return arguments.length ? this.each(typeof value === "function" ? function() { |
|
var v = value.apply(this, arguments); |
|
this.textContent = v == null ? "" : v; |
|
} : value == null ? function() { |
|
this.textContent = ""; |
|
} : function() { |
|
this.textContent = value; |
|
}) : this.node().textContent; |
|
}; |
|
d3_selectionPrototype.html = function(value) { |
|
return arguments.length ? this.each(typeof value === "function" ? function() { |
|
var v = value.apply(this, arguments); |
|
this.innerHTML = v == null ? "" : v; |
|
} : value == null ? function() { |
|
this.innerHTML = ""; |
|
} : function() { |
|
this.innerHTML = value; |
|
}) : this.node().innerHTML; |
|
}; |
|
d3_selectionPrototype.append = function(name) { |
|
name = d3_selection_creator(name); |
|
return this.select(function() { |
|
return this.appendChild(name.apply(this, arguments)); |
|
}); |
|
}; |
|
function d3_selection_creator(name) { |
|
return typeof name === "function" ? name : (name = d3.ns.qualify(name)).local ? function() { |
|
return this.ownerDocument.createElementNS(name.space, name.local); |
|
} : function() { |
|
return this.ownerDocument.createElementNS(this.namespaceURI, name); |
|
}; |
|
} |
|
d3_selectionPrototype.insert = function(name, before) { |
|
name = d3_selection_creator(name); |
|
before = d3_selection_selector(before); |
|
return this.select(function() { |
|
return this.insertBefore(name.apply(this, arguments), before.apply(this, arguments) || null); |
|
}); |
|
}; |
|
d3_selectionPrototype.remove = function() { |
|
return this.each(function() { |
|
var parent = this.parentNode; |
|
if (parent) parent.removeChild(this); |
|
}); |
|
}; |
|
d3_selectionPrototype.data = function(value, key) { |
|
var i = -1, n = this.length, group, node; |
|
if (!arguments.length) { |
|
value = new Array(n = (group = this[0]).length); |
|
while (++i < n) { |
|
if (node = group[i]) { |
|
value[i] = node.__data__; |
|
} |
|
} |
|
return value; |
|
} |
|
function bind(group, groupData) { |
|
var i, n = group.length, m = groupData.length, n0 = Math.min(n, m), updateNodes = new Array(m), enterNodes = new Array(m), exitNodes = new Array(n), node, nodeData; |
|
if (key) { |
|
var nodeByKeyValue = new d3_Map(), dataByKeyValue = new d3_Map(), keyValues = [], keyValue; |
|
for (i = -1; ++i < n; ) { |
|
keyValue = key.call(node = group[i], node.__data__, i); |
|
if (nodeByKeyValue.has(keyValue)) { |
|
exitNodes[i] = node; |
|
} else { |
|
nodeByKeyValue.set(keyValue, node); |
|
} |
|
keyValues.push(keyValue); |
|
} |
|
for (i = -1; ++i < m; ) { |
|
keyValue = key.call(groupData, nodeData = groupData[i], i); |
|
if (node = nodeByKeyValue.get(keyValue)) { |
|
updateNodes[i] = node; |
|
node.__data__ = nodeData; |
|
} else if (!dataByKeyValue.has(keyValue)) { |
|
enterNodes[i] = d3_selection_dataNode(nodeData); |
|
} |
|
dataByKeyValue.set(keyValue, nodeData); |
|
nodeByKeyValue.remove(keyValue); |
|
} |
|
for (i = -1; ++i < n; ) { |
|
if (nodeByKeyValue.has(keyValues[i])) { |
|
exitNodes[i] = group[i]; |
|
} |
|
} |
|
} else { |
|
for (i = -1; ++i < n0; ) { |
|
node = group[i]; |
|
nodeData = groupData[i]; |
|
if (node) { |
|
node.__data__ = nodeData; |
|
updateNodes[i] = node; |
|
} else { |
|
enterNodes[i] = d3_selection_dataNode(nodeData); |
|
} |
|
} |
|
for (;i < m; ++i) { |
|
enterNodes[i] = d3_selection_dataNode(groupData[i]); |
|
} |
|
for (;i < n; ++i) { |
|
exitNodes[i] = group[i]; |
|
} |
|
} |
|
enterNodes.update = updateNodes; |
|
enterNodes.parentNode = updateNodes.parentNode = exitNodes.parentNode = group.parentNode; |
|
enter.push(enterNodes); |
|
update.push(updateNodes); |
|
exit.push(exitNodes); |
|
} |
|
var enter = d3_selection_enter([]), update = d3_selection([]), exit = d3_selection([]); |
|
if (typeof value === "function") { |
|
while (++i < n) { |
|
bind(group = this[i], value.call(group, group.parentNode.__data__, i)); |
|
} |
|
} else { |
|
while (++i < n) { |
|
bind(group = this[i], value); |
|
} |
|
} |
|
update.enter = function() { |
|
return enter; |
|
}; |
|
update.exit = function() { |
|
return exit; |
|
}; |
|
return update; |
|
}; |
|
function d3_selection_dataNode(data) { |
|
return { |
|
__data__: data |
|
}; |
|
} |
|
d3_selectionPrototype.datum = function(value) { |
|
return arguments.length ? this.property("__data__", value) : this.property("__data__"); |
|
}; |
|
d3_selectionPrototype.each = function(callback) { |
|
return d3_selection_each(this, function(node, i, j) { |
|
callback.call(node, node.__data__, i, j); |
|
}); |
|
}; |
|
function d3_selection_each(groups, callback) { |
|
for (var j = 0, m = groups.length; j < m; j++) { |
|
for (var group = groups[j], i = 0, n = group.length, node; i < n; i++) { |
|
if (node = group[i]) callback(node, i, j); |
|
} |
|
} |
|
return groups; |
|
} |
|
d3_selectionPrototype.call = function(callback) { |
|
var args = d3_array(arguments); |
|
callback.apply(args[0] = this, args); |
|
return this; |
|
}; |
|
d3_selectionPrototype.empty = function() { |
|
return !this.node(); |
|
}; |
|
d3_selectionPrototype.node = function() { |
|
for (var j = 0, m = this.length; j < m; j++) { |
|
for (var group = this[j], i = 0, n = group.length; i < n; i++) { |
|
var node = group[i]; |
|
if (node) return node; |
|
} |
|
} |
|
return null; |
|
}; |
|
function d3_selection_enter(selection) { |
|
d3_subclass(selection, d3_selection_enterPrototype); |
|
return selection; |
|
} |
|
var d3_selection_enterPrototype = []; |
|
d3.selection.enter = d3_selection_enter; |
|
d3.selection.enter.prototype = d3_selection_enterPrototype; |
|
d3_selection_enterPrototype.append = d3_selectionPrototype.append; |
|
d3_selection_enterPrototype.empty = d3_selectionPrototype.empty; |
|
d3_selection_enterPrototype.node = d3_selectionPrototype.node; |
|
d3_selection_enterPrototype.call = d3_selectionPrototype.call; |
|
d3_selection_enterPrototype.select = function(selector) { |
|
var subgroups = [], subgroup, subnode, upgroup, group, node; |
|
for (var j = -1, m = this.length; ++j < m; ) { |
|
upgroup = (group = this[j]).update; |
|
subgroups.push(subgroup = []); |
|
subgroup.parentNode = group.parentNode; |
|
for (var i = -1, n = group.length; ++i < n; ) { |
|
if (node = group[i]) { |
|
subgroup.push(upgroup[i] = subnode = selector.call(group.parentNode, node.__data__, i, j)); |
|
subnode.__data__ = node.__data__; |
|
} else { |
|
subgroup.push(null); |
|
} |
|
} |
|
} |
|
return d3_selection(subgroups); |
|
}; |
|
d3_selection_enterPrototype.insert = function(name, before) { |
|
if (arguments.length < 2) before = d3_selection_enterInsertBefore(this); |
|
return d3_selectionPrototype.insert.call(this, name, before); |
|
}; |
|
function d3_selection_enterInsertBefore(enter) { |
|
var i0, j0; |
|
return function(d, i, j) { |
|
var group = enter[j].update, n = group.length, node; |
|
if (j != j0) j0 = j, i0 = 0; |
|
if (i >= i0) i0 = i + 1; |
|
while (!(node = group[i0]) && ++i0 < n) ; |
|
return node; |
|
}; |
|
} |
|
d3.select = function(node) { |
|
var group = [ typeof node === "string" ? d3_select(node, d3_document) : node ]; |
|
group.parentNode = d3_documentElement; |
|
return d3_selection([ group ]); |
|
}; |
|
d3.selectAll = function(nodes) { |
|
var group = d3_array(typeof nodes === "string" ? d3_selectAll(nodes, d3_document) : nodes); |
|
group.parentNode = d3_documentElement; |
|
return d3_selection([ group ]); |
|
}; |
|
d3_selectionPrototype.on = function(type, listener, capture) { |
|
var n = arguments.length; |
|
if (n < 3) { |
|
if (typeof type !== "string") { |
|
if (n < 2) listener = false; |
|
for (capture in type) this.each(d3_selection_on(capture, type[capture], listener)); |
|
return this; |
|
} |
|
if (n < 2) return (n = this.node()["__on" + type]) && n._; |
|
capture = false; |
|
} |
|
return this.each(d3_selection_on(type, listener, capture)); |
|
}; |
|
function d3_selection_on(type, listener, capture) { |
|
var name = "__on" + type, i = type.indexOf("."), wrap = d3_selection_onListener; |
|
if (i > 0) type = type.substring(0, i); |
|
var filter = d3_selection_onFilters.get(type); |
|
if (filter) type = filter, wrap = d3_selection_onFilter; |
|
function onRemove() { |
|
var l = this[name]; |
|
if (l) { |
|
this.removeEventListener(type, l, l.$); |
|
delete this[name]; |
|
} |
|
} |
|
function onAdd() { |
|
var l = wrap(listener, d3_array(arguments)); |
|
onRemove.call(this); |
|
this.addEventListener(type, this[name] = l, l.$ = capture); |
|
l._ = listener; |
|
} |
|
function removeAll() { |
|
var re = new RegExp("^__on([^.]+)" + d3.requote(type) + "$"), match; |
|
for (var name in this) { |
|
if (match = name.match(re)) { |
|
var l = this[name]; |
|
this.removeEventListener(match[1], l, l.$); |
|
delete this[name]; |
|
} |
|
} |
|
} |
|
return i ? listener ? onAdd : onRemove : listener ? d3_noop : removeAll; |
|
} |
|
var d3_selection_onFilters = d3.map({ |
|
mouseenter: "mouseover", |
|
mouseleave: "mouseout" |
|
}); |
|
d3_selection_onFilters.forEach(function(k) { |
|
if ("on" + k in d3_document) d3_selection_onFilters.remove(k); |
|
}); |
|
function d3_selection_onListener(listener, argumentz) { |
|
return function(e) { |
|
var o = d3.event; |
|
d3.event = e; |
|
argumentz[0] = this.__data__; |
|
try { |
|
listener.apply(this, argumentz); |
|
} finally { |
|
d3.event = o; |
|
} |
|
}; |
|
} |
|
d3.mouse = function(container) { |
|
return d3_mousePoint(container, d3_eventSource()); |
|
}; |
|
var d3_mouse_bug44083 = /WebKit/.test(d3_window.navigator.userAgent) ? -1 : 0; |
|
function d3_mousePoint(container, e) { |
|
if (e.changedTouches) e = e.changedTouches[0]; |
|
var svg = container.ownerSVGElement || container; |
|
if (svg.createSVGPoint) { |
|
var point = svg.createSVGPoint(); |
|
if (d3_mouse_bug44083 < 0 && (d3_window.scrollX || d3_window.scrollY)) { |
|
svg = d3.select("body").append("svg").style({ |
|
position: "absolute", |
|
top: 0, |
|
left: 0, |
|
margin: 0, |
|
padding: 0, |
|
border: "none" |
|
}, "important"); |
|
var ctm = svg[0][0].getScreenCTM(); |
|
d3_mouse_bug44083 = !(ctm.f || ctm.e); |
|
svg.remove(); |
|
} |
|
if (d3_mouse_bug44083) point.x = e.pageX, point.y = e.pageY; else point.x = e.clientX, |
|
point.y = e.clientY; |
|
point = point.matrixTransform(container.getScreenCTM().inverse()); |
|
return [ point.x, point.y ]; |
|
} |
|
var rect = container.getBoundingClientRect(); |
|
return [ e.clientX - rect.left - container.clientLeft, e.clientY - rect.top - container.clientTop ]; |
|
} |
|
var π = Math.PI, τ = 2 * π, halfπ = π / 2, ε = 1e-6, ε2 = ε * ε, d3_radians = π / 180, d3_degrees = 180 / π; |
|
function d3_functor(v) { |
|
return typeof v === "function" ? v : function() { |
|
return v; |
|
}; |
|
} |
|
d3.functor = d3_functor; |
|
function d3_identity(d) { |
|
return d; |
|
} |
|
var d3_format_decimalPoint = ".", d3_format_thousandsSeparator = ",", d3_format_grouping = [ 3, 3 ], d3_format_currencySymbol = "$"; |
|
function d3_formatPrefix(d, i) { |
|
var k = Math.pow(10, abs(8 - i) * 3); |
|
return { |
|
scale: i > 8 ? function(d) { |
|
return d / k; |
|
} : function(d) { |
|
return d * k; |
|
}, |
|
symbol: d |
|
}; |
|
} |
|
d3.format = function(specifier) { |
|
var match = d3_format_re.exec(specifier), fill = match[1] || " ", align = match[2] || ">", sign = match[3] || "", symbol = match[4] || "", zfill = match[5], width = +match[6], comma = match[7], precision = match[8], type = match[9], scale = 1, suffix = "", integer = false; |
|
if (precision) precision = +precision.substring(1); |
|
if (zfill || fill === "0" && align === "=") { |
|
zfill = fill = "0"; |
|
align = "="; |
|
if (comma) width -= Math.floor((width - 1) / 4); |
|
} |
|
switch (type) { |
|
case "n": |
|
comma = true; |
|
type = "g"; |
|
break; |
|
|
|
case "%": |
|
scale = 100; |
|
suffix = "%"; |
|
type = "f"; |
|
break; |
|
|
|
case "p": |
|
scale = 100; |
|
suffix = "%"; |
|
type = "r"; |
|
break; |
|
|
|
case "b": |
|
case "o": |
|
case "x": |
|
case "X": |
|
if (symbol === "#") symbol = "0" + type.toLowerCase(); |
|
|
|
case "c": |
|
case "d": |
|
integer = true; |
|
precision = 0; |
|
break; |
|
|
|
case "s": |
|
scale = -1; |
|
type = "r"; |
|
break; |
|
} |
|
if (symbol === "#") symbol = ""; else if (symbol === "$") symbol = d3_format_currencySymbol; |
|
if (type == "r" && !precision) type = "g"; |
|
if (precision != null) { |
|
if (type == "g") precision = Math.max(1, Math.min(21, precision)); else if (type == "e" || type == "f") precision = Math.max(0, Math.min(20, precision)); |
|
} |
|
type = d3_format_types.get(type) || d3_format_typeDefault; |
|
var zcomma = zfill && comma; |
|
return function(value) { |
|
if (integer && value % 1) return ""; |
|
var negative = value < 0 || value === 0 && 1 / value < 0 ? (value = -value, "-") : sign; |
|
if (scale < 0) { |
|
var prefix = d3.formatPrefix(value, precision); |
|
value = prefix.scale(value); |
|
suffix = prefix.symbol; |
|
} else { |
|
value *= scale; |
|
} |
|
value = type(value, precision); |
|
var i = value.lastIndexOf("."), before = i < 0 ? value : value.substring(0, i), after = i < 0 ? "" : d3_format_decimalPoint + value.substring(i + 1); |
|
if (!zfill && comma) before = d3_format_group(before); |
|
var length = symbol.length + before.length + after.length + (zcomma ? 0 : negative.length), padding = length < width ? new Array(length = width - length + 1).join(fill) : ""; |
|
if (zcomma) before = d3_format_group(padding + before); |
|
negative += symbol; |
|
value = before + after; |
|
return (align === "<" ? negative + value + padding : align === ">" ? padding + negative + value : align === "^" ? padding.substring(0, length >>= 1) + negative + value + padding.substring(length) : negative + (zcomma ? value : padding + value)) + suffix; |
|
}; |
|
}; |
|
var d3_format_re = /(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i; |
|
var d3_format_types = d3.map({ |
|
f: function(x, p) { |
|
return x.toFixed(p); |
|
} |
|
}); |
|
function d3_format_precision(x, p) { |
|
return p - (x ? Math.ceil(Math.log(x) / Math.LN10) : 1); |
|
} |
|
function d3_format_typeDefault(x) { |
|
return x + ""; |
|
} |
|
var d3_format_group = d3_identity; |
|
if (d3_format_grouping) { |
|
var d3_format_groupingLength = d3_format_grouping.length; |
|
d3_format_group = function(value) { |
|
var i = value.length, t = [], j = 0, g = d3_format_grouping[0]; |
|
while (i > 0 && g > 0) { |
|
t.push(value.substring(i -= g, i + g)); |
|
g = d3_format_grouping[j = (j + 1) % d3_format_groupingLength]; |
|
} |
|
return t.reverse().join(d3_format_thousandsSeparator); |
|
}; |
|
} |
|
function d3_true() { |
|
return true; |
|
} |
|
function d3_geom_pointX(d) { |
|
return d[0]; |
|
} |
|
function d3_geom_pointY(d) { |
|
return d[1]; |
|
} |
|
d3.interpolateNumber = d3_interpolateNumber; |
|
function d3_interpolateNumber(a, b) { |
|
b -= a = +a; |
|
return function(t) { |
|
return a + b * t; |
|
}; |
|
} |
|
var d3_interpolate_number = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g; |
|
d3.interpolate = d3_interpolate; |
|
function d3_interpolate(a, b) { |
|
var i = d3.interpolators.length, f; |
|
while (--i >= 0 && !(f = d3.interpolators[i](a, b))) ; |
|
return f; |
|
} |
|
d3.interpolators = [ function(a, b) { |
|
var t = typeof b; |
|
return (t === "string" ? d3_rgb_names.has(b) || /^(#|rgb\(|hsl\()/.test(b) ? d3_interpolateRgb : d3_interpolateString : t === "object" ? Array.isArray(b) ? d3_interpolateArray : d3_interpolateObject : d3_interpolateNumber)(a, b); |
|
} ]; |
|
function d3_uninterpolateNumber(a, b) { |
|
b = b - (a = +a) ? 1 / (b - a) : 0; |
|
return function(x) { |
|
return (x - a) * b; |
|
}; |
|
} |
|
d3.scale = {}; |
|
function d3_scaleExtent(domain) { |
|
var start = domain[0], stop = domain[domain.length - 1]; |
|
return start < stop ? [ start, stop ] : [ stop, start ]; |
|
} |
|
function d3_scaleRange(scale) { |
|
return scale.rangeExtent ? scale.rangeExtent() : d3_scaleExtent(scale.range()); |
|
} |
|
function d3_scale_bilinear(domain, range, uninterpolate, interpolate) { |
|
var u = uninterpolate(domain[0], domain[1]), i = interpolate(range[0], range[1]); |
|
return function(x) { |
|
return i(u(x)); |
|
}; |
|
} |
|
d3.scale.linear = function() { |
|
return d3_scale_linear([ 0, 1 ], [ 0, 1 ], d3_interpolate, false); |
|
}; |
|
function d3_scale_linear(domain, range, interpolate, clamp) { |
|
var output, input; |
|
function rescale() { |
|
var linear = Math.min(domain.length, range.length) > 2 ? d3_scale_polylinear : d3_scale_bilinear, uninterpolate = clamp ? d3_uninterpolateClamp : d3_uninterpolateNumber; |
|
output = linear(domain, range, uninterpolate, interpolate); |
|
input = linear(range, domain, uninterpolate, d3_interpolate); |
|
return scale; |
|
} |
|
function scale(x) { |
|
return output(x); |
|
} |
|
scale.invert = function(y) { |
|
return input(y); |
|
}; |
|
scale.range = function(x) { |
|
if (!arguments.length) return range; |
|
range = x; |
|
return rescale(); |
|
}; |
|
scale.ticks = function(m) { |
|
return d3_scale_linearTicks(domain, m); |
|
}; |
|
scale.tickFormat = function(m, format) { |
|
return d3_scale_linearTickFormat(domain, m, format); |
|
}; |
|
|
|
scale.copy = function() { |
|
return d3_scale_linear(domain, range, interpolate, clamp); |
|
}; |
|
return rescale(); |
|
} |
|
function d3_scale_linearTickRange(domain, m) { |
|
if (m == null) m = 10; |
|
var extent = d3_scaleExtent(domain), span = extent[1] - extent[0], step = Math.pow(10, Math.floor(Math.log(span / m) / Math.LN10)), err = m / span * step; |
|
if (err <= .15) step *= 10; else if (err <= .35) step *= 5; else if (err <= .75) step *= 2; |
|
extent[0] = Math.ceil(extent[0] / step) * step; |
|
extent[1] = Math.floor(extent[1] / step) * step + step * .5; |
|
extent[2] = step; |
|
return extent; |
|
} |
|
function d3_scale_linearTicks(domain, m) { |
|
return d3.range.apply(d3, d3_scale_linearTickRange(domain, m)); |
|
} |
|
function d3_scale_linearTickFormat(domain, m, format) { |
|
var range = d3_scale_linearTickRange(domain, m); |
|
return d3.format(format ? format.replace(d3_format_re, function(a, b, c, d, e, f, g, h, i, j) { |
|
return [ b, c, d, e, f, g, h, i || "." + d3_scale_linearFormatPrecision(j, range), j ].join(""); |
|
}) : ",." + d3_scale_linearPrecision(range[2]) + "f"); |
|
} |
|
function d3_scale_linearPrecision(value) { |
|
return -Math.floor(Math.log(value) / Math.LN10 + .01); |
|
} |
|
d3.svg = {}; |
|
function d3_svg_line(projection) { |
|
var x = d3_geom_pointX, y = d3_geom_pointY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, tension = .7; |
|
function line(data) { |
|
var segments = [], points = [], i = -1, n = data.length, d, fx = d3_functor(x), fy = d3_functor(y); |
|
function segment() { |
|
segments.push("M", interpolate(projection(points), tension)); |
|
} |
|
while (++i < n) { |
|
if (defined.call(this, d = data[i], i)) { |
|
points.push([ +fx.call(this, d, i), +fy.call(this, d, i) ]); |
|
} else if (points.length) { |
|
segment(); |
|
points = []; |
|
} |
|
} |
|
if (points.length) segment(); |
|
return segments.length ? segments.join("") : null; |
|
} |
|
line.x = function(_) { |
|
if (!arguments.length) return x; |
|
x = _; |
|
return line; |
|
}; |
|
line.y = function(_) { |
|
if (!arguments.length) return y; |
|
y = _; |
|
return line; |
|
}; |
|
return line; |
|
} |
|
d3.svg.line = function() { |
|
return d3_svg_line(d3_identity); |
|
}; |
|
var d3_svg_lineInterpolators = d3.map({ |
|
"linear-closed": d3_svg_lineLinearClosed |
|
}); |
|
d3_svg_lineInterpolators.forEach(function(key, value) { |
|
value.key = key; |
|
value.closed = /-closed$/.test(key); |
|
}); |
|
function d3_svg_lineLinear(points) { |
|
return points.join("L"); |
|
} |
|
function d3_svg_lineLinearClosed(points) { |
|
return d3_svg_lineLinear(points) + "Z"; |
|
} |
|
d3.svg.axis = function() { |
|
var scale = d3.scale.linear(), orient = d3_svg_axisDefaultOrient, innerTickSize = 6, outerTickSize = 6, tickPadding = 3, tickArguments_ = [ 10 ], tickValues = null, tickFormat_; |
|
function axis(g) { |
|
g.each(function() { |
|
var g = d3.select(this); |
|
var scale0 = this.__chart__ || scale, scale1 = this.__chart__ = scale.copy(); |
|
var ticks = tickValues == null ? scale1.ticks ? scale1.ticks.apply(scale1, tickArguments_) : scale1.domain() : tickValues, tickFormat = tickFormat_ == null ? scale1.tickFormat ? scale1.tickFormat.apply(scale1, tickArguments_) : d3_identity : tickFormat_, tick = g.selectAll(".tick").data(ticks, scale1), tickEnter = tick.enter().insert("g", ".domain").attr("class", "tick").style("opacity", ε), tickExit = tick.exit().style("opacity", ε).remove(), tickUpdate = tick.style("opacity", 1), tickTransform; |
|
var range = d3_scaleRange(scale1), path = g.selectAll(".domain").data([ 0 ]), pathUpdate = (path.enter().append("path").attr("class", "domain"), |
|
path); |
|
tickEnter.append("line"); |
|
tickEnter.append("text"); |
|
var lineEnter = tickEnter.select("line"), lineUpdate = tickUpdate.select("line"), text = tick.select("text").text(tickFormat), textEnter = tickEnter.select("text"), textUpdate = tickUpdate.select("text"); |
|
switch (orient) { |
|
case "bottom": |
|
{ |
|
tickTransform = d3_svg_axisX; |
|
lineEnter.attr("y2", innerTickSize); |
|
textEnter.attr("y", Math.max(innerTickSize, 0) + tickPadding); |
|
lineUpdate.attr("x2", 0).attr("y2", innerTickSize); |
|
textUpdate.attr("x", 0).attr("y", Math.max(innerTickSize, 0) + tickPadding); |
|
text.attr("dy", ".71em").style("text-anchor", "middle"); |
|
pathUpdate.attr("d", "M" + range[0] + "," + outerTickSize + "V0H" + range[1] + "V" + outerTickSize); |
|
break; |
|
} |
|
|
|
case "top": |
|
{ |
|
tickTransform = d3_svg_axisX; |
|
lineEnter.attr("y2", -innerTickSize); |
|
textEnter.attr("y", -(Math.max(innerTickSize, 0) + tickPadding)); |
|
lineUpdate.attr("x2", 0).attr("y2", -innerTickSize); |
|
textUpdate.attr("x", 0).attr("y", -(Math.max(innerTickSize, 0) + tickPadding)); |
|
text.attr("dy", "0em").style("text-anchor", "middle"); |
|
pathUpdate.attr("d", "M" + range[0] + "," + -outerTickSize + "V0H" + range[1] + "V" + -outerTickSize); |
|
break; |
|
} |
|
|
|
case "left": |
|
{ |
|
tickTransform = d3_svg_axisY; |
|
lineEnter.attr("x2", -innerTickSize); |
|
textEnter.attr("x", -(Math.max(innerTickSize, 0) + tickPadding)); |
|
lineUpdate.attr("x2", -innerTickSize).attr("y2", 0); |
|
textUpdate.attr("x", -(Math.max(innerTickSize, 0) + tickPadding)).attr("y", 0); |
|
text.attr("dy", ".32em").style("text-anchor", "end"); |
|
pathUpdate.attr("d", "M" + -outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + -outerTickSize); |
|
break; |
|
} |
|
|
|
case "right": |
|
{ |
|
tickTransform = d3_svg_axisY; |
|
lineEnter.attr("x2", innerTickSize); |
|
textEnter.attr("x", Math.max(innerTickSize, 0) + tickPadding); |
|
lineUpdate.attr("x2", innerTickSize).attr("y2", 0); |
|
textUpdate.attr("x", Math.max(innerTickSize, 0) + tickPadding).attr("y", 0); |
|
text.attr("dy", ".32em").style("text-anchor", "start"); |
|
pathUpdate.attr("d", "M" + outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + outerTickSize); |
|
break; |
|
} |
|
} |
|
if (scale1.rangeBand) { |
|
var x = scale1, dx = x.rangeBand() / 2; |
|
scale0 = scale1 = function(d) { |
|
return x(d) + dx; |
|
}; |
|
} else if (scale0.rangeBand) { |
|
scale0 = scale1; |
|
} else { |
|
tickExit.call(tickTransform, scale1); |
|
} |
|
tickEnter.call(tickTransform, scale0); |
|
tickUpdate.call(tickTransform, scale1); |
|
}); |
|
} |
|
axis.scale = function(x) { |
|
if (!arguments.length) return scale; |
|
scale = x; |
|
return axis; |
|
}; |
|
axis.orient = function(x) { |
|
if (!arguments.length) return orient; |
|
orient = x in d3_svg_axisOrients ? x + "" : d3_svg_axisDefaultOrient; |
|
return axis; |
|
}; |
|
axis.ticks = function() { |
|
if (!arguments.length) return tickArguments_; |
|
tickArguments_ = arguments; |
|
return axis; |
|
}; |
|
axis.tickFormat = function(x) { |
|
if (!arguments.length) return tickFormat_; |
|
tickFormat_ = x; |
|
return axis; |
|
}; |
|
axis.tickSize = function(x) { |
|
var n = arguments.length; |
|
if (!n) return innerTickSize; |
|
innerTickSize = +x; |
|
outerTickSize = +arguments[n - 1]; |
|
return axis; |
|
}; |
|
return axis; |
|
}; |
|
var d3_svg_axisDefaultOrient = "bottom", d3_svg_axisOrients = { |
|
top: 1, |
|
right: 1, |
|
bottom: 1, |
|
left: 1 |
|
}; |
|
function d3_svg_axisX(selection, x) { |
|
selection.attr("transform", function(d) { |
|
return "translate(" + x(d) + ",0)"; |
|
}); |
|
} |
|
function d3_svg_axisY(selection, y) { |
|
selection.attr("transform", function(d) { |
|
return "translate(0," + y(d) + ")"; |
|
}); |
|
} |
|
return d3; |
|
}(); |
|
</script> |
|
|
|
<script> |
|
|
|
/*** Model ***/ |
|
|
|
var model = window; |
|
|
|
// Select initial values based on rain example. |
|
var prior = 0.07; |
|
var sensitivity = 0.80; |
|
var specificity = 0.95; |
|
|
|
function isClassificationContradition() { |
|
return sensitivity === 0 && specificity === 1; |
|
} |
|
|
|
function bayes(prior, sensitivity, specificity) { |
|
if (prior === 0 || prior === 1 || // extreme prior |
|
sensitivity === 0 && specificity === 1) { // contradiction |
|
return prior; |
|
} |
|
|
|
return sensitivity * prior / |
|
(sensitivity * prior + (1 - specificity) * (1 - prior)); |
|
} |
|
|
|
function bayesAt(variable, value) { |
|
switch (variable) { |
|
case "prior": |
|
return bayes(value, sensitivity, specificity); |
|
case "sensitivity": |
|
return bayes(prior, value, specificity); |
|
case "specificity": |
|
return bayes(prior, sensitivity, value); |
|
default: |
|
throw new TypeError("invalid variable "+variable); |
|
} |
|
} |
|
|
|
var posteriorFindTolerance = 0.00001; |
|
function withinTolerance(v, w) { |
|
return Math.abs(v - w) < posteriorFindTolerance; |
|
} |
|
|
|
function bayesFind(variable, posteriorValue) { |
|
// Use binary search. |
|
var min = 0; |
|
var max = 1; |
|
for (;;) { |
|
var middle = (max + min) / 2; |
|
if (withinTolerance(middle, 0)) { |
|
return 0; |
|
} |
|
if (withinTolerance(middle, 1)) { |
|
return 1; |
|
} |
|
var v = bayesAt(variable, middle); |
|
if (withinTolerance(v, posteriorValue)) { |
|
return middle; |
|
} |
|
if (v < posteriorValue) { |
|
min = middle; |
|
} else { |
|
max = middle; |
|
} |
|
} |
|
} |
|
|
|
function between(x, lower, upper) { |
|
return lower <= x && x <= upper; |
|
} |
|
|
|
function square(x) { |
|
return x * x; |
|
} |
|
|
|
function clamp(value) { |
|
return Math.max(0, Math.min(value, 1)); |
|
} |
|
|
|
Object.defineProperty(model, "fScore", { |
|
get: function() { |
|
var sum = sensitivity + specificity; |
|
return sum === 0 ? 0 : 2 * sensitivity * specificity / sum; |
|
} |
|
}); |
|
|
|
Object.defineProperty(model, "posterior", { |
|
get: function() { |
|
return bayes(prior, sensitivity, specificity); |
|
}, |
|
set: function(value) { |
|
// Use hill climbing. |
|
var maxIterations = 100000; |
|
var samples = 1000; |
|
var wiggliness = 100; |
|
var posteriorWeight = 1000000; |
|
|
|
var basePrior = prior; |
|
var baseSensitivity = sensitivity; |
|
var baseSpecificity = specificity; |
|
function objective(prior, sensitivity, specificity) { |
|
var posterior = bayes(prior, sensitivity, specificity); |
|
return square(posterior - value) * posteriorWeight + |
|
square(prior - basePrior) + |
|
square(sensitivity - baseSensitivity) + |
|
square(specificity - baseSpecificity); |
|
} |
|
|
|
function wiggle(v) { |
|
return clamp(v + wiggliness * posteriorFindTolerance * |
|
(Math.random() - 0.5)); |
|
} |
|
|
|
for (var i = 0; i < maxIterations; ++i) { |
|
if (withinTolerance(posterior, value)) { |
|
break; |
|
} |
|
|
|
var bestPrior = prior; |
|
var bestSensitivity = sensitivity; |
|
var bestSpecificity = specificity; |
|
var bestValue = objective(prior, sensitivity, specificity); |
|
for (var j = 0; j < samples; ++j) { |
|
// Wiggle independent variables. |
|
var p = wiggle(prior); |
|
var sn = wiggle(sensitivity); |
|
var sp = wiggle(specificity); |
|
|
|
// Choose the better. |
|
var newValue = objective(p, sn, sp); |
|
if (newValue < bestValue) { |
|
bestValue = newValue; |
|
bestPrior = p; |
|
bestSensitivity = sn; |
|
bestSpecificity = sp; |
|
} |
|
} |
|
|
|
prior = bestPrior; |
|
sensitivity = bestSensitivity; |
|
specificity = bestSpecificity; |
|
} |
|
} |
|
}); |
|
|
|
// extraPush helps posterior round toward amount. |
|
function nudge(variable, amount, extraPush) { |
|
if (extraPush === undefined) { |
|
extraPush = 0; |
|
} |
|
|
|
var value = model[variable]; |
|
var clamped = Math.floor(value / amount) * amount; |
|
model[variable] = clamp(clamped + amount + extraPush); |
|
} |
|
|
|
/*** Perspective ***/ |
|
|
|
var selectedVariable = "prior"; // one of the independent variables. |
|
var selectedPosterior = false; // a separate selection. |
|
var controlState = "inert"; // one of "inert", "focused", "active". |
|
var explicitFocusValue = null; |
|
var selectedGuideText = false; |
|
|
|
var dynamicSelections = []; |
|
|
|
function dynamic(selection, redrawFn) { |
|
if (typeof redrawFn == "undefined") { |
|
redrawFn = selection; |
|
selection = null; |
|
} |
|
|
|
dynamicSelections.push({ |
|
selection: selection, |
|
redraw: redrawFn |
|
}); |
|
redrawFn(selection); |
|
return selection; |
|
} |
|
|
|
var redrawRequested = null; |
|
|
|
function redraw() { |
|
if (redrawRequested === null) { |
|
redrawRequested = requestAnimationFrame(redrawNow); |
|
} |
|
} |
|
|
|
function redrawNow() { |
|
for (var i = 0; i < dynamicSelections.length; ++i) { |
|
dynamicSelections[i].redraw(dynamicSelections[i].selection); |
|
} |
|
redrawRequested = null; |
|
} |
|
|
|
function transition(states, d, i) { |
|
var transitionFn = states[controlState]; |
|
if (typeof transitionFn !== "function") { |
|
return; |
|
} |
|
return transitionFn(d, i); |
|
} |
|
|
|
function on(selection, type, listener) { |
|
if (typeof listener !== "function") { |
|
var states = listener; |
|
listener = function(d, i) { |
|
return transition(states, d, i); |
|
} |
|
} |
|
|
|
return selection.on(type, function(d, i) { |
|
var newState = listener(d, i); |
|
if (typeof newState == "string") { |
|
controlState = newState; |
|
} |
|
redraw(); |
|
}); |
|
} |
|
|
|
/*** View ***/ |
|
|
|
var width = 400; |
|
var height = 400; |
|
|
|
var margin = { |
|
top: 20, // just for spacing. |
|
right: 40, // for overflow of xGuideText. |
|
bottom: 120, // for prior, sensitivity, and specificity sliders. |
|
left: 100, // for the posterior slider. |
|
}; |
|
|
|
var x = d3.scale.linear().range([0, width]); |
|
var y = d3.scale.linear().range([height, 0]); |
|
|
|
function xAxis() { |
|
return d3.svg.axis() |
|
.scale(x) |
|
.orient("bottom") |
|
.ticks(10); |
|
} |
|
|
|
function yAxis() { |
|
return d3.svg.axis() |
|
.scale(y) |
|
.orient("left") |
|
.ticks(10); |
|
} |
|
|
|
var svg = d3.select("main").append("svg") |
|
.attr("width", width + margin.left + margin.right) |
|
.attr("height", height + margin.top + margin.bottom) |
|
|
|
var svgg = svg.append("g") |
|
.attr("transform", "translate("+margin.left+","+margin.top+")"); |
|
|
|
/** Grid **/ |
|
|
|
// Draw the x grid lines. |
|
svgg.append("g") |
|
.classed("grid", true) |
|
.attr("transform", "translate(0,"+height+")") |
|
.call(xAxis() |
|
.tickSize(-height, 0, 0) |
|
.tickFormat("")) |
|
|
|
// Draw the y grid lines. |
|
svgg.append("g") |
|
.classed("grid", true) |
|
.call(yAxis() |
|
.tickSize(-width, 0, 0) |
|
.tickFormat("")); |
|
|
|
svgg.append("g") |
|
.classed("axis", true) |
|
.attr("transform", "translate(0," + height + ")") |
|
.call(xAxis()); |
|
|
|
svgg.append("g") |
|
.classed("axis", true) |
|
.call(yAxis()); |
|
|
|
/** Guide **/ |
|
|
|
var xLabelOffsetBelowXAxis = 36; |
|
var yLabelOffsetLeftOfYAxis = 60; |
|
var labelOffsetFromLine = 3; |
|
|
|
var guideVariable; |
|
var guideValue; |
|
var guideRangeValue; |
|
var guideLineStarts = {}; |
|
dynamic(function() { |
|
guideVariable = controlState === "inert" ? |
|
null : selectedVariable; |
|
guideValue = controlState === "active" || |
|
explicitFocusValue === null ? |
|
model[selectedVariable] : explicitFocusValue; |
|
guideRangeValue = bayesAt(selectedVariable, guideValue); |
|
}); |
|
|
|
var guide = svgg.append("g") |
|
.classed("guide", true); |
|
|
|
function ifGuideShouldAppear(selection, updates) { |
|
if (guideVariable !== null || selectedGuideText) { |
|
selection.attr("display", null) |
|
.call(updates); |
|
} else { |
|
selection.attr("display", "none"); |
|
} |
|
} |
|
|
|
var verticalGuideLine = guide.append("line") |
|
.call(dynamic, function(s) {s |
|
.call(ifGuideShouldAppear, function(s) {s |
|
.attr("x1", x(guideValue)) |
|
.attr("x2", x(guideValue)) |
|
.attr("y1", guideLineStarts[selectedVariable]) |
|
.attr("y2", y(guideRangeValue)); |
|
}); |
|
}); |
|
|
|
var horizontalGuideLine = guide.append("line") |
|
.call(dynamic, function(s) {s |
|
.call(ifGuideShouldAppear, function(s) {s |
|
.attr("x1", guideLineStarts["posterior"]) |
|
.attr("x2", x(guideValue)) |
|
.attr("y1", y(guideRangeValue)) |
|
.attr("y2", y(guideRangeValue)); |
|
}); |
|
}); |
|
|
|
var guideFormat = d3.format(".3f"); |
|
|
|
var xGuideText = guide.append("text") |
|
.attr("y", y(0) + xLabelOffsetBelowXAxis) |
|
.attr("dx", labelOffsetFromLine) |
|
.call(dynamic, function(s) {s |
|
.call(ifGuideShouldAppear, function(s) {s |
|
.attr("x", x(guideValue)) |
|
.text(guideFormat(guideValue)); |
|
}); |
|
}); |
|
|
|
var yGuideText = guide.append("text") |
|
.attr("x", x(0) - yLabelOffsetLeftOfYAxis) |
|
.attr("dy", -labelOffsetFromLine) |
|
.call(dynamic, function(s) {s |
|
.call(ifGuideShouldAppear, function(s) {s |
|
.attr("y", y(guideRangeValue)) |
|
.text(guideFormat(guideRangeValue)); |
|
}); |
|
}); |
|
|
|
dynamic(function() { |
|
if (!selectedGuideText) { |
|
return; |
|
} |
|
|
|
d3.select("body").classed("disable-user-select", false); |
|
|
|
var node = (selectedPosterior ? yGuideText : xGuideText).node(); |
|
|
|
range = document.createRange(); |
|
// range.selectNode often selects too much. |
|
range.setStart(node, 0); |
|
range.setEnd(node, 1); |
|
|
|
selection = window.getSelection(); |
|
selection.removeAllRanges(); |
|
selection.addRange(range); |
|
}); |
|
|
|
/** Series **/ |
|
|
|
var seriesResolution = 200; |
|
|
|
var seriesPoints = []; |
|
for (var i = 0; i <= seriesResolution; ++i) { |
|
seriesPoints.push(i / seriesResolution); |
|
} |
|
|
|
// Separate groups to ensure that circles are on top of all paths. |
|
var seriesPaths = svgg.append("g") |
|
.classed("series", true); |
|
var seriesCircles = svgg.append("g") |
|
.classed("series", true); |
|
function series(variable) { |
|
var line = d3.svg.line() |
|
.x(x) |
|
.y(function(d) { return y(bayesAt(variable, d)); }); |
|
|
|
seriesPaths.append("path") |
|
.classed(variable, true) |
|
.datum(variable) |
|
.call(dynamic, function(s) {s |
|
.attr("d", line(seriesPoints)); |
|
}); |
|
|
|
seriesCircles.append("circle") |
|
.classed(variable, true) |
|
.datum(variable) |
|
.attr("r", 5) |
|
.call(dynamic, function(s) {s |
|
.attr("cx", x(model[variable])) |
|
.attr("cy", y(posterior)); |
|
}); |
|
} |
|
|
|
series("specificity"); |
|
series("sensitivity"); |
|
series("prior"); |
|
|
|
/** Slider Thumb Shadow **/ |
|
|
|
var sliderThumbShadow = svgg.append("filter") |
|
.attr("id", "slider-thumb-shadow") |
|
// Tell SVG how big the filter might be. |
|
.attr("x", "-50%") |
|
.attr("y", "-50%") |
|
.attr("width", "200%") |
|
.attr("height", "200%"); |
|
|
|
sliderThumbShadow.append("feGaussianBlur") |
|
.attr("in", "SourceAlpha") |
|
.attr("stdDeviation", 1.1) |
|
.attr("result", "blur"); |
|
|
|
sliderThumbShadow.append("feOffset") |
|
.attr("in", "blur") |
|
.attr("dy", "1") |
|
.attr("result", "shadow"); |
|
|
|
var shadowMerge = sliderThumbShadow.append("feMerge"); |
|
shadowMerge.append("feMergeNode").attr("in", "shadow"); |
|
shadowMerge.append("feMergeNode").attr("in", "SourceGraphic"); |
|
|
|
/** Slider **/ |
|
|
|
var thumbMajor = 10; |
|
var thumbMinor = 16; |
|
var hitZoneExtension = 20; |
|
|
|
var sliderXProperties = { |
|
v: "x", v1: "x1", v2: "x2", |
|
scale: x, dimension: "width", |
|
r: "rx", c: "cx" |
|
}; |
|
var sliderYProperties = { |
|
v: "y", v1: "y1", v2: "y2", |
|
scale: y, dimension: "height", |
|
r: "ry", c: "cy" |
|
}; |
|
function slider(variable, offset, orientation) { |
|
if (orientation === undefined) { |
|
orientation = "horizontal"; |
|
} |
|
switch (orientation) { |
|
case "horizontal": |
|
var major = sliderXProperties; |
|
var minor = sliderYProperties; |
|
break; |
|
case "vertical": |
|
var major = sliderYProperties; |
|
var minor = sliderXProperties; |
|
break; |
|
default: |
|
throw new TypeError("invalid orientation "+orientation); |
|
} |
|
|
|
var slider = svgg.append("g") |
|
.classed("slider", true) |
|
.classed(variable, true) |
|
.datum(variable); |
|
|
|
slider.append("line") |
|
.classed("track", true) |
|
.classed("maximum", true) |
|
.attr(major.v1, major.scale(0)) |
|
.attr(major.v2, major.scale(1)) |
|
.attr(minor.v1, offset) |
|
.attr(minor.v2, offset); |
|
|
|
slider.append("line") |
|
.classed("track", true) |
|
.classed("minimum", true) |
|
.attr(major.v1, major.scale(0)) |
|
.attr(minor.v1, offset) |
|
.attr(minor.v2, offset) |
|
.call(dynamic, function(s) {s |
|
.attr(major.v2, major.scale(model[variable])); |
|
}); |
|
|
|
slider.append("rect") |
|
.classed("hit-zone", true) |
|
.attr(major.v, |
|
Math.min(major.scale(0), major.scale(1)) - hitZoneExtension) |
|
.attr(major.dimension, |
|
Math.abs(major.scale(1) - major.scale(0)) + 2 * hitZoneExtension) |
|
.attr(minor.v, offset - thumbMinor / 2) |
|
.attr(minor.dimension, thumbMinor); |
|
|
|
slider.append("ellipse") |
|
.classed("thumb", true) |
|
.attr(major.r, thumbMajor / 2) |
|
.attr(minor.r, thumbMinor / 2) |
|
.attr("filter", "url(#slider-thumb-shadow)") |
|
.attr(minor.c, offset) |
|
.call(dynamic, function(s) {s |
|
.attr(major.c, major.scale(model[variable])) |
|
.style("stroke", function(d) { |
|
return selectedVariable === d ? "black" : null; |
|
}); |
|
}); |
|
|
|
guideLineStarts[variable] = offset; |
|
} |
|
|
|
var sliderSpacing = 24; |
|
var firstSliderY = 445; |
|
slider("prior", firstSliderY); |
|
slider("sensitivity", firstSliderY + sliderSpacing); |
|
slider("specificity", firstSliderY + sliderSpacing * 2); |
|
|
|
slider("posterior", -70, "vertical") |
|
|
|
/** Cursor **/ |
|
|
|
d3.selectAll("main svg, body") |
|
.call(dynamic, function(s) {s |
|
.style("cursor", controlState === "inert" ? null : |
|
selectedPosterior ? "row-resize" : "col-resize"); |
|
}); |
|
|
|
// Don't want to select axis labels while dragging. |
|
d3.select("body") |
|
.call(dynamic, function(s) {s |
|
.classed("disable-user-select", |
|
controlState !== "inert" && !selectedGuideText); |
|
}); |
|
|
|
/*** Controller ***/ |
|
|
|
function mousePositionValue(scale) { |
|
if (scale === undefined) { |
|
scale = x; |
|
} |
|
|
|
var m = d3.mouse(svgg.node()); |
|
return clamp(scale.invert(m[scale === x ? 0 : 1])); |
|
} |
|
|
|
// To prevent moveover and mousemove from interfering with each other. |
|
var ignoreNextMouseMove = false; |
|
|
|
function focusOn(variable) { |
|
if (variable === "posterior") { |
|
selectedPosterior = true; |
|
} else { |
|
selectedPosterior = false; |
|
selectedVariable = variable; |
|
} |
|
ignoreNextMouseMove = true; |
|
explicitFocusValue = null; |
|
return "focused"; |
|
} |
|
|
|
function focusOnWithValueFromMousePosition(variable) { |
|
focusOn(variable); |
|
explicitFocusValue = selectedPosterior ? |
|
bayesFind(selectedVariable, mousePositionValue(y)) : |
|
mousePositionValue(); |
|
return "focused"; |
|
} |
|
|
|
if (svg.node().checkIntersection === undefined) { |
|
function overlaps(t1, t2, x1, x2) { |
|
return t1 < x1 ? t2 >= x1 : t1 <= x2; |
|
} |
|
|
|
// For Firefox, hack an adequate alternative. |
|
svg.node().checkIntersection = function(element, rect) { |
|
var left = rect.x - margin.left; |
|
var right = left + rect.width; |
|
var top = rect.y - margin.top; |
|
var bottom = top + rect.height; |
|
if (element === horizontalGuideLine.node()) { |
|
var y = +horizontalGuideLine.attr("y1"); |
|
var x1 = +horizontalGuideLine.attr("x1"); |
|
var x2 = +horizontalGuideLine.attr("x2"); |
|
return between(y, top, bottom) && |
|
overlaps(left, right, x1, x2); |
|
} else { |
|
var x = +verticalGuideLine.attr("x1"); |
|
// Index reversed since we draw stroke upwards. |
|
var y1 = +verticalGuideLine.attr("y2"); |
|
var y2 = +verticalGuideLine.attr("y1"); |
|
return between(x, left, right) && |
|
overlaps(top, bottom, y1, y2); |
|
} |
|
} |
|
} |
|
|
|
var isNearThreshhold = 20; |
|
var isNearRect = svg.node().createSVGRect(); |
|
isNearRect.width = isNearRect.height = 2 * isNearThreshhold; |
|
function isNear(selection) { |
|
var m = d3.mouse(svg.node()); |
|
isNearRect.x = m[0] - isNearThreshhold; |
|
isNearRect.y = m[1] - isNearThreshhold; |
|
return svg.node().checkIntersection(selection.node(), isNearRect); |
|
} |
|
function movedAway(selection) { |
|
return !isNear(selection); |
|
} |
|
|
|
function becomeFocusedIfIsNearGuideLines() { |
|
return (isNear(verticalGuideLine) || |
|
isNear(horizontalGuideLine)) && "focused"; |
|
} |
|
|
|
function becomeInertIfMovedAwayFromGuideLines() { |
|
if (isNear(verticalGuideLine)) { |
|
selectedPosterior = false; |
|
} else if (isNear(horizontalGuideLine)) { |
|
selectedPosterior = true; |
|
} |
|
|
|
return movedAway(verticalGuideLine) && |
|
movedAway(horizontalGuideLine) && "inert"; |
|
} |
|
|
|
function updateSelectedVariableFromMousePosition() { |
|
if (selectedPosterior) { |
|
posterior = mousePositionValue(y); |
|
} else { |
|
model[selectedVariable] = mousePositionValue(); |
|
} |
|
} |
|
|
|
function activate(variable) { |
|
if (variable === "posterior") { |
|
selectedPosterior = true; |
|
} else if (controlState !== "focused" && variable !== undefined) { |
|
selectedVariable = variable; |
|
} |
|
explicitFocusValue = null; |
|
selectedGuideText = false; |
|
updateSelectedVariableFromMousePosition(); |
|
return "active"; |
|
} |
|
|
|
svgg.selectAll(".series circle, .slider .thumb") |
|
.call(on, "mouseover", { |
|
inert: focusOn, |
|
focused: focusOn |
|
}) |
|
.call(on, "dblclick", function(variable) { |
|
selectedGuideText = true; |
|
}); |
|
|
|
svgg.selectAll(".series path, .slider .hit-zone") |
|
.call(on, "mousemove", { |
|
inert: focusOnWithValueFromMousePosition, |
|
focused: focusOnWithValueFromMousePosition |
|
}) |
|
.call(on, "mousedown", activate); |
|
|
|
d3.select("main") |
|
.call(on, "mousemove", function() { |
|
if (ignoreNextMouseMove) { |
|
ignoreNextMouseMove = false; |
|
return; |
|
} |
|
|
|
return transition({ |
|
inert: becomeFocusedIfIsNearGuideLines, |
|
focused: becomeInertIfMovedAwayFromGuideLines, |
|
active: updateSelectedVariableFromMousePosition |
|
}); |
|
}) |
|
.call(on, "mousedown", { |
|
inert: function() { |
|
selectedGuideText = false; |
|
}, |
|
focused: activate |
|
}) |
|
.call(on, "mouseup", { |
|
active: d3.functor("focused") |
|
}); |
|
|
|
d3.select("body") |
|
.call(on, "keydown", function() { |
|
switch (d3.event.keyCode) { |
|
case 37: // left |
|
nudge(selectedVariable, -0.001); |
|
break; |
|
case 39: // right |
|
nudge(selectedVariable, 0.001); |
|
break; |
|
case 38: // up |
|
nudge("posterior", 0.001, posteriorFindTolerance); |
|
break; |
|
case 40: // down |
|
nudge("posterior", -0.001, -posteriorFindTolerance); |
|
break; |
|
default: |
|
return; |
|
} |
|
controlState = "focused"; |
|
explicitFocusValue = null; |
|
d3.event.preventDefault(); // to avoid scrolling. |
|
}); |
|
</script> |
|
|
|
<div id="details"> |
|
|
|
<div id="example"> |
|
<h3>Example:</h3> |
|
<div class="switch-extreme-prior"> |
|
|
|
<div class="case normal"> |
|
<p>Imagine there is generally |
|
<span class="a-an-prior">{a|an}</span> |
|
<span class="percent prior">%%</span> chance <br> |
|
of rain on any given day where you live. Siri says, “It looks like there will be some rain today.”</p> |
|
<p>Siri |
|
<span class="switch-f-score"> |
|
<span class="case lies">lies:</span> |
|
<span class="case tea">would be better off reading tea leaves:</span> |
|
<span class="case sources">could use some better sources:</span> |
|
<span class="case majored">majored in meteorology:</span> |
|
<span class="case makes">makes the rain:</span> |
|
</span> |
|
</p> |
|
|
|
<div class="switch-classification-contradition"> |
|
<div class="case true"> |
|
<ul> |
|
<li>When it rains, |
|
<div>Siri <span class="sensitivity">never</span> predicts rain.</div></li> |
|
<li>When it doesn't rain, |
|
<div>Siri <span class="specificity">always</span> predicts no rain.</div></li> |
|
</ul> |
|
|
|
<p>That's weird.</p> |
|
|
|
<ul> |
|
<li>Siri predicts rain, |
|
<div>so then it <span class="sensitivity">won't</span> rain.</div> |
|
</li> |
|
<li>But with no rain, |
|
<div>Siri <span class="specificity">wouldn't</span> have predicted rain in the first place.</div> |
|
</li> |
|
</ul> |
|
</div> |
|
<div class="case false"> |
|
<ul> |
|
<li>When it rains, |
|
<div>Siri predicts rain |
|
<span class="percent sensitivity">%%</span> of the time.</div></li> |
|
<li>When it doesn't rain, |
|
<div>Siri predicts no rain |
|
<span class="percent specificity">%%</span> of the time.</div></li> |
|
</ul> |
|
</div> |
|
</div> |
|
|
|
<p> |
|
<span class="switch-classification-contradition"> |
|
<span class="case true"> |
|
Ignore Siri. Conclude that there's still |
|
</span> |
|
<span class="case false"> |
|
By Bayes rule, you should conclude that there's |
|
</span> |
|
</span> |
|
<span class="a-an-posterior">{a|an}</span> |
|
<span class="percent posterior">%%</span> chance of rain today. |
|
<span class="switch-outlook"> |
|
<span class="case sun">☀</span> |
|
<span class="case cloud">☁</span> |
|
<span class="case umbrella">☂</span> |
|
</span> |
|
</p> |
|
|
|
</div> |
|
|
|
<div class="case zero"> |
|
<p>Suppose you believe there is <span class="prior">absolutely no chance</span> that it will rain where you live.</p> |
|
<p>Then no report will convince you otherwise, and you likely live in LA.</p> |
|
</div> |
|
|
|
<div class="case one"> |
|
<p>Suppose you are <span class="prior">certain</span> it rains every day where you live.</p> |
|
<p>Then no report will convince you otherwise, and you may be living in the Matrix.</p> |
|
</div> |
|
|
|
</div> |
|
</div> |
|
<script> |
|
|
|
/** Example **/ |
|
|
|
var percentFormat = d3.format("%"); |
|
|
|
function formatExampleVariable(variable) { |
|
d3.selectAll("#example .percent."+variable) |
|
.call(dynamic, function(s) {s |
|
.text(percentFormat(model[variable])); |
|
}); |
|
|
|
d3.selectAll("#example .a-an-"+variable) |
|
.call(dynamic, function(s) { |
|
var v = Math.round(model[variable] * 100); |
|
s.text(v === 8 || v === 11 || v === 18 || between(v, 80, 89) |
|
? "an" : "a"); |
|
}); |
|
} |
|
|
|
formatExampleVariable("prior"); |
|
formatExampleVariable("posterior"); |
|
formatExampleVariable("sensitivity"); |
|
formatExampleVariable("specificity"); |
|
|
|
d3.select("#example") |
|
.call(dynamic, function(s) {s |
|
.selectAll(".case").style("display", "none"); |
|
}); |
|
|
|
function displayExampleSwitch(switchName, toShow) { |
|
var switchSelector = "#example .switch-"+switchName; |
|
dynamic(function(s) { |
|
var c = d3.selectAll(switchSelector+" > ."+toShow()); |
|
if (c.empty()) { |
|
c = d3.selectAll(switchSelector+" > .default"); |
|
} |
|
c.style("display", null); |
|
}); |
|
} |
|
|
|
displayExampleSwitch("f-score", function() { |
|
return isClassificationContradition() ? "lies" : |
|
posterior < prior ? "tea" : |
|
fScore < 0.9 ? "sources" : |
|
fScore < 1 ? "majored" : |
|
"makes"; |
|
}); |
|
|
|
displayExampleSwitch("outlook", function() { |
|
return posterior < 0.1 ? "sun" : |
|
posterior < 0.7 ? "cloud" : |
|
"umbrella" |
|
}); |
|
|
|
displayExampleSwitch("extreme-prior", function() { |
|
return prior === 0 ? "zero" : |
|
prior === 1 ? "one" : |
|
"normal"; |
|
}); |
|
|
|
displayExampleSwitch("classification-contradition", |
|
isClassificationContradition); |
|
</script> |
|
|
|
<footer><address> |
|
<div id="credits"> |
|
<div><span> |
|
William Taysom 2014 |
|
</span> |
|
<a class="license" rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="Creative Commons License" style="border-width:0; vertical-align: top" src="https://i.creativecommons.org/l/by-nc-sa/4.0/80x15.png" width="80" height="15"/></a></div> |
|
<div>with thanks to |
|
<a href="http://www.tlhiv.org/ltxpreview/">LaTeX Previewer</a> and |
|
<a href="http://d3js.org/">d3</a>.</div> |
|
</address></footer> |
|
|
|
</div> |
|
</main> |