diff --git a/package.json b/package.json index bed1d76..ab63632 100644 --- a/package.json +++ b/package.json @@ -37,16 +37,16 @@ }, "homepage": "https://github.com/mljs/regression-base#readme", "dependencies": { - "cheminfo-types": "^1.7.2", + "cheminfo-types": "^1.7.3", "is-any-array": "^2.0.1" }, "devDependencies": { - "@vitest/coverage-v8": "^0.34.1", + "@vitest/coverage-v8": "^1.6.0", "eslint": "^8.46.0", - "eslint-config-cheminfo-typescript": "^12.0.4", - "prettier": "^3.0.1", - "rimraf": "^5.0.1", - "typescript": "^5.1.6", - "vitest": "^0.34.1" + "eslint-config-cheminfo-typescript": "^12.4.0", + "prettier": "^3.2.5", + "rimraf": "^5.0.7", + "typescript": "^5.4.5", + "vitest": "^1.6.0" } } diff --git a/src/BaseRegression.ts b/src/BaseRegression.ts new file mode 100644 index 0000000..4403389 --- /dev/null +++ b/src/BaseRegression.ts @@ -0,0 +1,99 @@ +import { type NumberArray } from 'cheminfo-types'; +import { isAnyArray } from 'is-any-array'; + +import { checkArrayLength } from './checkArrayLength'; + +export interface RegressionScore { + r: number; + r2: number; + chi2: number; + rmsd: number; +} +export class BaseRegression { + constructor() { + if (new.target === BaseRegression) { + throw new Error('BaseRegression must be subclassed'); + } + } + + predict(x: number): number; + predict(x: NumberArray): number[]; + predict(x: number | NumberArray) { + if (typeof x === 'number') { + return this._predict(x); + } else if (isAnyArray(x)) { + const y = []; + for (const xVal of x) { + y.push(this._predict(xVal)); + } + return y; + } else { + throw new TypeError('x must be a number or array'); + } + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _predict(x: number): number { + throw new Error('_predict must be implemented'); + } + + train() { + // Do nothing for this package + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + toString(precision?: number) { + return ''; + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + toLaTeX(precision?: number) { + return ''; + } + + /** + * Return the correlation coefficient of determination (r) and chi-square. + * @param x - explanatory variable + * @param y - response variable + * @return - Object with further statistics. + */ + score(x: NumberArray, y: NumberArray): RegressionScore { + checkArrayLength(x, y); + + const n = x.length; + const y2: number[] = new Array(n); + for (let i = 0; i < n; i++) { + y2[i] = this._predict(x[i]); + } + + let xSum = 0; + let ySum = 0; + let chi2 = 0; + let rmsd = 0; + let xSquared = 0; + let ySquared = 0; + let xY = 0; + for (let i = 0; i < n; i++) { + xSum += y2[i]; + ySum += y[i]; + xSquared += y2[i] * y2[i]; + ySquared += y[i] * y[i]; + xY += y2[i] * y[i]; + if (y[i] !== 0) { + chi2 += ((y[i] - y2[i]) * (y[i] - y2[i])) / y[i]; + } + rmsd += (y[i] - y2[i]) * (y[i] - y2[i]); + } + + const r = + (n * xY - xSum * ySum) / + Math.sqrt((n * xSquared - xSum * xSum) * (n * ySquared - ySum * ySum)); + + return { + r, + r2: r * r, + chi2, + rmsd: Math.sqrt(rmsd / n), + }; + } +} diff --git a/src/__tests__/test.test.ts b/src/__tests__/BaseRegression.test.ts similarity index 96% rename from src/__tests__/test.test.ts rename to src/__tests__/BaseRegression.test.ts index 7e41c29..ff33742 100644 --- a/src/__tests__/test.test.ts +++ b/src/__tests__/BaseRegression.test.ts @@ -1,6 +1,6 @@ import { expect, it, describe } from 'vitest'; -import BaseRegression from '..'; +import { BaseRegression } from '..'; class NoPredict extends BaseRegression {} class Basic extends BaseRegression { @@ -17,6 +17,7 @@ class Basic extends BaseRegression { describe('base regression', () => { it('should not be directly constructable', () => { expect(() => { + // eslint-disable-next-line no-new new BaseRegression(); }).toThrow(/BaseRegression must be subclassed/); }); diff --git a/src/__tests__/checkArrayLength.test.ts b/src/__tests__/checkArrayLength.test.ts index 53715ef..31814c6 100644 --- a/src/__tests__/checkArrayLength.test.ts +++ b/src/__tests__/checkArrayLength.test.ts @@ -1,6 +1,6 @@ import { expect, it, describe } from 'vitest'; -import checkArrayLength from '../checkArrayLength'; +import { checkArrayLength } from '..'; describe('checkArrayLength', () => { it('throws on different Length', () => { diff --git a/src/__tests__/maybeToPrecision.test.ts b/src/__tests__/maybeToPrecision.test.ts index af8332c..d27b9ca 100644 --- a/src/__tests__/maybeToPrecision.test.ts +++ b/src/__tests__/maybeToPrecision.test.ts @@ -1,6 +1,6 @@ import { expect, it, describe } from 'vitest'; -import maybeToPrecision from '../maybeToPrecision'; +import { maybeToPrecision } from '..'; describe('maybeToPrecision', () => { it('positive number - no digit', () => { diff --git a/src/checkArrayLength.ts b/src/checkArrayLength.ts index 12a2a16..1e424a9 100644 --- a/src/checkArrayLength.ts +++ b/src/checkArrayLength.ts @@ -6,7 +6,7 @@ import { isAnyArray } from 'is-any-array'; * @param y - second array * @throws if x or y are not the same length, or if they are not arrays */ -export default function checkArrayLength(x: NumberArray, y: NumberArray) { +export function checkArrayLength(x: NumberArray, y: NumberArray) { if (!isAnyArray(x) || !isAnyArray(y)) { throw new TypeError('x and y must be arrays'); } diff --git a/src/index.ts b/src/index.ts index e33697e..0175194 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,103 +1,3 @@ -import { type NumberArray } from 'cheminfo-types'; -import { isAnyArray } from 'is-any-array'; - -import checkArrayLength from './checkArrayLength'; - -export { default as maybeToPrecision } from './maybeToPrecision'; - -export interface RegressionScore { - r: number; - r2: number; - chi2: number; - rmsd: number; -} -export default class BaseRegression { - constructor() { - if (new.target === BaseRegression) { - throw new Error('BaseRegression must be subclassed'); - } - } - - predict(x: number): number; - predict(x: NumberArray): number[]; - predict(x: number | NumberArray) { - if (typeof x === 'number') { - return this._predict(x); - } else if (isAnyArray(x)) { - const y = []; - for (const xVal of x) { - y.push(this._predict(xVal)); - } - return y; - } else { - throw new TypeError('x must be a number or array'); - } - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - _predict(x: number): number { - throw new Error('_predict must be implemented'); - } - - train() { - // Do nothing for this package - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - toString(precision?: number) { - return ''; - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - toLaTeX(precision?: number) { - return ''; - } - - /** - * Return the correlation coefficient of determination (r) and chi-square. - * @param x - explanatory variable - * @param y - response variable - * @return - Object with further statistics. - */ - score(x: NumberArray, y: NumberArray): RegressionScore { - checkArrayLength(x, y); - - const n = x.length; - const y2 = new Array(n); - for (let i = 0; i < n; i++) { - y2[i] = this._predict(x[i]); - } - - let xSum = 0; - let ySum = 0; - let chi2 = 0; - let rmsd = 0; - let xSquared = 0; - let ySquared = 0; - let xY = 0; - for (let i = 0; i < n; i++) { - xSum += y2[i]; - ySum += y[i]; - xSquared += y2[i] * y2[i]; - ySquared += y[i] * y[i]; - xY += y2[i] * y[i]; - if (y[i] !== 0) { - chi2 += ((y[i] - y2[i]) * (y[i] - y2[i])) / y[i]; - } - rmsd += (y[i] - y2[i]) * (y[i] - y2[i]); - } - - const r = - (n * xY - xSum * ySum) / - Math.sqrt((n * xSquared - xSum * xSum) * (n * ySquared - ySum * ySum)); - - return { - r, - r2: r * r, - chi2, - rmsd: Math.sqrt(rmsd / n), - }; - } -} - -export { checkArrayLength, type NumberArray }; +export * from './BaseRegression'; +export * from './checkArrayLength'; +export * from './maybeToPrecision'; diff --git a/src/maybeToPrecision.ts b/src/maybeToPrecision.ts index 99b6ba7..24afffc 100644 --- a/src/maybeToPrecision.ts +++ b/src/maybeToPrecision.ts @@ -4,7 +4,7 @@ * @param figures * @returns - A string representation of `number`. */ -export default function maybeToPrecision(number: number, figures?: number) { +export function maybeToPrecision(number: number, figures?: number) { if (number < 0) { number = 0 - number; if (typeof figures === 'number') { diff --git a/tsconfig.json b/tsconfig.json index 66e32a3..9e6ac8c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,10 +5,9 @@ "moduleResolution": "node", "outDir": "lib", "sourceMap": true, + "skipLibCheck": true, "strict": true, "target": "es2020" }, - "include": [ - "./src/**/*" - ] + "include": ["./src/**/*"] }