Published
Edited
Feb 14, 2022
Fork of Normal Table
Importers
4 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// Generate the main table. The variable "start" should be
// either 0 (in which case we accumulate from 0) or
// anythihng else (in which case we accumulate from -infty)
function make_normal_table(start) {
let div = d3.create("div");

let table = div
.append("table")
.attr("id", "table")
.attr("border", "0")
.attr("align", "center")
.attr("cellspacing", "2")
.attr("cellpadding", 3);

let first_row = table.append("tr");
first_row.append("th").attr("class", "left").html("Z");
for (let j = 0; j < 0.09999; j = j + 0.01) {
first_row
.append("th")
.attr("col", `${Math.round(100 * j)}`)
.text(d3.format("0.2f")(j));
}
let initial_idx;
if (start == 0) {
initial_idx = 0;
} else {
initial_idx = -31;
}
for (let i = initial_idx; i < 31; i++) {
let row = d3.create("tr");
row
.append("th")
.text(d3.format("0.1f")(i / 10))
.attr("row", `${i}`)
.attr("class", "left");
for (let j = 0; j < 10; j++) {
let td = row
.append("td")
.attr("row", `${i}`)
.attr("col", `${j}`)
.attr("title", pic(i, j, start).outerHTML)
.text(d3.format("0.4f")(P(i, j, start)));

const tip = tippy(td.node(), {
delay: [50, 0],
duration: [200, 50],
placement: "top",
arrow: true
});
}
table.append(() => row.node());
}

div
.selectAll("td")
.on("mouseover", function () {
let row = d3.select(this).attr("row");
let col = d3.select(this).attr("col");
d3.selectAll(`[row="${row}"]`).attr("bgcolor", "#ddd");
d3.selectAll(`[col="${col}"]`).attr("bgcolor", "#ddd");
})
.on("mouseleave", function () {
let row = d3.select(this).attr("row");
let col = d3.select(this).attr("col");
d3.selectAll(`[row="${row}"]`).attr("bgcolor", "#fff");
d3.selectAll(`[col="${col}"]`).attr("bgcolor", "#fff");
});

return div.node();
}
Insert cell
// Generate the pictures that go in the tooltips
function pic(i, j, start, size) {
let width, height;
if (size) {
width = size;
height = size / 2;
} else {
width = 230;
height = 100;
}
let pad = 5;
let div = d3
.create('div')
.attr('class', 'pic')
.style('width', `${width}px`)
.style('height', `${height + 40}px`);
//.style('background-color', 'gray');

let pic_container = div
.append('div')
.style('width', `${width + 1}px`)
.style('height', `${height + 1}px`);
let pic = pic_container
.append('svg')
.attr('width', width)
.attr('height', height)
.style('background-color', 'white');

let xmin = -3.4;
let xmax = 3.4;
let ymin = -0.05;
let ymax = 0.5;
let x_scale = d3
.scaleLinear()
.domain([xmin, xmax])
.range([pad, width - pad]);
let y_scale = d3
.scaleLinear()
.domain([ymin, ymax])
.range([height - pad, pad]);
let pts_to_path = d3
.line()
.x(function(d) {
return x_scale(d[0]);
})
.y(function(d) {
return y_scale(d[1]);
});

let dx = 0.01;
let Z = i / 10 + j / 100;
if (start == 0) {
let fill_pts = d3.range(0, Z + dx, dx).map(x => [x, normal_pdf(x)]);
fill_pts = [[0, 0]].concat(fill_pts).concat([[Z, 0]]);
pic
.append('path')
.attr('class', 'fill')
.attr('d', pts_to_path(fill_pts))
.attr('stroke', 'none')
.attr('fill', 'lightblue');
} else {
let fill_pts = d3.range(xmin, Z + dx, dx).map(x => [x, normal_pdf(x)]);
fill_pts = [[xmin, 0]].concat(fill_pts).concat([[Z, 0]]);
pic
.append('path')
.attr('class', 'fill')
.attr('d', pts_to_path(fill_pts))
.attr('stroke', 'none')
.attr('fill', 'lightblue');
}

pic
.append('line')
.attr('x1', x_scale(xmin))
.attr('x2', x_scale(xmax))
.attr('y1', y_scale(0))
.attr('y2', y_scale(0))
.attr('stroke', 'black');
pic
.append('line')
.attr('x1', x_scale(0))
.attr('x2', x_scale(0))
.attr('y1', y_scale(ymin))
.attr('y2', y_scale(ymax))
.attr('stroke', 'black');
let x_ticks = [-3, -2, -1, 1, 2, 3];
pic
.selectAll('line.xtick')
.data(x_ticks)
.join('line')
.attr('class', 'xtick')
.attr('x1', x => x_scale(x))
.attr('x2', x => x_scale(x))
.attr('y1', y_scale(0))
.attr('y2', y_scale(ymin / 3))
.attr('stroke', 'black');
pic
.selectAll('text.xtick')
.data(x_ticks)
.join('text')
.attr('class', 'xtick')
.attr('x', x => x_scale(x) - width / 100)
.attr('y', y_scale(1.3 * ymin))
.text(x => x);

let pts = d3.range(xmin, xmax + dx, dx).map(x => [x, normal_pdf(x)]);
pic
.append('path')
.attr('class', 'curve')
.attr('d', pts_to_path(pts))
.attr('stroke', 'black')
.attr('stroke-width', 2.5)
.attr('fill', 'none');

let tex_string = tex`Z=${d3.format("0.2f")(
Z
)}, \: \: \: P \approx ${d3.format("0.4f")(P(i, j, start))}`;
div
.append('div')
.attr('class', 'tex_container')
.style('width', '230px')
.style('margin', '0 auto')
.append(() => tex_string);

return div.node();
}
Insert cell
Insert cell
// Lookup the standard normal CDF of i/10+j/100
// in the table. Also account for starting at
// zero or -infinity.
function P(i, j, start) {
let idx = 10 * i + j;
if (start == 0) {
// Accumulate from zero
// In this case, we need 10*i+j>=0
return ss.standardNormalTable[idx] - 0.5;
} else {
// Accumulate from -infinity
if (idx == -310) {
return 0.0010;
} else if (idx < 0) {
return 1 - ss.standardNormalTable[-idx];
} else {
return ss.standardNormalTable[idx];
}
}
}
Insert cell
function normal_pdf(t) {
return Math.exp((-t * t) / 2) / Math.sqrt(2 * Math.PI);
}
Insert cell
Insert cell
import { radio } from "@jashkenas/inputs"
Insert cell
ss = require('simple-statistics')
Insert cell
d3 = require('d3-selection@2', 'd3-format@2', 'd3-scale@3', 'd3-shape@2', 'd3-array@2')
Insert cell
tippy = require("https://unpkg.com/tippy.js@2.5.4/dist/tippy.all.min.js")
Insert cell
tippy_style = html`<link rel=stylesheet href=${await require.resolve(
'https://unpkg.com/browse/tippy.js@2.5.4/dist/tippy.css'
)}>`
Insert cell
html`
<style>
table {
cursor: default
}
</style>`
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more