Published
Edited
Mar 9, 2020
Insert cell
Insert cell
Insert cell
uniformData = Float64Array.from({length: 1000}, d3.randomUniform())
Insert cell
Type JavaScript, then Shift-Enter. Ctrl-space for more options. Arrow ↑/↓ to switch modes.

Insert cell
qqnorm(uniformData)
Insert cell
histogram(uniformData)
Insert cell
Insert cell
uniform2Data = {
const r1 = d3.randomUniform(0, 1);
const r2 = d3.randomUniform(0, 3);
return Float64Array.from({length: 2000}, (_, i) => (i & 1 ? r1 : r2)());
}
Insert cell
qqnorm(uniform2Data)
Insert cell
histogram(uniform2Data)
Insert cell
Insert cell
normalData = Float64Array.from({length: 1000}, d3.randomNormal())
Insert cell
qqnorm(normalData)
Insert cell
histogram(normalData)
Insert cell
Insert cell
normal2data = {
const r1 = d3.randomNormal(1, 0.2);
const r2 = d3.randomNormal(2, 0.2);
return Float64Array.from({length: 2000}, (_, i) => (i & 1 ? r1 : r2)());
}
Insert cell
qqnorm(normal2data)
Insert cell
histogram(normal2data)
Insert cell
Insert cell
logNormalData = Float64Array.from({length: 1000}, d3.randomLogNormal())
Insert cell
qqnorm(logNormalData)
Insert cell
histogram(logNormalData)
Insert cell
Insert cell
logLogNormalData = logNormalData.map(Math.log)
Insert cell
qqnorm(logLogNormalData)
Insert cell
histogram(logLogNormalData)
Insert cell
Insert cell
exponentialData = Float64Array.from({length: 1000}, d3.randomExponential(1))
Insert cell
qqnorm(exponentialData)
Insert cell
histogram(exponentialData)
Insert cell
Insert cell
paretoData = Float64Array.from({length: 1000}, d3.randomPareto(5))
Insert cell
qqnorm(paretoData)
Insert cell
histogram(paretoData)
Insert cell
Insert cell
function histogram(data) {
const width = 640;
const height = 240;
const margin = ({top: 20, right: 20, bottom: 30, left: 40});

const x = d3.scaleLinear()
.domain(d3.extent(data)).nice()
.range([margin.left, width - margin.right]);

const bins = d3.histogram()
.domain(x.domain())
.thresholds(x.ticks(50))
(data);

const y = d3.scaleLinear()
.domain([0, d3.max(bins, d => d.length)]).nice()
.range([height - margin.bottom, margin.top]);

const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height])
.style("max-width", `${width}px`);
const bar = svg.append("g")
.attr("fill", "steelblue")
.selectAll("rect")
.data(bins)
.join("rect")
.attr("x", d => x(d.x0) + 1)
.attr("width", d => Math.max(0, x(d.x1) - x(d.x0) - 1))
.attr("y", d => y(d.length))
.attr("height", d => y(0) - y(d.length));

svg.append("g")
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x).tickSizeOuter(0));

svg.append("g")
.attr("transform", `translate(${margin.left - 6},0)`)
.call(d3.axisLeft(y).ticks(5))
.call(g => g.select(".domain").remove());

return svg.node();
}
Insert cell
function qqnorm(data) {
const width = 640;
const height = 640;
const margin = ({top: 20, right: 40, bottom: 30, left: 40});

const qy = Float64Array.from(data).sort(d3.ascending);
const n = qy.length;
const a = n <= 10 ? 5 / 8 : 0.5;
const z = i => qnorm((i + a) / (n + 1 - 2 * a));

const x = d3.scaleLinear()
.domain([-3, 3])
.range([margin.left, width - margin.right]);

const regression = x.domain().map(
ss.linearRegressionLine(
ss.linearRegression(
Array.from(qy, (d, i) => ([z(i), d])))));

const y = d3.scaleLinear()
.domain(regression).nice()
.range([height - margin.bottom, margin.top]);

const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height])
.style("max-width", `${width}px`);

svg.append("g")
.attr("transform", `translate(0,${height - margin.bottom + 6})`)
.call(d3.axisBottom(x.copy().interpolate(d3.interpolateRound)).ticks(null, "+f"))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line").clone()
.attr("stroke-opacity", 0.1)
.attr("y1", -height))
.call(g => g.append("text")
.attr("x", width - margin.right)
.attr("y", -3)
.attr("fill", "currentColor")
.attr("font-weight", "bold")
.text("z"));

svg.append("g")
.attr("transform", `translate(${margin.left - 6},0)`)
.call(d3.axisLeft(y.copy().interpolate(d3.interpolateRound)))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line").clone()
.attr("stroke-opacity", 0.1)
.attr("x1", width));

svg.append("line")
.attr("stroke", "currentColor")
.attr("stroke-opacity", 0.3)
.attr("x1", x.range()[0])
.attr("x2", x.range()[1])
.attr("y1", y(regression[0]))
.attr("y2", y(regression[1]));

svg.append("g")
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-width", 1.5)
.selectAll("circle")
.data(d3.range(n))
.join("circle")
.attr("cx", i => x(z(i)))
.attr("cy", i => y(qy[i]))
.attr("r", 3);

return svg.node();
}
Insert cell
qnorm = p => Math.SQRT2 * erfinv(2 * p - 1)
Insert cell
erfinv = {
const a = 8 * (Math.PI - 3) / (3 * Math.PI * (4 - Math.PI));
return x => {
const b = Math.log(1 - x * x);
const c = b / 2 + (2 / (Math.PI * a));
return Math.sign(x) * Math.sqrt(Math.sqrt((c * c) - b / a) - c);
};
}
Insert cell
d3 = require("d3@5", "d3-random@2")
Insert cell
ss = require("simple-statistics@7")
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