|
|
|
|
|
export default function define(runtime, observer) { |
|
|
const main = runtime.module(); |
|
|
|
|
|
|
|
|
main.variable(observer("viewof singleSampleSD")).define("viewof singleSampleSD", ["html"], html => { |
|
|
return html`<div class="control-panel"> |
|
|
<h3><strong>The Population</strong></h3> |
|
|
<h4><strong>Population Mean: 0</strong></h4> |
|
|
<div class="input-group"> |
|
|
<label for="normalSD">Population SD:</label> |
|
|
<input id="normalSD" type="number" min="0.01" step="0.1" value="1"> |
|
|
</div> |
|
|
|
|
|
<hr> |
|
|
|
|
|
<h3><strong>Your Single Sample</strong></h3> |
|
|
<div class="input-group"> |
|
|
<label for="sampleSize">Sample Size:</label> |
|
|
<input id="sampleSize" type="number" min="1" value="50"> |
|
|
</div> |
|
|
|
|
|
<div class="input-group"> |
|
|
<label for="confidenceLevel">Confidence Level (%):</label> |
|
|
<input id="confidenceLevel" type="range" min="1" max="99" value="95" step="1"> |
|
|
<span id="confidenceLevelValue">95%</span> |
|
|
</div> |
|
|
|
|
|
<button id="generateBtn">Generate New Sample</button> |
|
|
|
|
|
<hr> |
|
|
|
|
|
<h4><strong>Guiding Questions:</strong></h4> |
|
|
<h5>1. How does increasing _______ affect the certainty that your sample mean is close to the population mean?</h5> |
|
|
<ul> |
|
|
<li>Sample size</li> |
|
|
<li>Standard deviation</li> |
|
|
</ul> |
|
|
<h5>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?</h5> |
|
|
</div>`; |
|
|
}); |
|
|
|
|
|
|
|
|
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) |
|
|
}; |
|
|
}); |
|
|
|
|
|
|
|
|
main.variable(observer("singleSample")).define("singleSample", ["singleSampleSD", "normrnd"], (params, normrnd) => { |
|
|
return normrnd(0, params.sd, params.sampleSize); |
|
|
}); |
|
|
|
|
|
|
|
|
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}; |
|
|
|
|
|
|
|
|
const mean = d3.mean(sample); |
|
|
const sd = d3.deviation(sample); |
|
|
const se = sd / Math.sqrt(sample.length); |
|
|
|
|
|
|
|
|
const alpha = 1 - (params.confidenceLevel / 100); |
|
|
const zCrit = -d3.quantileNormal(alpha / 2); |
|
|
const halfwidth = zCrit * se; |
|
|
const ciLower = mean - halfwidth; |
|
|
const ciUpper = mean + halfwidth; |
|
|
|
|
|
|
|
|
const svg = d3.create("svg") |
|
|
.attr("width", width) |
|
|
.attr("height", height) |
|
|
.attr("viewBox", [0, 0, width, height]) |
|
|
.attr("style", "max-width: 100%; height: auto;"); |
|
|
|
|
|
|
|
|
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]); |
|
|
|
|
|
|
|
|
const bins = d3.bin() |
|
|
.domain(x.domain()) |
|
|
.thresholds(20) |
|
|
(sample); |
|
|
|
|
|
|
|
|
const binMax = d3.max(bins, d => d.length) / sample.length; |
|
|
const yScale = d3.scaleLinear() |
|
|
.domain([0, binMax]) |
|
|
.range([height - margin.bottom, margin.top]); |
|
|
|
|
|
|
|
|
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"); |
|
|
|
|
|
|
|
|
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"); |
|
|
|
|
|
|
|
|
svg.append("g") |
|
|
.attr("transform", `translate(0,${height - margin.bottom})`) |
|
|
.call(d3.axisBottom(x)); |
|
|
|
|
|
|
|
|
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"); |
|
|
|
|
|
|
|
|
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); |
|
|
|
|
|
|
|
|
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); |
|
|
|
|
|
|
|
|
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(); |
|
|
}); |
|
|
|
|
|
|
|
|
main.variable(observer("normrnd")).define("normrnd", ["d3"], d3 => { |
|
|
return (mean, sd, n) => { |
|
|
const normal = d3.randomNormal(mean, sd); |
|
|
return Array.from({length: n}, normal); |
|
|
}; |
|
|
}); |
|
|
|
|
|
|
|
|
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; |
|
|
} |