block by gabrielflorit 32494d816b9c39c81f3533dee11322d0

Sampling distribution

Full Screen

script.js

// Create margins.
const margin = { top: 30, right: 30, bottom: 30, left: 30 }
const width = 960 - margin.left - margin.right
const height = 240 - margin.top - margin.bottom
const populationSize = 10000

// Create svg.
const svg = d3.select('body').append('svg')
		.attr('width', width + margin.left + margin.right)
		.attr('height', height + margin.top + margin.bottom)
	.append('g')
		.attr('transform', `translate(${margin.left}, ${margin.top})`)

// Create scales.
const x = d3.scaleLinear().range([0, width])
const y = d3.scaleLinear().range([height, 0])

// Helper function to update range output
const updateRangeOutput = (selector, value) =>
	document.querySelector(selector).parentNode.querySelector('output')
		.textContent = value

const updateElement = (selector, value) =>
	document.querySelector(selector).textContent = value

const drawData = () => {

	// Get adjustable parameters.
	const proportion = document.querySelector('input.proportion').value
	const samples = document.querySelector('input.samples').value
	const size = document.querySelector('input.size').value

	// Update parameters next to sliders.
	// updateRangeOutput('input.proportion', d3.format('.0%')(proportion))
	updateElement('span.proportion', d3.format('.0%')(proportion))

	// updateRangeOutput('input.population', d3.format(',')(populationSize))

	// updateRangeOutput('input.samples', d3.format(',')(samples))
	updateElement('span.samples', d3.format(',')(samples))

	// updateRangeOutput('input.size', d3.format(',')(size))
	updateElement('span.size', d3.format(',')(size))

	// Create the population.
	const population = d3.range(populationSize)
		.map((v, index, array) => index < proportion * array.length ? 1 : 0)

	// For a total of `samples` trials,
	const data = d3.range(samples)
		.map(v => {

			// create a random sample of size `size`,
			const sample = d3.shuffle(population).slice(0, size)

			// and return mean.
			return d3.mean(sample)

		})

	// Bin data.
	const bins = d3.histogram()
		.domain(x.domain())
		.thresholds(x.ticks(Math.min(100, size)))
		(data)

	// Set y-scale based on data.
	y.domain([0, d3.max(bins, d => d.length)])

	// Remove all elements.
	svg.selectAll('*').remove()

	// Draw data.
	const bar = svg.selectAll('.bar')
			.data(bins)
		.enter().append('g')
			.attr('class', 'bar')
			.attr('transform', d => `translate(${x(d.x0)}, ${y(d.length)})`)

	// Draw bars.
	bar.append('rect')
			.attr('x', 0)
			.attr('width', d => x(d.x1) - x(d.x0))
			.attr('height', d => height - y(d.length))

	// Draw axis.
	svg.append('g')
			.attr('class', 'axis axis--x')
			.attr('transform', `translate(0, ${height})`)
			.call(d3.axisBottom(x))

}

d3.selectAll('input').on('input', d => drawData())

drawData()

index.html

<!DOCTYPE html>
<title>Sampling distribution</title>
<link href='dist.css' rel='stylesheet' />
<body>

	<div class='intro'>

		<p>Given a population where <i>p</i> = <span class="group"><span class="proportion label"></span><input class="proportion" type="range" min="0" max="1" step="0.01" value="0.2" /></span>,</p>

		<p>draw
		a histogram of <span class="group"><span class="samples label"></span> sample proportions<input class="samples" type="range" min="1" max="1000" step="1" value="200" /></span></p>

		<p>where the samples are of
		size <span class="group"><i>n</i> = <span class="size label"></span><input class="size" type="range" min="1" max="1000" step="1" value="100" /></span>.</p>

		<p>We call this a <b>sampling distribution</b>.</p>

	</div>

  <script src="https://d3js.org/d3.v4.min.js"></script>
  <script src='dist.js'></script>
</body>

dist.css

body{font-family:sans-serif}.intro{margin:0 1em}span.label{display:inline-block;text-align:center;font-weight:700;width:2.75em}span.group{display:inline-block;white-space:nowrap}input{position:relative;top:2px;width:12em}text{font:10px sans-serif}svg{display:block}.bar rect{fill:#4682b4;shape-rendering:crispEdges}

dist.js

var margin={top:30,right:30,bottom:30,left:30},width=960-margin.left-margin.right,height=240-margin.top-margin.bottom,populationSize=1e4,svg=d3.select("body").append("svg").attr("width",width+margin.left+margin.right).attr("height",height+margin.top+margin.bottom).append("g").attr("transform","translate("+margin.left+", "+margin.top+")"),x=d3.scaleLinear().range([0,width]),y=d3.scaleLinear().range([height,0]),updateRangeOutput=function(t,e){return document.querySelector(t).parentNode.querySelector("output").textContent=e},updateElement=function(t,e){return document.querySelector(t).textContent=e},drawData=function(){var t=document.querySelector("input.proportion").value,e=document.querySelector("input.samples").value,a=document.querySelector("input.size").value;updateElement("span.proportion",d3.format(".0%")(t)),updateElement("span.samples",d3.format(",")(e)),updateElement("span.size",d3.format(",")(a));var n=d3.range(populationSize).map(function(e,a,n){return a<t*n.length?1:0}),r=d3.range(e).map(function(t){var e=d3.shuffle(n).slice(0,a);return d3.mean(e)}),i=d3.histogram().domain(x.domain()).thresholds(x.ticks(Math.min(100,a)))(r);y.domain([0,d3.max(i,function(t){return t.length})]),svg.selectAll("*").remove();var o=svg.selectAll(".bar").data(i).enter().append("g").attr("class","bar").attr("transform",function(t){return"translate("+x(t.x0)+", "+y(t.length)+")"});o.append("rect").attr("x",0).attr("width",function(t){return x(t.x1)-x(t.x0)}).attr("height",function(t){return height-y(t.length)}),svg.append("g").attr("class","axis axis--x").attr("transform","translate(0, "+height+")").call(d3.axisBottom(x))};d3.selectAll("input").on("input",function(t){return drawData()}),drawData();

style.styl

body
	font-family sans-serif
	
.intro
	margin 0 1em
	
span.label
	display inline-block
	text-align center
	font-weight bold
	width 2.75em
	
span.group
	display inline-block
	white-space nowrap
	
input
	position relative
	top 2px
	width 12em

text
	font 10px sans-serif

svg
	display block
	
.bar rect
	fill steelblue
	shape-rendering crispEdges