Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor!: migration to typescript #8

Merged
merged 5 commits into from Oct 2, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .babelrc.json

This file was deleted.

4 changes: 1 addition & 3 deletions .eslintrc.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
extends: cheminfo
parserOptions:
sourceType: module
extends: cheminfo-typescript
21 changes: 0 additions & 21 deletions .github/workflows/documentationjs.yml

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ name: Node.js CI
on:
push:
branches:
- master
- main
pull_request:

jobs:
nodejs:
# Documentation: https://github.com/zakodium/workflows#nodejs-ci
uses: zakodium/workflows/.github/workflows/nodejs.yml@nodejs-v1
with:
node-version-matrix: '[14, 16]'
lint-check-types: true
3 changes: 1 addition & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Release
on:
push:
branches:
- master
- main

jobs:
release:
Expand All @@ -14,4 +14,3 @@ jobs:
secrets:
github-token: ${{ secrets.BOT_TOKEN }}
npm-token: ${{ secrets.NPM_BOT_TOKEN }}

32 changes: 32 additions & 0 deletions .github/workflows/typedoc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Deploy TypeDoc on GitHub pages

on:
workflow_dispatch:
release:
types: [published]

env:
NODE_VERSION: 18.x
ENTRY_FILE: 'src/index.ts'

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
- name: Install dependencies
run: npm install
- name: Build documentation
uses: zakodium/typedoc-action@v2
with:
entry: ${{ env.ENTRY_FILE }}
- name: Deploy to GitHub pages
uses: JamesIves/github-pages-deploy-action@v4
with:
token: ${{ secrets.BOT_TOKEN }}
branch: gh-pages
folder: docs
clean: true
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,5 @@ jspm_packages
.node_repl_history

lib

lib-esm
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,5 @@ For intercepting at zero using an array, skip the zero in the array (the option
[download-url]: https://npmjs.org/package/ml-regression-polynomial
[codecov-image]: https://img.shields.io/codecov/c/github/mljs/regression-polynomial.svg
[codecov-url]: https://codecov.io/gh/mljs/regression-polynomial
[ci-image]: https://github.com/mljs/regression-polynomial/workflows/Node.js%20CI/badge.svg?branch=master
[ci-image]: https://github.com/mljs/regression-polynomial/workflows/Node.js%20CI/badge.svg?branch=main
[ci-url]: https://github.com/mljs/regression-polynomial/actions?query=workflow%3A%22Node.js+CI%22
35 changes: 21 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,26 @@
"name": "ml-regression-polynomial",
"version": "2.2.0",
"description": "Polynomial Regression",
"main": "lib/index.js",
"module": "src/index.js",
"main": "./lib/index.js",
targos marked this conversation as resolved.
Show resolved Hide resolved
"module": "./lib-esm/index.js",
"files": [
"src",
"lib",
"src"
"lib-esm"
],
"scripts": {
"compile": "rollup -c",
"check-types": "tsc --noEmit",
"clean": "rimraf lib lib-esm",
"eslint": "eslint src",
"eslint-fix": "npm run eslint -- --fix",
"prepack": "npm run compile",
"prepack": "npm run tsc",
"prettier": "prettier --check src",
"prettier-write": "prettier --write src",
"test": "npm run test-only && npm run eslint && npm run prettier",
"test-only": "jest --coverage"
"test": "npm run test-only && npm run eslint && npm run prettier && npm run check-types",
"test-only": "vitest run --coverage",
"tsc": "npm run clean && npm run tsc-cjs && npm run tsc-esm",
"tsc-cjs": "tsc --project tsconfig.cjs.json",
"tsc-esm": "tsc --project tsconfig.esm.json"
},
"repository": {
"type": "git",
Expand All @@ -30,16 +35,18 @@
},
"homepage": "https://github.com/mljs/regression-polynomial#readme",
"devDependencies": {
"@babel/plugin-transform-modules-commonjs": "^7.23.0",
"@vitest/coverage-v8": "^0.34.5",
"eslint": "^8.50.0",
"eslint-config-cheminfo": "^9.0.2",
"jest": "^29.7.0",
"eslint-config-cheminfo-typescript": "^12.0.4",
"prettier": "^3.0.3",
"rollup": "^3.29.3"
"rimraf": "^5.0.5",
"typescript": "^5.2.2",
"vitest": "^0.34.5"
},
"dependencies": {
"@jest/globals": "^29.7.0",
"cheminfo-types": "^1.7.2",
"ml-matrix": "^6.10.5",
"ml-regression-base": "^3.0.0"
}
}
},
"types": "./lib/index.d.ts"
targos marked this conversation as resolved.
Show resolved Hide resolved
}
8 changes: 0 additions & 8 deletions rollup.config.js

