/** * Least Squares Linear Regression * By Raphael Rouvinov-Kats * * simplest least-squares fit to the data * more info from super general to super specific: * https://en.wikipedia.org/wiki/Machine_learning * https://en.wikipedia.org/wiki/Linear_regression * https://en.wikipedia.org/wiki/Ordinary_least_squares * https://en.wikipedia.org/wiki/Proofs_involving_ordinary_least_squares * **/ /** * The math worked out: * * a vector of length L is an L x 1 matrix, [[1],[2],...,[L]] * an apostrophe means transpose * * Data: * X is the N x D matrix of independent variables. * [[x_1],[x_2],...,[x_N]] * Where x_i is a length D input vector / variable and column in the matrix * Y is the N x 1 matrix of dependent variables that result * * Goal: to create a line * y_hat = w'x + b * that approximates the data * with w a vector and b a scalar such that the least squares distance * of Y_hat from Y is minimized * Y ~= w' X + B * * we can change the equation to * y_hat = w' X * by adding a first row to X of all ones * So each x_i is now [[1] [x_i1] [x_i2] ... [x_iD]] * * J is the loss function, the squared distance of Y from Y_hat * J = sum_i^N (y_i - y_hat)^2 * J = (Y - X'w)' (Y - X'w) ‹ convert to matrix notation * J = (Y' - w'X) (Y - X'w) ‹ apply transpose * J = Y'Y - Y'X'w - w'XY + w'XX'w ‹ distribute * * to minimize the loss, take the gradient and set it equal to zero * first step done by applying * http://www.atmos.washington.edu/~dennis/MatrixCalculus.pdf * dJ/dw = -Y'X' - Y'X' + w'(XX' + X'X) = 0 * w'(XX' + X'X) = 2Y'X' * 2w'XX' = 2Y'X' * X'Xw = XY * w = (X'X)^-1 (XY) * * Done! * The line is * f(x) = [(X'X)^-1 (XY)]' x **/ /** * Returns a gaussian random function with the given mean and stdev * From http://stackoverflow.com/questions/25582882/javascript-math-random-normal-distribution-gaussian-bell-curve **/ var gaussian = function(mean, stdev) { var y2; var use_last = false; return function() { var y1; if(use_last) { y1 = y2; use_last = false; } else { var x1, x2, w; do { x1 = 2.0 * Math.random() - 1.0; x2 = 2.0 * Math.random() - 1.0; w = x1 * x1 + x2 * x2; } while( w >= 1.0); w = Math.sqrt((-2.0 * Math.log(w))/w); y1 = x1 * w; y2 = x2 * w; use_last = true; } var retval = mean + stdev * y1; if(retval > 0) { return retval; } return -retval; }; }; /** * Returns the inverse of matrix M * from http://blog.acipo.com/matrix-inversion-in-javascript/ * */ var matrix_invert = function(M) { // I use Guassian Elimination to calculate the inverse: // (1) 'augment' the matrix (left) by the identity (on the right) // (2) Turn the matrix on the left into the identity by elemetry row ops // (3) The matrix on the right is the inverse (was the identity matrix) // There are 3 elemtary row ops: (I combine b and c in my code) // (a) Swap 2 rows // (b) Multiply a row by a scalar // (c) Add 2 rows //if the matrix isn't square: exit (error) if(M.length !== M[0].length){return;} //create the identity matrix (I), and a copy (C) of the original var i=0, ii=0, j=0, dim=M.length, e=0, t=0; var I = [], C = []; for(i=0; i