Code
viewof pop_shape = {
const input = Inputs.select(
["Exponential (right-skewed)", "Uniform (flat)", "U-shaped (bimodal)"],
{value: "Exponential (right-skewed)", label: "Population shape:"}
);
input.addEventListener('pointerdown', e => e.stopPropagation());
input.addEventListener('touchstart', e => e.stopPropagation());
input.addEventListener('mousedown', e => e.stopPropagation());
input.addEventListener('click', e => e.stopPropagation());
return input;
}
viewof sample_n = {
const input = Inputs.range([1, 100], {value: 5, step: 1, label: "Sample size n:"});
input.addEventListener('pointerdown', e => e.stopPropagation());
input.addEventListener('touchstart', e => e.stopPropagation());
input.addEventListener('mousedown', e => e.stopPropagation());
input.addEventListener('click', e => e.stopPropagation());
input.addEventListener('wheel', e => e.stopPropagation());
input.addEventListener('pointermove', e => e.stopPropagation());
input.addEventListener('touchmove', e => e.stopPropagation());
return input;
}
// Population parameters
popParams = {
if (pop_shape === "Exponential (right-skewed)") return {mu: 1.0, sigma: 1.0, label: "Exp(1)"};
if (pop_shape === "Uniform (flat)") return {mu: 0.5, sigma: Math.sqrt(1/12), label: "Uniform(0,1)"};
return {mu: 0.5, sigma: Math.sqrt(1/8), label: "U-shaped"};
}
// Simulate sample means
simMeans = {
const nSim = 4000;
const rng = d3.randomLcg(42);
const means = [];
for (let s = 0; s < nSim; s++) {
let sum = 0;
for (let i = 0; i < sample_n; i++) {
let y;
if (pop_shape === "Exponential (right-skewed)") {
y = -Math.log(1 - rng());
} else if (pop_shape === "Uniform (flat)") {
y = rng();
} else {
const u = rng();
y = u < 0.5 ? rng() * 0.35 : 0.65 + rng() * 0.35;
}
sum += y;
}
means.push({ybar: sum / sample_n});
}
return means;
}
// CLT normal overlay data
cltNormal = {
const {mu, sigma} = popParams;
const se = sigma / Math.sqrt(sample_n);
const xMin = mu - 4 * se;
const xMax = mu + 4 * se;
const pts = [];
for (let i = 0; i <= 200; i++) {
const x = xMin + (i / 200) * (xMax - xMin);
const z = (x - mu) / se;
const density = Math.exp(-0.5 * z * z) / (Math.sqrt(2 * Math.PI) * se);
pts.push({x, density});
}
return pts;
}
normalityScore = {
if (sample_n >= 30) return {msg: "โ
n โฅ 30: CLT approximation is reliable", color: "#d4edda"};
if (sample_n >= 15) return {msg: "โ ๏ธ n โฅ 15: Reasonable for mild skewness", color: "#fff3cd"};
return {msg: "โ n < 15: Distribution still resembles population", color: "#f8d7da"};
}
md`**Population:** ${popParams.label} โ ฮผ = ${popParams.mu.toFixed(3)}, ฯ = ${popParams.sigma.toFixed(3)}
**SE of mean:** ฯ/โn = ${(popParams.sigma / Math.sqrt(sample_n)).toFixed(4)}
<div style="background: ${normalityScore.color}; padding: 5px; border-radius: 4px; margin-top: 6px;">
${normalityScore.msg}
</div>`