hidden-bias / public /anonymization /make-estimates.js
mervenoyan's picture
commit files to HF hub
7341022
window.makeEstimates = function(){
var estimateScale = d3.scaleLinear()
.domain([.5 - .15, .5 + .15]).range([0, c.width])
.interpolate(d3.interpolateRound)
var jitterHeight = 90
var rs = 4 // rect size
var estimates = students[0].coinVals.map(d => ({val: .5, pctHead: .25, x: c.width/2, y: c.height - jitterHeight/2}))
var simulation = d3.forceSimulation(estimates)
.force('collide', d3.forceCollide(rs).strength(.1))
.stop()
function updateEstimates(){
var selectedStudents = students.all.slice(0, sliders.population)
selectedStudents[0].coinVals.map((_, i) => {
estimates[i].pctHead = d3.mean(selectedStudents, d => (d.coinVals[i] < sliders.headsProb) || d.plagerized)
estimates[i].val = (1 - estimates[i].pctHead)/(1 - sliders.headsProb)
})
updateSimulation(60)
}
updateEstimates()
function updateSimulation(ticks=80, yStrength=.005){
var variance = d3.variance(estimates, d => d.val)
var xStength = variance < .0005 ? .3 : .1
estimates.forEach(d => d.targetX = estimateScale(d.val))
simulation
.force('x', d3.forceX(d => d.targetX).strength(xStength))
.force('y', d3.forceY(c.height - jitterHeight/2).strength(yStrength))
.alpha(1)
// .alphaDecay(1 - Math.pow(0.001, 1/ticks))
for (var i = 0; i < ticks; ++i) simulation.tick()
estimates.forEach(d => {
d.x = Math.round(d.x)
d.y = Math.round(d.y)
})
}
updateSimulation(80, 1)
updateSimulation(80, .005)
// Set up DOM
var histogramSel = c.svg.append('g').translate([0, -25])
var axisSel = histogramSel.append('g.axis.state.init-hidden')
var histogramAxis = axisSel.append('g')
var numTicks = 6
var xAxis = d3.axisTop(estimateScale).ticks(numTicks).tickFormat(d3.format('.0%')).tickSize(100)
histogramAxis.call(xAxis).translate([.5, c.height + 5])
middleTick = histogramAxis.selectAll('g').filter((d, i) => i === 3)
middleTick.select('text').classed('bold', 1)
middleTick.select('line').st({stroke: '#000'})
histogramAxis.append('text.bold')
.text('actual non-plagiarism rate')
.translate([c.width/2, 11])
.st({fontSize: '10px'})
var containerSel = histogramSel.append('g#histogram').translate([0.5, .5])
// Selection overlay to highlight individual estimates.
var selectSize = rs*2 + 2
var selectColor = '#007276'
var rectFill = '#007276'
var activeSel = histogramSel.append('g.active.init-hidden.axis')
.st({pointerEvents: 'none'})
activeSel.append('rect')
.at({width: selectSize, height: selectSize, stroke: selectColor, fill: 'none', strokeWidth: 3})
.translate([-selectSize/2, -selectSize/2])
var activeTextHighlight = activeSel.append('rect')
.at({x: -32, width: 32*2, height: 18, y: -25, fill: 'rgba(255,255,255,.6)', rx: 10, ry: 10, xfill: 'red'})
var activeTextSel = activeSel.append('text.est-text.bold')
.text('34%')
.at({textAnchor: 'middle', textAnchor: 'middle', y: '-1em'})
.st({fill: selectColor})
var activePathSel = activeSel.append('path')
.st({stroke: selectColor, strokeWidth: 3})
// Update highlight DOM with current highlight
var curDrawData = {pctHead: .25, val: .5, x: c.width/2, y: c.height - jitterHeight/2}
function setActive(active, dur=0){
if (active !== estimates.active){
estimates.forEach(d => {
d.active = d == active
d.fy = d.active ? d.y : null
})
estimates.active = active
}
students.updateHeadsPos()
sel.flipCircle
.transition().duration(0).delay(d => d.i*5*(dur > 0 ? 1 : 0))
.at({transform: d => slides && slides.curSlide && slides.curSlide.showFlipCircle && d.coinVals[active.index] < sliders.headsProb ?
'scale(1)' : 'scale(.1)'})
flipCoinTimer.stop()
if (dur){
var objI = d3.interpolateObject(curDrawData, active)
flipCoinTimer = d3.timer(ms => {
var t = d3.easeCubicInOut(d3.clamp(0, ms/dur, 1))
drawData(objI(t))
if (t == 1) flipCoinTimer.stop()
})
} else{
drawData(active)
}
function drawData({pctHead, val, x, y}){
activeSel.translate([x + rs/2, y + rs/2])
activeTextSel.text('est. ' + d3.format('.1%')(val))
activePathSel.at({d: `M ${selectSize/2*Math.sign(c.width/2 - x)} -1 H ${c.width/2 - x}`})
var error = Math.abs(val - .5)
var fmt = d3.format(".1%")
var pop = sliders.population
d3.select('.rand-text')
// .html(`${fmt(1 - pctHead)} of students said they had never plagerized. Since about half the students flipped heads and automatically reported plagerizism, we double that to <span class='highlight square blue-box box'>estimate ${fmt(val)}</span> of students haven't plagerized—${error > .1 ? '' : error > .07 ? 'a little ' : 'not '}far from the actual rate of ${fmt(.5)}`)
// .html(`${Math.round((1 - pctHead)*pop)} of ${pop} students said they had never plagiarized. Since about half the students flipped heads and automatically reported plagiarism, we double that rate to <span class='highlight square blue-box box'>estimate ${fmt(val)}</span> of students haven't plagiarized—${error > .4 ? '' : error > .07 ? 'a little ' : 'not '}far from the actual rate of ${fmt(.5)}`)
.html(`Here, ${fmt(1 - pctHead)} students said they had never plagiarized. Doubling that, we <span class='highlight square blue-box box'>estimate ${fmt(val)}</span> of students haven't plagiarized—${error > .1 ? 'quite ' : error > .07 ? 'a little ' : 'not '}far from the actual rate of ${fmt(.5)}`)
curDrawData = {pctHead, val, x, y}
}
}
window.flipCoinTimer = d3.timer(d => d)
var estimateSel = containerSel.appendMany('rect.estimate', estimates)
.at({width: rs, height: rs, stroke: '#fff', fill: rectFill, strokeWidth: .5})
.st({fill: rectFill})
.translate([rs/2, rs/2])
.on('mouseover', (d, i) => {
if (window.slides.curSlide.showHistogram) {
setActive(d)
}
})
function setSelectorOpacity(textOpacity, strokeOpacity) {
activeTextSel.st({opacity: textOpacity})
activeSel.st({opacity: strokeOpacity})
activePathSel.st({opacity: strokeOpacity})
}
function render(transition=false){
estimateSel.translate(d => [d.x, d.y])
setActive(estimates.active)
if (transition){
if (window.flipAllCoinsTimer) window.flipAllCoinsTimer.stop()
window.flipAllCoinsTimer = d3.timer(ms => {
var t = d3.easeExpIn(d3.clamp(0, ms/5000, 1), 20)
if (flipAllCoinsTimer.forceEnd) t = 1
if (t > .028) {
setSelectorOpacity(textOpacity=0, strokeOpacity=0.7)
}
var index = Math.floor((estimates.length - 2)*t) + 1
estimateSel.classed('active', (d, i) => i <= index)
setActive(estimates[index])
// flipCoinsSel.text('Flip coins ' + d3.format('03')(index < 100 ? index : index + 1) + ' times')
flipCoinsSel.text('Flip coins 200 times')
if (t == 1) {
flipAllCoinsTimer.stop()
setSelectorOpacity(textOpacity=1, strokeOpacity=1)
}
})
} else {
setSelectorOpacity(textOpacity=1, strokeOpacity=1)
flipCoinsSel
}
}
window.flipAllCoinsTimer = d3.timer(d => d)
var flipCoinsSel = d3.select('.flip-coins').on('click', () => {
students.all.forEach(student => {
student.coinVals = student.coinVals.map(j => Math.random())
})
updateEstimates()
render(true)
})
d3.select('.flip-coins-once').on('click', flipCoin)
function flipCoin(){
active = estimates[0]
students.all.forEach(student => {
student.coinVals = student.coinVals.map(j => Math.random())
})
active.fy = active.y = c.height - jitterHeight/2
updateEstimates()
estimateSel.translate(d => [d.x, d.y])
estimates.active = null
setActive(active, 1000)
}
Object.assign(estimates, {updateEstimates, setActive, render, flipCoin, axisSel, containerSel, estimateSel, activeSel})
return estimates
}
if (window.init) window.init()