Last active
February 17, 2016 10:04
-
-
Save standarderror/4cbf70c74747c829e4a6 to your computer and use it in GitHub Desktop.
20150111 Regression Scatterplot Animation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<style> | |
#chart { | |
} | |
.circle { | |
stroke: '#fff'; | |
} | |
.label { | |
fill: #777; | |
} | |
.axis path, | |
.axis line { | |
fill: none; | |
stroke: black; | |
shape-rendering: crispEdges; | |
} | |
.axis text { | |
font-family: sans-serif; | |
font-size: 11px; | |
} | |
.grid .tick { | |
stroke: lightgrey; | |
opacity: 0.7; | |
} | |
.grid path { | |
stroke-width: 0; | |
} | |
</style> | |
<body> | |
<div id='chart' align='center'></div> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script src="regression_animation.js"></script> | |
</body> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var width = 100 | |
, height = 100 | |
, margin = {top: 8, right: 8, bottom: 8, left: 8} | |
, uid = 0 | |
, datasetSize = 3 // start dataset | |
, interval = 1500 | |
, maxData = 10 | |
, minData = 3; | |
var color = d3.scale.category10(); | |
// DATASET | |
// [0] is uid | |
// [1] is x value | |
// [2] is y value | |
// [3] for colour | |
var dataset = []; | |
for (var i = 0 ; i < datasetSize ; i++) { | |
dataset.push(generateDatum()); | |
} | |
// set up the scales | |
var xScale = d3.scale.linear() | |
.range([0, width]) | |
.domain([0, 100]) | |
.nice(); | |
var yScale = d3.scale.linear() | |
.range([height, 0]) | |
.domain([0, 100]) | |
.nice(); | |
// set the axes | |
var xAxis = d3.svg.axis() | |
.scale(xScale) | |
.orient('bottom') | |
.outerTickSize(5) | |
.ticks(8) | |
; | |
var yAxis = d3.svg.axis() | |
.scale(yScale) | |
.orient('left') | |
.outerTickSize(5) | |
.ticks(8) | |
; | |
// append the svg to the body of the page and set the width and height | |
// append a 'g' element to group the circles together and 'translate' | |
var svg = d3.select("#chart").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 + ")") | |
; | |
// draw x gridlines | |
var svgXaxis = svg.append("g"); | |
svgXaxis.attr("class", "grid") | |
.attr("transform", "translate(0," + height + ")") | |
.call(xAxis | |
.tickSize(-height, 0, 0) | |
.tickFormat("") | |
) | |
; | |
// var svgXaxis = svg.append('g') | |
// .attr("class", "axis") | |
// .attr({'transform': 'translate(0,' + height + ')'}) | |
// .call(xAxis); | |
// draw Y gridlines | |
var svgYaxis = svg.append("g"); | |
svgYaxis.attr("class", "grid") | |
.call(yAxis | |
.tickSize(-width, 0, 0) | |
.tickFormat("") | |
) | |
// var svgYaxis = svg.append('g') | |
// .attr({ | |
// 'class': 'axis label' | |
// }) | |
// .call(yAxis); | |
// draw initial display | |
update(); | |
// add new data | |
var count = 0; | |
var interval = setInterval(function() { | |
// remove if too many, add if too few... | |
if (dataset.length >= maxData) { | |
var indexToRemove = parseInt((Math.random() * dataset.length)); | |
// console.log('Removing point ' + indexToRemove + ': ' + dataset[indexToRemove]); | |
dataset.splice(indexToRemove, 1) | |
} else if (dataset.length <= minData) { | |
var d = generateDatum(); | |
// console.log('Adding new point ' + d); | |
dataset.push(d); | |
// ...else, randomly decide whether to add or remove | |
} else if (Math.random() < 0.5) { | |
var indexToRemove = parseInt((Math.random() * dataset.length)); | |
// console.log('Removing point ' + indexToRemove + ': ' + dataset[indexToRemove]); | |
dataset.splice(indexToRemove, 1) | |
} else { | |
var d = generateDatum(); | |
// console.log('Adding new point ' + d); | |
dataset.push(d); | |
} | |
update(); | |
// update counter | |
count++; | |
if (count >= 5000) { | |
clearInterval(interval); | |
console.log('Finished.'); | |
} | |
}, interval); | |
var line = d3.svg.line().interpolate("monotone") | |
.x(function(d){ return xScale(d.x); }) | |
.y(function(d){ return yScale(d.y); }) | |
function update () { | |
// update the scales' domain | |
xScale.domain([0, d3.max(dataset, function(d) { return d[1]; })]); | |
yScale.domain([0, d3.max(dataset, function(d) { return d[2]; })]); | |
var xMax = d3.max(dataset, function(d) { return d[1]; }); | |
// data-join | |
var dot = svg.selectAll("circle") | |
.data(dataset, function(d) {return d[0]}); | |
// udpate | |
dot.transition() | |
.duration(750) | |
.attr({ | |
"cx": function(d) { return xScale(d[1]); } | |
, "cy": function(d) { return yScale(d[2]); } | |
}) | |
.attr('r','5') | |
.attr("fill", function(d) { return color(d[3]); }) | |
; | |
// enter | |
dot.enter().append("circle") | |
.attr({ | |
"class": "circle" | |
, "cx": function(d) { return xScale(d[1]); } | |
, "cy": function(d) { return yScale(d[2]); } | |
}) | |
.attr('r','0') | |
.attr("fill", function(d) { return color(d[3]); }) | |
.style('opacity', 1e-6) | |
.transition() | |
.duration(200) | |
.ease('bounce') | |
.style('opacity', 1) | |
.attr('r', 10) | |
.transition() | |
.duration(400) | |
.attr('r', 5) | |
; | |
dot.exit() | |
.transition() | |
.duration(700) | |
.attr('r','0') | |
.style('opacity', 1e-6) | |
.remove(); | |
//// Regression line | |
// Calculate line of best fit | |
var yval = dataset.map(function (d) { return parseFloat(d[2]); }); | |
var xval = dataset.map(function (d) { return parseFloat(d[1]); }); | |
var lr = linearRegression(yval,xval); | |
// now you have: | |
// lr.slope | |
// lr.intercept | |
// lr.r2 | |
var myLine = svg.selectAll(".line") | |
.data([lr]) | |
.attr("stroke-width", 1.5) | |
.attr("stroke", "black") | |
; | |
myLine.transition() | |
.attr("class","line") | |
.delay(500) | |
.transition() | |
.attr("x1", xScale(0)+10) | |
.attr("y1", function(d) { | |
return yScale(d[0]); | |
}) | |
.attr("x2", xScale(xMax)-10) | |
.attr("y2", function(d) { | |
return yScale((xMax * d[1]) + d[0] ); | |
}) | |
; | |
myLine.enter() | |
.append("line") | |
.attr("class","line") | |
.attr("x1", xScale(0)+10) | |
.attr("y1", function(d) { | |
return yScale(d[0]); | |
}) | |
.attr("x2", xScale(0)+10) | |
.attr("y2", function(d) { | |
return yScale(d[0]); | |
}) | |
.attr("stroke-width", 0) | |
.transition().duration(750) | |
.attr("stroke-width", 1.5) | |
.attr("stroke", "black") | |
.attr("x2", xScale(xMax)-10) | |
.attr("y2", function(d) { | |
return yScale((xMax * d[1]) + d[0] ); | |
}) | |
; | |
myLine.exit().remove() | |
; | |
// update axes | |
svgXaxis.transition().call(xAxis); | |
svgXaxis.selectAll("text").remove(); // remove tick labels | |
svgYaxis.transition().call(yAxis); | |
svgYaxis.selectAll("text").remove(); | |
} | |
function generateDatum () { | |
return [ | |
uid++ | |
, parseInt(5 + Math.random() * 90) | |
, parseInt(5 + Math.random() * 90) | |
, parseInt((Math.random() * 10)) // + 10 | |
]; | |
} | |
function linearRegression(y,x){ | |
// var lr = {}; | |
var lr = []; | |
var n = y.length; | |
var sum_x = 0; | |
var sum_y = 0; | |
var sum_xy = 0; | |
var sum_xx = 0; | |
var sum_yy = 0; | |
for (var i = 0; i < y.length; i++) { | |
sum_x += x[i]; | |
sum_y += y[i]; | |
sum_xy += (x[i]*y[i]); | |
sum_xx += (x[i]*x[i]); | |
sum_yy += (y[i]*y[i]); | |
} | |
// intercept 0, slope 1 | |
lr[1] = (n * sum_xy - sum_x * sum_y) / (n*sum_xx - sum_x * sum_x); | |
lr[0] = (sum_y - lr[1] * sum_x)/n; | |
// lr['slope'] = (n * sum_xy - sum_x * sum_y) / (n*sum_xx - sum_x * sum_x); | |
// lr['intercept'] = (sum_y - lr.slope * sum_x)/n; | |
//lr['r2'] = Math.pow((n*sum_xy - sum_x*sum_y)/Math.sqrt((n*sum_xx-sum_x*sum_x)*(n*sum_yy-sum_y*sum_y)),2); | |
return lr; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment