// Observable JS module export default function define(runtime, observer) { const main = runtime.module(); // Define inputs and reactive values main.variable(observer("viewof singleSampleSD")).define("viewof singleSampleSD", ["html"], html => { return html`

The Population

Population Mean: 0


Your Single Sample

95%

Guiding Questions:

1. How does increasing _______ affect the certainty that your sample mean is close to the population mean?
2. If the standard deviation is 4, what is the minimum sample size you should consider collecting if you want your sample mean to be likely within 1 of the population mean?
`; }); // Extract input values main.variable(observer("singleSampleSD")).define("singleSampleSD", ["viewof singleSampleSD"], view => { return { sd: parseFloat(view.querySelector("#normalSD").value), sampleSize: parseInt(view.querySelector("#sampleSize").value), confidenceLevel: parseInt(view.querySelector("#confidenceLevel").value) }; }); // Generate random sample main.variable(observer("singleSample")).define("singleSample", ["singleSampleSD", "normrnd"], (params, normrnd) => { return normrnd(0, params.sd, params.sampleSize); }); // Define the plot main.variable(observer("singleSamplePlot")).define("singleSamplePlot", ["d3", "singleSample", "singleSampleSD"], (d3, sample, params) => { const width = 800; const height = 400; const margin = {top: 30, right: 30, bottom: 50, left: 50}; // Calculate statistics const mean = d3.mean(sample); const sd = d3.deviation(sample); const se = sd / Math.sqrt(sample.length); // Calculate CI const alpha = 1 - (params.confidenceLevel / 100); const zCrit = -d3.quantileNormal(alpha / 2); const halfwidth = zCrit * se; const ciLower = mean - halfwidth; const ciUpper = mean + halfwidth; // Create SVG const svg = d3.create("svg") .attr("width", width) .attr("height", height) .attr("viewBox", [0, 0, width, height]) .attr("style", "max-width: 100%; height: auto;"); // Create scales const x = d3.scaleLinear() .domain([-7, 7]) .range([margin.left, width - margin.right]); const y = d3.scaleLinear() .domain([0, 0.5]) .range([height - margin.bottom, margin.top]); // Generate histogram const bins = d3.bin() .domain(x.domain()) .thresholds(20) (sample); // Normalize bin heights const binMax = d3.max(bins, d => d.length) / sample.length; const yScale = d3.scaleLinear() .domain([0, binMax]) .range([height - margin.bottom, margin.top]); // Draw histogram svg.append("g") .selectAll("rect") .data(bins) .join("rect") .attr("x", d => x(d.x0)) .attr("y", d => yScale(d.length / sample.length)) .attr("width", d => Math.max(0, x(d.x1) - x(d.x0) - 1)) .attr("height", d => yScale(0) - yScale(d.length / sample.length)) .attr("fill", "lightblue"); // Draw normal curve (population) const curve = d3.line() .x(d => x(d)) .y(d => y(d3.randomNormal.pdf(d, 0, params.sd))); const points = d3.range(-7, 7, 0.1); svg.append("path") .attr("d", curve(points)) .attr("stroke", "red") .attr("stroke-width", 2) .attr("fill", "none"); // Draw axes svg.append("g") .attr("transform", `translate(0,${height - margin.bottom})`) .call(d3.axisBottom(x)); // Add vertical lines for population and sample means svg.append("line") .attr("x1", x(0)) .attr("x2", x(0)) .attr("y1", margin.top) .attr("y2", height - margin.bottom) .attr("stroke", "red") .attr("stroke-width", 2) .attr("stroke-dasharray", "5,5"); svg.append("line") .attr("x1", x(mean)) .attr("x2", x(mean)) .attr("y1", margin.top) .attr("y2", height - margin.bottom) .attr("stroke", "blue") .attr("stroke-width", 2) .attr("stroke-dasharray", "5,5"); // Add CI line svg.append("line") .attr("x1", x(ciLower)) .attr("x2", x(ciUpper)) .attr("y1", y(0.5)) .attr("y2", y(0.5)) .attr("stroke", "black") .attr("stroke-width", 3); // Add CI endpoints svg.append("line") .attr("x1", x(ciLower)) .attr("x2", x(ciLower)) .attr("y1", y(0.48)) .attr("y2", y(0.52)) .attr("stroke", "black") .attr("stroke-width", 3); svg.append("line") .attr("x1", x(ciUpper)) .attr("x2", x(ciUpper)) .attr("y1", y(0.48)) .attr("y2", y(0.52)) .attr("stroke", "black") .attr("stroke-width", 3); // Add legend const legend = svg.append("g") .attr("transform", `translate(${width - margin.right - 200}, ${margin.top + 20})`); const legendItems = [ { label: "Population distribution", color: "red", type: "line" }, { label: "Population mean", color: "red", type: "dashed" }, { label: "Sample mean", color: "blue", type: "dashed" }, { label: "95% CI", color: "black", type: "line" } ]; legendItems.forEach((item, i) => { legend.append("line") .attr("x1", 0) .attr("x2", 30) .attr("y1", i * 25) .attr("y2", i * 25) .attr("stroke", item.color) .attr("stroke-width", 2) .attr("stroke-dasharray", item.type === "dashed" ? "5,5" : null); legend.append("text") .attr("x", 40) .attr("y", i * 25 + 5) .text(item.label); }); return svg.node(); }); // Helper function for normal random numbers main.variable(observer("normrnd")).define("normrnd", ["d3"], d3 => { return (mean, sd, n) => { const normal = d3.randomNormal(mean, sd); return Array.from({length: n}, normal); }; }); // Add normal PDF calculation d3.randomNormal.pdf = (x, mean, sd) => { return (1 / (sd * Math.sqrt(2 * Math.PI))) * Math.exp(-0.5 * Math.pow((x - mean) / sd, 2)); }; return main; }