This file was deleted.

35 changes: 23 additions & 12 deletions src/__tests__/test.js → src/__tests__/test.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { expect, describe, it } from '@jest/globals';
import { NumberArray } from 'cheminfo-types';
import { expect, it, describe } from 'vitest';

import { PolynomialRegression } from '..';

function assertCoefficientsAndPowers(result, expectedCs, expectedPowers) {
let i = 0;
for (i; i < expectedCs.length; ++i) {
function assertCoefficientsAndPowers(
result: PolynomialRegression,
expectedCs: NumberArray,
expectedPowers: NumberArray,
) {
for (let i = 0; i < expectedCs.length; ++i) {
expect(result.coefficients[i]).toBeCloseTo(expectedCs[i], 10e-6);
expect(result.powers).toStrictEqual(expectedPowers);
}
Expand All @@ -16,9 +20,10 @@ describe('Polynomial regression', () => {
const x = [-3, 0, 2, 4];
const y = [3, 1, 1, 3];
const result = new PolynomialRegression(x, y, 2);
const expected = [0.850519, -0.192495, 0.178462];
const expectedCoefficients = [0.850519, -0.192495, 0.178462];
const expectedPowers = [0, 1, 2];

assertCoefficientsAndPowers(result, expected, [0, 1, 2]);
assertCoefficientsAndPowers(result, expectedCoefficients, expectedPowers);

const score = result.score(x, y);
expect(score.r2).toBeGreaterThan(0.8);
Expand All @@ -35,9 +40,10 @@ describe('Polynomial regression', () => {
const y = new Float64Array([3, 1, 1, 3]);
const result = new PolynomialRegression(x, y, 2);

const expected = [0.850519, -0.192495, 0.178462];
const expectedCoefficients = [0.850519, -0.192495, 0.178462];
const expectedPowers = [0, 1, 2];

assertCoefficientsAndPowers(result, expected, [0, 1, 2]);
assertCoefficientsAndPowers(result, expectedCoefficients, expectedPowers);

const score = result.score(x, y);
expect(score.r2).toBeGreaterThan(0.8);
Expand Down Expand Up @@ -98,11 +104,16 @@ describe('Polynomial regression', () => {
const solution = [0.018041553971009705, 1.0095279075485593];
assertCoefficientsAndPowers(result, solution, [1, 2]);
});
it('Fit a parabola inverting the degree array terms', () => {

it('We should get the same result using numeric degree', () => {
const x = new Float64Array([-4, 4, 2, 3, 1, 8, 5, 7]);
// the .5 is to prove that we can force the origin on 0.
// remove .5 and it tends to y=x^2 as expected.
const y = new Float64Array([16.5, 16.5, 4.5, 9.5, 1.5, 64.5, 25.5, 49.5]);
const result = new PolynomialRegression(x, y, [2, 1]);
const solution = [1.0095279075485593, 0.018041553971009705];
assertCoefficientsAndPowers(result, solution, [2, 1]);
const result = new PolynomialRegression(x, y, 2, {
interceptAtZero: true,
});
const solution = [0.018041553971009705, 1.0095279075485593];
assertCoefficientsAndPowers(result, solution, [1, 2]);
});
});
79 changes: 61 additions & 18 deletions src/index.js → src/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,42 @@
import { type NumberArray } from 'cheminfo-types';
import { Matrix, MatrixTransposeView, solve } from 'ml-matrix';
import BaseRegression, {
checkArrayLength,
maybeToPrecision,
} from 'ml-regression-base';

interface PolynomialRegressionOptions {
interceptAtZero?: boolean;
}
export class PolynomialRegression extends BaseRegression {
constructor(x, y, degree, options = {}) {
degree: number;
powers: number[];
coefficients: number[];
/**
* @param x - independent or explanatory variable
* @param y - dependent or response variable
* @param degree - degree of the polynomial regression, or array of powers to be used. When degree is an array, intercept at zero is forced to false/ignored.
* @example `new PolynomialRegression(x, y, 2)`, in this case, you can pass the option `interceptAtZero`, if you need it.
* @example `new PolynomialRegression(x, y, [1, 3, 5])`
* Each of the degrees corresponds to a column, so if you have them switched, just do:
* @example `new PolynomialRegression(x, y, [3, 1, 5])`
*
* @param options.interceptAtZero - force the polynomial regression so that f(0) = 0
*/
constructor(
x: NumberArray,
y: NumberArray,
degree: number | NumberArray,
options: PolynomialRegressionOptions = {},
) {
super();
// @ts-expect-error internal use only
if (x === true) {
// @ts-expect-error internal use only
this.degree = y.degree;
// @ts-expect-error internal use only
this.powers = y.powers;
// @ts-expect-error internal use only
this.coefficients = y.coefficients;
} else {
checkArrayLength(x, y);
Expand All @@ -20,7 +47,7 @@ export class PolynomialRegression extends BaseRegression {
}
}

_predict(x) {
_predict(x: number) {
let y = 0;
for (let k = 0; k < this.powers.length; k++) {
y += this.coefficients[k] * Math.pow(x, this.powers[k]);
Expand All @@ -37,15 +64,15 @@ export class PolynomialRegression extends BaseRegression {
};
}

toString(precision) {
toString(precision: number) {
return this._toFormula(precision, false);
}

toLaTeX(precision) {
toLaTeX(precision: number) {
return this._toFormula(precision, true);
}

_toFormula(precision, isLaTeX) {
_toFormula(precision: number, isLaTeX: boolean) {
let sup = '^';
let closeSup = '';
let times = ' * ';
Expand Down Expand Up @@ -78,37 +105,53 @@ export class PolynomialRegression extends BaseRegression {
}
fn = str + fn;
}
if (fn.charAt(0) === '+') {
if (fn.startsWith('+')) {
fn = fn.slice(1);
}

return `f(x) = ${fn}`;
}

static load(json) {
static load(json: ReturnType<PolynomialRegression['toJSON']>) {
if (json.name !== 'polynomialRegression') {
throw new TypeError('not a polynomial regression model');
}
// @ts-expect-error internal use only
return new PolynomialRegression(true, json);
}
}

function regress(x, y, degree, options) {
/**
* Perform a polynomial regression on the given data set.
* This is an internal function.
* @param x - independent or explanatory variable
* @param y - dependent or response variable
* @param degree - degree of the polynomial regression
* @param options.interceptAtZero - force the polynomial regression so that $f(0) = 0$
*/
function regress(
x: NumberArray,
y: NumberArray,
degree: number | NumberArray,
options: PolynomialRegressionOptions = {},
) {
const n = x.length;
let { interceptAtZero = false } = options;
let powers;
let powers: number[] = [];
if (Array.isArray(degree)) {
powers = degree;
interceptAtZero = false; //must be false in this case
} else if (interceptAtZero) {
powers = new Array(degree);
for (let k = 0; k < degree; k++) {
powers[k] = k + 1;
}
} else {
powers = new Array(degree + 1);
for (let k = 0; k <= degree; k++) {
powers[k] = k;
} else if (typeof degree === 'number') {
if (interceptAtZero) {
powers = new Array(degree);
for (let k = 0; k < degree; k++) {
powers[k] = k + 1;
}
} else {
powers = new Array(degree + 1);
for (let k = 0; k <= degree; k++) {
powers[k] = k;
}
}
}
const nCoefficients = powers.length; //1 per power, in any case.
Expand Down
11 changes: 11 additions & 0 deletions tsconfig.cjs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"declarationMap": true
},
"exclude": [
"./src/**/__tests__"
]
}
Loading