Medium article: https://medium.com/@waywardverities/linear-regression-in-javascript-an-inefficient-guide-225d95e88e67
// input data
const X = [[2, 14], [5, 12], [7, 16], [11, 18], [15, 22]]
const y = [55000, 60000, 75000, 90000, 120000]
const extraLogging = true
// Helper functions
function transpose(matrix) {
const result = []
for (let i = 0; i < matrix[0].length; i++) {
result[i] = []
for (let j = 0; j < matrix.length; j++) {
result[i][j] = matrix[j][i]
}
}
return result
}
function matrixMultiplication(X, Xt) {
const exampleShape = new Array(X[0].length).fill(0).map(() => new Array(X[0].length).fill(0))
// console.log(exampleShape)
// In our example this will become a 3x3 matrix
let outputMatrix = []
for (XRow=0; XRow<X[0].length; XRow++) {
outputMatrix.push([])
for (XtRow=0; XtRow<X[0].length; XtRow++) {
// Multiply full rows with full columns
let sum = 0
for (k=0; k<X.length; k++) {
sum += X[k][XRow] * X[k][XtRow]
}
outputMatrix[XRow].push(sum)
}
}
return outputMatrix
}
function matrixMultiplication2(matrixA, matrixB) {
let result = []
// Get the number of rows and columns for each matrix
let rowsA = matrixA.length
let colsA = matrixA[0].length
let rowsB = matrixB.length
let colsB = matrixB[0].length
// Check if the matrices can be multiplied
if (colsA !== rowsB) {
throw new Error("Cannot multiply matrices: Invalid dimensions")
}
// Create a new matrix filled with zeros
for (let i = 0; i < rowsA; i++) {
result[i] = []
for (let j = 0; j < colsB; j++) {
result[i][j] = 0
}
}
// Multiply the matrices
for (let i = 0; i < rowsA; i++) {
for (let j = 0; j < colsB; j++) {
for (let k = 0; k < colsA; k++) {
result[i][j] += matrixA[i][k] * matrixB[k][j]
}
}
}
return result
}
function matrix_inverse(matrix) {
// Calculate the determinant of the matrix
const a = matrix[0][0], b = matrix[0][1], c = matrix[0][2],
d = matrix[1][0], e = matrix[1][1], f = matrix[1][2],
g = matrix[2][0], h = matrix[2][1], i = matrix[2][2]
const det = a*e*i + b*f*g + c*d*h - c*e*g - b*d*i - a*f*h
if (extraLogging) console.log(`det ${det}`)
// Check if the matrix is invertible
if (det === 0) {
throw new Error("Matrix is not invertible");
}
// Calculate the inverse of the matrix
const inv_det = 1 / det
if (extraLogging) console.log(`inv_det ${inv_det}`)
const inv_matrix = [
[(e*i - f*h) * inv_det, (c*h - b*i) * inv_det, (b*f - c*e) * inv_det],
[(f*g - d*i) * inv_det, (a*i - c*g) * inv_det, (c*d - a*f) * inv_det],
[(d*h - e*g) * inv_det, (g*b - a*h) * inv_det, (a*e - b*d) * inv_det]
];
return inv_matrix;
}
function manual_linear_regression(X, y) {
if (extraLogging) console.log(`y: ${y}`)
const XWithOnes = X.map(([first, second]) => [1, first, second])
if (extraLogging) console.log(`XWithOnes: ${XWithOnes}`)
// Transpose
const transposedXt = transpose(XWithOnes)
if (extraLogging) console.log(`transposedXt: ${transposedXt}`)
// Multiply the XWithOnes matrix with the transposed matrix
const multipliedXtX = matrixMultiplication2(transposedXt, XWithOnes)
if (extraLogging) console.log(`multipliedXtX: ${multipliedXtX}`)
// Create the inverse matrix
const inverseXtX = matrix_inverse(multipliedXtX)
if (extraLogging) console.log(`Inverse matrix inverseXtX: ${inverseXtX}`)
// A couple more matrix multiplications
const XtX_inv_Xt = matrixMultiplication2(inverseXtX, transposedXt)
if (extraLogging) console.log(`Xt multiplied inverse matrix - XtX_inv_Xt: ${XtX_inv_Xt}`)
const yMap = y.map(nr => [nr])
const beta = matrixMultiplication2(XtX_inv_Xt, yMap)
if (extraLogging) console.log(`beta: ${beta}`)
return beta
}
const beta = manual_linear_regression(X, y)