diff --git a/datafit/CHANGELOG.md b/datafit/CHANGELOG.md index ebc11d70..35e6beae 100644 --- a/datafit/CHANGELOG.md +++ b/datafit/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 0.3.0 + +- Update variable and type names to be more descriptive +- Minor tsdoc updates +- Start working on main readme + ## 0.2.0 - Rename `CurveFit.ts` to `lib.ts` diff --git a/datafit/README.md b/datafit/README.md index 7df2f41b..06a1c9ae 100644 --- a/datafit/README.md +++ b/datafit/README.md @@ -1,4 +1,26 @@ -Simple curve-fitting algorithm: Curve fitting with single-variable or multivariate problems, using a genetic algorithm. +Simple curve-fitting algorithm using a genetic-style algorithm for single-variable or multivariate problems. ![NPM Version](https://img.shields.io/npm/v/datafit) -![NPM Downloads](https://img.shields.io/npm/dt/datafit) \ No newline at end of file +![NPM Downloads](https://img.shields.io/npm/dt/datafit) + +## Installation + +`datafit` can be installed from the official [npm package repository](https://www.npmjs.com/package/datafit). It is highly recommended to install the latest version, which is installed by default with the following command. + +```shell +npm i datafit +``` + +## Getting Started + +`datafit` exports only 1 function, [`fit`](https://npm.nicfv.com/datafit/functions/fit-1.html) which is used for curve fitting. All other exports are purely for information and defining types used within this package. + +## Example + +### Single Variable + +In this example written in JavaScript, we will fit a 2nd degree polynomial to a set of (x,y) points. + +```js +// TODO +``` \ No newline at end of file diff --git a/datafit/package.json b/datafit/package.json index dd37fd18..a8451e42 100644 --- a/datafit/package.json +++ b/datafit/package.json @@ -1,6 +1,6 @@ { "name": "datafit", - "version": "0.2.0", + "version": "0.3.0", "description": "Simple curve-fitting algorithm", "main": "dist/index.js", "types": "types/index.d.ts", diff --git a/datafit/src/lib.ts b/datafit/src/lib.ts index a04451d7..d7549530 100644 --- a/datafit/src/lib.ts +++ b/datafit/src/lib.ts @@ -1,40 +1,40 @@ import { SMath } from 'smath'; -import { Config, Dataset, Fit, Params, X, fx } from './types'; +import { Config, Dataset, Fit, Params, VariableType, fx } from './types'; /** * Minimize the sum of squared errors to fit a set of data * points to a curve with a set of unknown parameters. * @param f The model function for curve fitting. * @param data The entire dataset, as an array of points. - * @param a_initial The initial guess for function parameters, + * @param params_initial The initial guess for function parameters, * which defaults to an array filled with zeroes. * @param config Configuration options for curve fitting. * @returns The set of parameters and error for the best fit. * @example * ```ts * const bestFit: Fit = fit(f, dataset), - * a: Params = bestFit.a, + * params: Params = bestFit.params, * err: number = bestFit.err; * ``` */ -export function fit(f: fx, data: Dataset, a_initial: Params = [], config: Config = { generations: 100, population: 100, survivors: 10, initialDeviation: 10, finalDeviation: 1 }): Fit { +export function fit(f: fx, data: Dataset, params_initial: Params = [], config: Config = { generations: 100, population: 100, survivors: 10, initialDeviation: 10, finalDeviation: 1 }): Fit { const N_params: number = f.length - 1; - if (a_initial.length === 0) { - a_initial.length = N_params; - a_initial.fill(0); + if (params_initial.length === 0) { + params_initial.length = N_params; + params_initial.fill(0); } - if (a_initial.length !== N_params) { + if (params_initial.length !== N_params) { throw new Error('The initial guess should contain ' + N_params + ' parameters.'); } const census: Array = []; for (let generation = 0; generation < config.generations; generation++) { for (let i = 0; i < config.population; i++) { // Mutate a random parent from the prior generation of survivors - const a: Params = mutate( - census[randInt(0, config.survivors)]?.a ?? a_initial, + const params: Params = mutate( + census[randInt(0, config.survivors)]?.params ?? params_initial, SMath.translate(generation, 0, config.generations, config.initialDeviation, config.finalDeviation) ); - census.push({ a: a, err: err(f, a, data) }); + census.push({ params: params, err: err(f, params, data) }); } // Sort by increasing error and only keep the survivors census.sort((x, y) => x.err - y.err); @@ -45,23 +45,23 @@ export function fit(f: fx, data: Dataset, a_initial: Params = [], c /** * Calculate the sum of squared errors for a set of function parameters. * @param f The model function for curve fitting. - * @param a The array of parameters to check. + * @param params The array of parameters to check. * @param data The entire dataset, as an array of points. * @returns The sum of squared errors. */ -function err(f: fx, a: Params, data: Dataset): number { +function err(f: fx, params: Params, data: Dataset): number { let sum: number = 0; - data.forEach(point => sum += (point.y - f(point.x, ...a)) ** 2); + data.forEach(point => sum += (point.y - f(point.x, ...params)) ** 2); return sum; } /** * Randomly mutate the set of function parameters by some maximum deviation. - * @param a The set of function parameters to mutate. + * @param params The set of function parameters to mutate. * @param deviation The maximum amount to deviate in any direction. * @returns A mutated set of parameters. */ -function mutate(a: Params, deviation: number): Params { - return a.map(c => c += SMath.expand(Math.random(), -deviation, deviation)); +function mutate(params: Params, deviation: number): Params { + return params.map(c => c += SMath.expand(Math.random(), -deviation, deviation)); } /** * Generate a random integer between `min, max` diff --git a/datafit/src/types.ts b/datafit/src/types.ts index 1614a423..46fc564d 100644 --- a/datafit/src/types.ts +++ b/datafit/src/types.ts @@ -7,15 +7,15 @@ export type SingleVariable = number; */ export type MultiVariable = Array; /** - * Defines whether this is a single- or multi-variable problem. + * Declares whether this is a single- or multi-variable problem. */ -export type X = SingleVariable | MultiVariable; +export type VariableType = SingleVariable | MultiVariable; /** * Type of function constant parameters to fit the curve with. */ export type Params = Array; /** - * Represents a mathematical function y = f(x) with unknown constants `a` + * Represents a mathematical function y = f(x) with unknown parameters. * @example * Single variable function in Typescript, 2nd degree polynomial: * ```ts @@ -32,18 +32,18 @@ export type Params = Array; * **Note:** `SingleVariable` can be replaced with `number` and * `MultiVariable` can be replaced with `Array` or `number[]`. */ -export type fx = (x: T, ...a: Params) => number; +export type fx = (x: T, ...params: Params) => number; /** * Stores a data point. For multivariable points, the `x` * coordinate contains an array of all the free variables. */ -export interface Datum { +export interface Datum { /** - * Input: X variable(s) + * **Input:** X variable(s) */ readonly x: T; /** - * Output: Y variable + * **Output:** Y variable */ readonly y: number; } @@ -73,7 +73,7 @@ export interface Datum { * ]; * ``` */ -export type Dataset = Array>; +export type Dataset = Array>; /** * Includes information about a best-fit for a curve. */ @@ -81,7 +81,7 @@ export interface Fit { /** * Contains the set of best-fit parameters for the function `f(x)` */ - readonly a: Params; + readonly params: Params; /** * This is the residual sum of squared errors. */