ml-regression-simple-linear
Advanced tools
Comparing version 1.0.2 to 2.0.0
@@ -0,1 +1,15 @@ | ||
# [2.0.0](https://github.com/mljs/regression-simple-linear/compare/v1.0.2...v2.0.0) (2019-04-30) | ||
### chore | ||
* update dependencies ([aea8acc](https://github.com/mljs/regression-simple-linear/commit/aea8acc)) | ||
### BREAKING CHANGES | ||
* Support for Node.js 6 was removed. | ||
<a name="1.0.2"></a> | ||
@@ -2,0 +16,0 @@ ## [1.0.2](https://github.com/mljs/regression-simple-linear/compare/v1.0.1...v1.0.2) (2017-04-28) |
120
lib/index.js
@@ -9,79 +9,79 @@ 'use strict'; | ||
class SimpleLinearRegression extends BaseRegression__default { | ||
constructor(x, y) { | ||
super(); | ||
if (x === true) { | ||
this.slope = y.slope; | ||
this.intercept = y.intercept; | ||
this.coefficients = [y.intercept, y.slope]; | ||
} else { | ||
BaseRegression.checkArrayLength(x, y); | ||
regress(this, x, y); | ||
} | ||
constructor(x, y) { | ||
super(); | ||
if (x === true) { | ||
this.slope = y.slope; | ||
this.intercept = y.intercept; | ||
this.coefficients = [y.intercept, y.slope]; | ||
} else { | ||
BaseRegression.checkArrayLength(x, y); | ||
regress(this, x, y); | ||
} | ||
} | ||
toJSON() { | ||
return { | ||
name: 'simpleLinearRegression', | ||
slope: this.slope, | ||
intercept: this.intercept | ||
}; | ||
} | ||
toJSON() { | ||
return { | ||
name: 'simpleLinearRegression', | ||
slope: this.slope, | ||
intercept: this.intercept | ||
}; | ||
} | ||
_predict(x) { | ||
return this.slope * x + this.intercept; | ||
} | ||
_predict(x) { | ||
return this.slope * x + this.intercept; | ||
} | ||
computeX(y) { | ||
return (y - this.intercept) / this.slope; | ||
} | ||
computeX(y) { | ||
return (y - this.intercept) / this.slope; | ||
} | ||
toString(precision) { | ||
let result = 'f(x) = '; | ||
if (this.slope !== 0) { | ||
const xFactor = BaseRegression.maybeToPrecision(this.slope, precision); | ||
result += (xFactor === '1' ? '' : xFactor + ' * ') + 'x'; | ||
if (this.intercept) { | ||
const absIntercept = Math.abs(this.intercept); | ||
const operator = absIntercept === this.intercept ? '+' : '-'; | ||
result += ` ${operator} ${BaseRegression.maybeToPrecision(absIntercept, precision)}`; | ||
} | ||
} else { | ||
result += BaseRegression.maybeToPrecision(this.intercept, precision); | ||
} | ||
return result; | ||
toString(precision) { | ||
let result = 'f(x) = '; | ||
if (this.slope !== 0) { | ||
const xFactor = BaseRegression.maybeToPrecision(this.slope, precision); | ||
result += `${xFactor === '1' ? '' : `${xFactor} * `}x`; | ||
if (this.intercept) { | ||
const absIntercept = Math.abs(this.intercept); | ||
const operator = absIntercept === this.intercept ? '+' : '-'; | ||
result += ` ${operator} ${BaseRegression.maybeToPrecision(absIntercept, precision)}`; | ||
} | ||
} else { | ||
result += BaseRegression.maybeToPrecision(this.intercept, precision); | ||
} | ||
return result; | ||
} | ||
toLaTeX(precision) { | ||
return this.toString(precision); | ||
} | ||
toLaTeX(precision) { | ||
return this.toString(precision); | ||
} | ||
static load(json) { | ||
if (json.name !== 'simpleLinearRegression') { | ||
throw new TypeError('not a SLR model'); | ||
} | ||
return new SimpleLinearRegression(true, json); | ||
static load(json) { | ||
if (json.name !== 'simpleLinearRegression') { | ||
throw new TypeError('not a SLR model'); | ||
} | ||
return new SimpleLinearRegression(true, json); | ||
} | ||
} | ||
function regress(slr, x, y) { | ||
const n = x.length; | ||
let xSum = 0; | ||
let ySum = 0; | ||
const n = x.length; | ||
let xSum = 0; | ||
let ySum = 0; | ||
let xSquared = 0; | ||
let xY = 0; | ||
let xSquared = 0; | ||
let xY = 0; | ||
for (let i = 0; i < n; i++) { | ||
xSum += x[i]; | ||
ySum += y[i]; | ||
xSquared += x[i] * x[i]; | ||
xY += x[i] * y[i]; | ||
} | ||
for (let i = 0; i < n; i++) { | ||
xSum += x[i]; | ||
ySum += y[i]; | ||
xSquared += x[i] * x[i]; | ||
xY += x[i] * y[i]; | ||
} | ||
const numerator = (n * xY - xSum * ySum); | ||
slr.slope = numerator / (n * xSquared - xSum * xSum); | ||
slr.intercept = (1 / n) * ySum - slr.slope * (1 / n) * xSum; | ||
slr.coefficients = [slr.intercept, slr.slope]; | ||
const numerator = n * xY - xSum * ySum; | ||
slr.slope = numerator / (n * xSquared - xSum * xSum); | ||
slr.intercept = (1 / n) * ySum - slr.slope * (1 / n) * xSum; | ||
slr.coefficients = [slr.intercept, slr.slope]; | ||
} | ||
module.exports = SimpleLinearRegression; |
{ | ||
"name": "ml-regression-simple-linear", | ||
"version": "1.0.2", | ||
"version": "2.0.0", | ||
"description": "Simple Linear Regression", | ||
"main": "lib/index.js", | ||
"module": "src/index.js", | ||
"types": "regression-simple-linear.d.ts", | ||
"sideEffects": false, | ||
"files": [ | ||
"regression-simple-linear.d.ts", | ||
"lib", | ||
@@ -12,7 +15,9 @@ "src" | ||
"scripts": { | ||
"compile": "rollup -c", | ||
"eslint": "eslint src", | ||
"eslint-fix": "npm run eslint -- --fix", | ||
"prepublish": "rollup -c", | ||
"test": "run-s testonly eslint", | ||
"testonly": "jest" | ||
"prepublishOnly": "npm run compile", | ||
"test": "npm run test-coverage && npm run eslint", | ||
"test-only": "jest", | ||
"test-coverage": "jest --coverage" | ||
}, | ||
@@ -34,14 +39,13 @@ "repository": { | ||
"devDependencies": { | ||
"babel-jest": "^19.0.0", | ||
"babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", | ||
"eslint": "^3.19.0", | ||
"eslint-config-cheminfo": "^1.7.0", | ||
"eslint-plugin-no-only-tests": "^1.1.0", | ||
"jest": "^19.0.2", | ||
"npm-run-all": "^4.0.2", | ||
"rollup": "^0.41.6" | ||
"@babel/plugin-transform-modules-commonjs": "^7.4.4", | ||
"eslint": "^5.16.0", | ||
"eslint-config-cheminfo": "^1.20.1", | ||
"eslint-plugin-import": "^2.17.2", | ||
"eslint-plugin-jest": "^22.5.1", | ||
"jest": "^24.7.1", | ||
"rollup": "^1.10.1" | ||
}, | ||
"dependencies": { | ||
"ml-regression-base": "^1.0.0" | ||
"ml-regression-base": "^2.0.1" | ||
} | ||
} |
import SLR from '..'; | ||
describe('Simple Linear Regression', () => { | ||
it('SLR1', () => { | ||
const inputs = [80, 60, 10, 20, 30]; | ||
const outputs = [20, 40, 30, 50, 60]; | ||
it('SLR1', () => { | ||
const inputs = [80, 60, 10, 20, 30]; | ||
const outputs = [20, 40, 30, 50, 60]; | ||
const regression = new SLR(inputs, outputs); | ||
const regression = new SLR(inputs, outputs); | ||
expect(regression.slope).toEqual(regression.coefficients[1]); | ||
expect(regression.intercept).toEqual(regression.coefficients[0]); | ||
expect(regression.slope).toStrictEqual(regression.coefficients[1]); | ||
expect(regression.intercept).toStrictEqual(regression.coefficients[0]); | ||
expect(regression.slope).toBeCloseTo(-0.264706, 1e-5); | ||
expect(regression.intercept).toBeCloseTo(50.588235, 1e-5); | ||
expect(regression.slope).toBeCloseTo(-0.264706, 1e-5); | ||
expect(regression.intercept).toBeCloseTo(50.588235, 1e-5); | ||
const y = regression.predict(85); | ||
expect(regression.computeX(y)).toEqual(85); | ||
expect(y).toBeCloseTo(28.088235294117649, 1e-10); | ||
const y = regression.predict(85); | ||
expect(regression.computeX(y)).toStrictEqual(85); | ||
expect(y).toBeCloseTo(28.088235294117649, 1e-10); | ||
expect(regression.toString(3)).toEqual('f(x) = - 0.265 * x + 50.6'); | ||
}); | ||
expect(regression.toString(3)).toStrictEqual('f(x) = - 0.265 * x + 50.6'); | ||
}); | ||
it('SLR2', () => { | ||
// example from https://en.wikipedia.org/wiki/Simple_linear_regression#Numerical_example | ||
const inputs = [1.47, 1.50, 1.52, 1.55, 1.57, 1.60, 1.63, 1.65, 1.68, 1.70, 1.73, 1.75, 1.78, 1.80, 1.83]; | ||
const outputs = [52.21, 53.12, 54.48, 55.84, 57.20, 58.57, 59.93, 61.29, 63.11, 64.47, 66.28, 68.10, 69.92, 72.19, 74.46]; | ||
it('SLR2', () => { | ||
// example from https://en.wikipedia.org/wiki/Simple_linear_regression#Numerical_example | ||
const inputs = [ | ||
1.47, | ||
1.5, | ||
1.52, | ||
1.55, | ||
1.57, | ||
1.6, | ||
1.63, | ||
1.65, | ||
1.68, | ||
1.7, | ||
1.73, | ||
1.75, | ||
1.78, | ||
1.8, | ||
1.83 | ||
]; | ||
const outputs = [ | ||
52.21, | ||
53.12, | ||
54.48, | ||
55.84, | ||
57.2, | ||
58.57, | ||
59.93, | ||
61.29, | ||
63.11, | ||
64.47, | ||
66.28, | ||
68.1, | ||
69.92, | ||
72.19, | ||
74.46 | ||
]; | ||
const regression = new SLR(inputs, outputs); | ||
const regression = new SLR(inputs, outputs); | ||
expect(regression.slope).toBeCloseTo(61.272, 1e-3); | ||
expect(regression.intercept).toBeCloseTo(-39.062, 1e-3); | ||
expect(regression.slope).toBeCloseTo(61.272, 1e-3); | ||
expect(regression.intercept).toBeCloseTo(-39.062, 1e-3); | ||
const score = regression.score(inputs, outputs); | ||
expect(score.r).toBeCloseTo(0.9945, 1e-4); | ||
expect(score.r2).toEqual(score.r * score.r); | ||
expect(score.chi2).toBeLessThan(1); | ||
expect(score.rmsd).toBeLessThan(1); | ||
}); | ||
const score = regression.score(inputs, outputs); | ||
expect(score.r).toBeCloseTo(0.9945, 1e-4); | ||
expect(score.r2).toStrictEqual(score.r * score.r); | ||
expect(score.chi2).toBeLessThan(1); | ||
expect(score.rmsd).toBeLessThan(1); | ||
}); | ||
it('SLR3', () =>{ | ||
const inputs = [0, 1, 2, 3, 4, 5]; | ||
const outputs = [10, 8, 6, 4, 2, 0]; | ||
it('SLR3', () => { | ||
const inputs = [0, 1, 2, 3, 4, 5]; | ||
const outputs = [10, 8, 6, 4, 2, 0]; | ||
const regression = new SLR(inputs, outputs); | ||
const regression = new SLR(inputs, outputs); | ||
expect(regression.slope).toEqual(-2); | ||
expect(regression.intercept).toEqual(10); | ||
expect(regression.predict(6)).toEqual(-2); | ||
expect(regression.predict(-1)).toEqual(12); | ||
expect(regression.predict(2.5)).toEqual(5); | ||
expect(regression.computeX(5)).toEqual(2.5); | ||
expect(regression.computeX(9)).toEqual(0.5); | ||
expect(regression.computeX(-12)).toEqual(11); | ||
expect(regression.slope).toStrictEqual(-2); | ||
expect(regression.intercept).toStrictEqual(10); | ||
expect(regression.predict(6)).toStrictEqual(-2); | ||
expect(regression.predict(-1)).toStrictEqual(12); | ||
expect(regression.predict(2.5)).toStrictEqual(5); | ||
expect(regression.computeX(5)).toStrictEqual(2.5); | ||
expect(regression.computeX(9)).toStrictEqual(0.5); | ||
expect(regression.computeX(-12)).toStrictEqual(11); | ||
const score = regression.score(inputs, outputs); | ||
expect(score.r).toBeGreaterThan(0); | ||
expect(score.r2).toEqual(1); | ||
expect(score.chi2).toEqual(0); | ||
expect(score.rmsd).toEqual(0); | ||
const score = regression.score(inputs, outputs); | ||
expect(score.r).toBeGreaterThan(0); | ||
expect(score.r2).toStrictEqual(1); | ||
expect(score.chi2).toStrictEqual(0); | ||
expect(score.rmsd).toStrictEqual(0); | ||
expect(regression.toString(3)).toEqual('f(x) = - 2.00 * x + 10.0'); | ||
}); | ||
expect(regression.toString(3)).toStrictEqual('f(x) = - 2.00 * x + 10.0'); | ||
}); | ||
it('SLR constant', () => { | ||
const inputs = [0, 1, 2, 3]; | ||
const outputs = [2, 2, 2, 2]; | ||
it('SLR constant', () => { | ||
const inputs = [0, 1, 2, 3]; | ||
const outputs = [2, 2, 2, 2]; | ||
const regression = new SLR(inputs, outputs); | ||
const regression = new SLR(inputs, outputs); | ||
expect(regression.toLaTeX()).toEqual('f(x) = 2'); | ||
expect(regression.toString()).toEqual('f(x) = 2'); | ||
expect(regression.toString(1)).toEqual('f(x) = 2'); | ||
expect(regression.toString(5)).toEqual('f(x) = 2.0000'); | ||
}); | ||
expect(regression.toLaTeX()).toStrictEqual('f(x) = 2'); | ||
expect(regression.toString()).toStrictEqual('f(x) = 2'); | ||
expect(regression.toString(1)).toStrictEqual('f(x) = 2'); | ||
expect(regression.toString(5)).toStrictEqual('f(x) = 2.0000'); | ||
}); | ||
it('negative intercept and slope', () => { | ||
const inputs = [-1, 0, 1]; | ||
const outputs = [-2, -1, 0]; | ||
it('negative intercept and slope', () => { | ||
const inputs = [-1, 0, 1]; | ||
const outputs = [-2, -1, 0]; | ||
const regression = new SLR(inputs, outputs); | ||
const regression = new SLR(inputs, outputs); | ||
expect(regression.toString()).toEqual('f(x) = x - 1'); | ||
}); | ||
expect(regression.toString()).toStrictEqual('f(x) = x - 1'); | ||
}); | ||
it('different size on input and output', () => { | ||
const inputs = [0, 1, 2]; | ||
const outputs = [0, 1]; | ||
expect(() => { | ||
new SLR(inputs, outputs); | ||
}).toThrow(/x and y arrays must have the same length/); | ||
it('different size on input and output', () => { | ||
const inputs = [0, 1, 2]; | ||
const outputs = [0, 1]; | ||
expect(() => new SLR(inputs, outputs)).toThrow( | ||
/x and y arrays must have the same length/ | ||
); | ||
}); | ||
it('Load and export model', () => { | ||
const regression = SLR.load({ | ||
name: 'simpleLinearRegression', | ||
slope: 1, | ||
intercept: 1 | ||
}); | ||
expect(regression.slope).toStrictEqual(1); | ||
expect(regression.intercept).toStrictEqual(1); | ||
expect(regression.coefficients).toStrictEqual([1, 1]); | ||
it('Load and export model', () => { | ||
const regression = SLR.load({ | ||
name: 'simpleLinearRegression', | ||
slope: 1, | ||
intercept: 1 | ||
}); | ||
expect(regression.slope).toEqual(1); | ||
expect(regression.intercept).toEqual(1); | ||
expect(regression.coefficients).toEqual([1, 1]); | ||
const model = regression.toJSON(); | ||
expect(model.name).toEqual('simpleLinearRegression'); | ||
expect(model.slope).toEqual(1); | ||
expect(model.intercept).toEqual(1); | ||
}); | ||
const model = regression.toJSON(); | ||
expect(model.name).toStrictEqual('simpleLinearRegression'); | ||
expect(model.slope).toStrictEqual(1); | ||
expect(model.intercept).toStrictEqual(1); | ||
}); | ||
}); |
125
src/index.js
@@ -1,79 +0,82 @@ | ||
import BaseRegression, {checkArrayLength, maybeToPrecision} from 'ml-regression-base'; | ||
import BaseRegression, { | ||
checkArrayLength, | ||
maybeToPrecision | ||
} from 'ml-regression-base'; | ||
export default class SimpleLinearRegression extends BaseRegression { | ||
constructor(x, y) { | ||
super(); | ||
if (x === true) { | ||
this.slope = y.slope; | ||
this.intercept = y.intercept; | ||
this.coefficients = [y.intercept, y.slope]; | ||
} else { | ||
checkArrayLength(x, y); | ||
regress(this, x, y); | ||
} | ||
constructor(x, y) { | ||
super(); | ||
if (x === true) { | ||
this.slope = y.slope; | ||
this.intercept = y.intercept; | ||
this.coefficients = [y.intercept, y.slope]; | ||
} else { | ||
checkArrayLength(x, y); | ||
regress(this, x, y); | ||
} | ||
} | ||
toJSON() { | ||
return { | ||
name: 'simpleLinearRegression', | ||
slope: this.slope, | ||
intercept: this.intercept | ||
}; | ||
} | ||
toJSON() { | ||
return { | ||
name: 'simpleLinearRegression', | ||
slope: this.slope, | ||
intercept: this.intercept | ||
}; | ||
} | ||
_predict(x) { | ||
return this.slope * x + this.intercept; | ||
} | ||
_predict(x) { | ||
return this.slope * x + this.intercept; | ||
} | ||
computeX(y) { | ||
return (y - this.intercept) / this.slope; | ||
} | ||
computeX(y) { | ||
return (y - this.intercept) / this.slope; | ||
} | ||
toString(precision) { | ||
let result = 'f(x) = '; | ||
if (this.slope !== 0) { | ||
const xFactor = maybeToPrecision(this.slope, precision); | ||
result += (xFactor === '1' ? '' : xFactor + ' * ') + 'x'; | ||
if (this.intercept) { | ||
const absIntercept = Math.abs(this.intercept); | ||
const operator = absIntercept === this.intercept ? '+' : '-'; | ||
result += ` ${operator} ${maybeToPrecision(absIntercept, precision)}`; | ||
} | ||
} else { | ||
result += maybeToPrecision(this.intercept, precision); | ||
} | ||
return result; | ||
toString(precision) { | ||
let result = 'f(x) = '; | ||
if (this.slope !== 0) { | ||
const xFactor = maybeToPrecision(this.slope, precision); | ||
result += `${xFactor === '1' ? '' : `${xFactor} * `}x`; | ||
if (this.intercept) { | ||
const absIntercept = Math.abs(this.intercept); | ||
const operator = absIntercept === this.intercept ? '+' : '-'; | ||
result += ` ${operator} ${maybeToPrecision(absIntercept, precision)}`; | ||
} | ||
} else { | ||
result += maybeToPrecision(this.intercept, precision); | ||
} | ||
return result; | ||
} | ||
toLaTeX(precision) { | ||
return this.toString(precision); | ||
} | ||
toLaTeX(precision) { | ||
return this.toString(precision); | ||
} | ||
static load(json) { | ||
if (json.name !== 'simpleLinearRegression') { | ||
throw new TypeError('not a SLR model'); | ||
} | ||
return new SimpleLinearRegression(true, json); | ||
static load(json) { | ||
if (json.name !== 'simpleLinearRegression') { | ||
throw new TypeError('not a SLR model'); | ||
} | ||
return new SimpleLinearRegression(true, json); | ||
} | ||
} | ||
function regress(slr, x, y) { | ||
const n = x.length; | ||
let xSum = 0; | ||
let ySum = 0; | ||
const n = x.length; | ||
let xSum = 0; | ||
let ySum = 0; | ||
let xSquared = 0; | ||
let xY = 0; | ||
let xSquared = 0; | ||
let xY = 0; | ||
for (let i = 0; i < n; i++) { | ||
xSum += x[i]; | ||
ySum += y[i]; | ||
xSquared += x[i] * x[i]; | ||
xY += x[i] * y[i]; | ||
} | ||
for (let i = 0; i < n; i++) { | ||
xSum += x[i]; | ||
ySum += y[i]; | ||
xSquared += x[i] * x[i]; | ||
xY += x[i] * y[i]; | ||
} | ||
const numerator = (n * xY - xSum * ySum); | ||
slr.slope = numerator / (n * xSquared - xSum * xSum); | ||
slr.intercept = (1 / n) * ySum - slr.slope * (1 / n) * xSum; | ||
slr.coefficients = [slr.intercept, slr.slope]; | ||
const numerator = n * xY - xSum * ySum; | ||
slr.slope = numerator / (n * xSquared - xSum * xSum); | ||
slr.intercept = (1 / n) * ySum - slr.slope * (1 / n) * xSum; | ||
slr.coefficients = [slr.intercept, slr.slope]; | ||
} |
12929
7
8
271
+ Addedis-any-array@2.0.1(transitive)
+ Addedml-regression-base@2.1.6(transitive)
- Removedml-regression-base@1.2.1(transitive)
Updatedml-regression-base@^2.0.1