Last active
August 29, 2015 14:19
-
-
Save jonahwilliams/168c69d0f8925df7910b to your computer and use it in GitHub Desktop.
Regression Visual II
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"> | |
<head> | |
<style> | |
.axis { | |
font: 10px sans-serif; | |
} | |
path { | |
stroke: steelblue; | |
stroke-width: 2; | |
fill: none; | |
} | |
.errors { | |
fill: red; | |
fill-opacity: 0.4; | |
} | |
.axis path, | |
.axis line { | |
fill: none; | |
stroke: #000; | |
shape-rendering: crispEdges; | |
} | |
</style> | |
</head> | |
<body> | |
<form> | |
<label id="label">0 Degree Polynomial</label> | |
<input type="range" id="points" min="0" max="20" value="0"> | |
</form> | |
<script src="LinearRegression.js"></script> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script> | |
var data = []; | |
for (var i = 0; i < 20; i++){ | |
var a = Math.round(600 * Math.random())/100; | |
data.push({'x': a,'y': 0.9 * Math.sin(a) + (0.25 * Math.random()) - 0.125}); | |
} | |
var degree = document.getElementById("points").value; | |
var newdata = BasisExpand(data, degree); | |
var params = LinearRegression(newdata); | |
var smoothed = []; | |
for (var i = 1; i < 600; i++){ | |
var val = BasisExpand([{'x': i/100, 'y':0}], degree); | |
var y = 0.0; | |
for (var j = 0; j < params.length; j++){ | |
y += params[j][0] * val[0].x[j]; | |
} | |
smoothed.push({'x': i/100, 'y': y}); | |
} | |
var errors = Error(smoothed, data); | |
var margin = {top: 80, right: 180, bottom: 80, left: 180}, | |
width = 960 - margin.left - margin.right, | |
height = 500 - margin.top - margin.bottom; | |
var 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 + ")"); | |
var y = d3.scale.linear() | |
.domain([-1, 1]) | |
.range([height, 0]); | |
var x = d3.scale.linear() | |
.domain([0, 6]) | |
.range([0, width]) | |
var xAxis = d3.svg.axis() | |
.scale(x) | |
.orient("bottom"); | |
var yAxis = d3.svg.axis() | |
.scale(y) | |
.orient("left"); | |
var line = d3.svg.line() | |
.x(function(d) { return x(d.x); }) | |
.y(function(d) { return y(d.y); }); | |
svg.append("g") | |
.attr("class", "x axis") | |
.attr("transform", "translate(0," + height + ")") | |
.call(xAxis) | |
svg.append("g") | |
.attr("class", "y axis") | |
.call(yAxis); | |
svg.selectAll(".dot") | |
.data(data) | |
.enter().append("circle") | |
.attr("class", "dot") | |
.attr("r", 3.5) | |
.attr("cx", function(d){ | |
return x(d.x); | |
}) | |
.attr("cy", function(d){ | |
return y(d.y) | |
}) | |
.style("fill", "black"); | |
svg.append("path") | |
.datum(smoothed) | |
.attr("class", "line") | |
.attr("d", line); | |
svg.selectAll("errors") | |
.data(errors) | |
.enter().append("rect") | |
.attr("class", "errors") | |
.attr("x", function(d){ | |
return x(d.x); | |
}) | |
.attr("y", function(d){ | |
if (d.sy <= d.y){ | |
return y(d.y); | |
} | |
else { | |
return y(d.y) - Math.abs(y(d.y) - y(d.sy)); | |
} | |
}) | |
.attr("width", function(d){ | |
return Math.abs(x(d.y) - x(d.sy)); | |
}) | |
.attr("height", function(d){ | |
return Math.abs(y(d.y) - y(d.sy)); | |
}) | |
.style("fill", "red") | |
.style("fill-opacity", 0.5); | |
d3.select("#points") | |
.on("change", function(d){ | |
var degree = document.getElementById("points").value; | |
d3.select("label") | |
.text(degree + " Degree Polynomial"); | |
var newdata = BasisExpand(data, degree); | |
var params = LinearRegression(newdata); | |
var smoothed = []; | |
for (var i = 1; i < 600; i++){ | |
var val = BasisExpand([{'x': i/100, 'y':0}], degree); | |
var esty = 0.0; | |
for (var j = 0; j < params.length; j++){ | |
esty += params[j][0] * val[0].x[j]; | |
} | |
smoothed.push({'x': i/100, 'y': esty}); | |
} | |
var errors = Error(smoothed, data); | |
svg.selectAll(".line") | |
.datum(smoothed) | |
.transition() | |
.attr("class", "line") | |
.attr("d", line); | |
svg.selectAll(".errors") | |
.data(errors) | |
.transition() | |
.attr("class","errors") | |
.attr("x", function(d){ | |
return x(d.x); | |
}) | |
.attr("y", function(d){ | |
if (d.sy <= d.y){ | |
return y(d.y); | |
} | |
else { | |
return y(d.y) - Math.abs(y(d.y) - y(d.sy)); | |
} | |
}) | |
.attr("width", function(d){ | |
return Math.abs(x(d.y) - x(d.sy)); | |
}) | |
.attr("height", function(d){ | |
return Math.abs(y(d.y) - y(d.sy)); | |
}) | |
.style("fill", "red") | |
.style("fill-opacity", 0.5); | |
}); | |
function BasisExpand(data, degree){ | |
var BasisData = []; | |
for (var i = 0; i < data.length; i++){ | |
var expanded = {'x':[], 'y': data[i].y}; | |
for (var j = 0; j <= degree; j++){ | |
expanded['x'].push(Math.pow(data[i].x - 3, j)); | |
} | |
BasisData.push(expanded); | |
} | |
return BasisData; | |
} | |
function Error(smoothed_data, data){ | |
var errors = []; | |
for(var i = 0; i < data.length; i++){ | |
j = Math.round(data[i].x * 100) - 1; | |
var smooth_y = smoothed_data[j].y; | |
errors.push({'x': data[i].x, 'y': data[i].y, 'sy': smooth_y}); | |
} | |
return errors; | |
} | |
</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
function LinearRegression(data){ | |
var X = [], | |
y = []; | |
for (var i = 0; i < data.length; i ++){ | |
var tempX = []; | |
for(var j = 0; j < data[0]['x'].length; j++){ | |
tempX.push(data[i]['x'][j]); | |
} | |
X.push(tempX); | |
y.push([data[i].y]); | |
} | |
var t = matrixTranspose(X); | |
var hat = matrixMultiply(matrixMultiply(matrixInvert(matrixMultiply(t, X)), t), y); | |
return hat; | |
} | |
function matrixTranspose(a){ | |
//http://stackoverflow.com/questions/4492678/to-swap-rows-with-columns-of-matrix-in-javascript-or-jquery | |
return Object.keys(a[0]).map( | |
function (c) { return a.map(function (r) { return r[c]; }); } | |
); | |
} | |
function matrixMultiply(m1, m2) { | |
// http://tech.pro/tutorial/1527/matrix-multiplication-in-functional-javascript | |
// | |
var result = []; | |
for (var i = 0; i < m1.length; i++) { | |
result[i] = []; | |
for (var j = 0; j < m2[0].length; j++) { | |
var sum = 0; | |
for (var k = 0; k < m1[0].length; k++) { | |
sum += m1[i][k] * m2[k][j]; | |
} | |
result[i][j] = sum; | |
} | |
} | |
return result; | |
} | |
function matrixInvert(M){ | |
// source - http://blog.acipo.com/matrix-inversion-in-javascript/ | |
// | |
if(M.length !== M[0].length){return;} | |
var i=0, ii=0, j=0, dim=M.length, e=0, t=0; | |
var I = [], C = []; | |
for(i=0; i<dim; i+=1){ | |
I[I.length]=[]; | |
C[C.length]=[]; | |
for(j=0; j<dim; j+=1){ | |
if(i==j){ I[i][j] = 1; } | |
else{ I[i][j] = 0; } | |
C[i][j] = M[i][j]; | |
} | |
} | |
for(i=0; i<dim; i+=1){ | |
e = C[i][i]; | |
if(e==0){ | |
for(ii=i+1; ii<dim; ii+=1){ | |
if(C[ii][i] != 0){ | |
for(j=0; j<dim; j++){ | |
e = C[i][j]; | |
C[i][j] = C[ii][j]; | |
C[ii][j] = e; | |
e = I[i][j]; | |
I[i][j] = I[ii][j]; | |
I[ii][j] = e; | |
} | |
break; | |
} | |
} | |
e = C[i][i]; | |
if(e==0){return} | |
} | |
for(j=0; j<dim; j++){ | |
C[i][j] = C[i][j]/e; | |
I[i][j] = I[i][j]/e; | |
} | |
for(ii=0; ii<dim; ii++){ | |
if(ii==i){continue;} | |
e = C[ii][i]; | |
for(j=0; j<dim; j++){ | |
C[ii][j] -= e*C[i][j]; | |
I[ii][j] -= e*I[i][j]; | |
} | |
} | |
} | |
return I; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment