Created
January 12, 2015 03:41
-
-
Save standarderror/893a408346a2d107e7a0 to your computer and use it in GitHub Desktop.
20150115 Regression Animation in Viewbox
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 { | |
/* margin-left:50%; */ | |
} | |
.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; | |
} | |
/* Below makes it fill browser window & scale with */ | |
* { | |
margin: 0; | |
padding: 0; | |
} | |
svg { | |
height: 100%; | |
left: 0; | |
position: absolute; | |
top: 0; | |
right: 0; | |
width: 100%; | |
z-index: -1; | |
} | |
</style> | |
<body> | |
<div id='chart'></div> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script> | |
var width = 500 | |
, height = 500 | |
, margin = {top: 5, right: 5, bottom: 5, left: 5} | |
, uid = 0 | |
, datasetSize = 3 // start dataset | |
, interval = 1500 | |
, maxData = 15 | |
, minData = 3 | |
, dotRadius = 10 | |
, lineWidth = 2; | |
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("body").append("svg") | |
// .attr("width", width + margin.left + margin.right) | |
// .attr("height", height + margin.top + margin.bottom) | |
.attr("viewBox", [0, 0, | |
width + margin.left + margin.right, | |
height + margin.top + margin.bottom | |
]) // .join(' ')) | |
.attr("preserveAspectRatio", "xMinYMax meet") //xMidYMid slice") | |
.append("g") | |
.attr("transform", "translate(" + margin.left + "," + margin.top + ")") | |
// .attr("width", w) | |
// .attr("height", h) | |
; | |
// 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)); | |
dataset.splice(indexToRemove, 1) | |
} else if (dataset.length <= minData) { | |
var d = generateDatum(); | |
dataset.push(d); | |
// ...else, randomly decide whether to add or remove | |
} else if (Math.random() < 0.5) { | |
var indexToRemove = parseInt((Math.random() * dataset.length)); | |
dataset.splice(indexToRemove, 1) | |
} else { | |
var d = generateDatum(); | |
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',dotRadius) | |
.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', dotRadius*2) | |
.transition() | |
.duration(400) | |
.attr('r', dotRadius) | |
; | |
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", lineWidth) | |
.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", lineWidth) | |
.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() * 9)) // + 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; | |
}; | |
</script> | |
</body> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment