Skip to content

Commit

Permalink
Beta distribution bug fix, distributions added (#11)
Browse files Browse the repository at this point in the history
* 2.8.0 - Compertz distribution added, tests added

* 2.8.1 - Beta distribution bug fixed

Beta distribution for alpha=1 or beta=1 returned NaN
every time. Fixed it by changing prng.random(n) logic.

* 2.8.2 - Delaporte distribution added
  • Loading branch information
AlexeySKiselev authored Jun 28, 2020
1 parent 13f24ce commit 5789ab3
Show file tree
Hide file tree
Showing 25 changed files with 808 additions and 42 deletions.
4 changes: 3 additions & 1 deletion Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# Unirand
A JavaScript module for generating seeded random distributions and its statistical analysis.

Implemented in pure JavaScript with no dependencies, designed to work in Node.js and fully asynchronous, tested *with 740+ tests*.
Implemented in pure JavaScript with no dependencies, designed to work in Node.js and fully asynchronous, tested *with 780+ tests*.

#### [Supported distributions](./core/methods/)

Expand All @@ -20,6 +20,8 @@ Implemented in pure JavaScript with no dependencies, designed to work in Node.js
| Cauchy (Lorenz) distribution | `x` - any value, `gamma` > 0 | `unirand.cauchy(x, gamma).random()` |
| Chi distribution | `k` - integer, `k` > 0 | `unirand.chi(k).random()` |
| Chi Square distribution | `k` - integer, `k` > 0 | `unirand.chisquare(k).random()` |
| Compertz distribution | `nu` > 0 - float value, `b` > 0 - float value | `unirand.compertz(nu, b).random()` |
| Delaporte distribution | `alpha` > 0 - float value, `beta` > 0 - float value, `lambda` > 0 - float value | `unirand.delaporte(alpha, beta, lambda).random()` |
| Erlang distribution | `k` - integer, `k` > 0, `mu` - float value, `mu` > 0 | `unirand.erlang(k, mu).random()` |
| Exponential distribution | `lambda` - float value, `lambda` > 0 | `unirand.exponential(lambda).random()` |
| Extreme (Gumbel-type) Value distribution | `mu` - any value, `sigma` - float number, `sigma` > 0 | `unirand.extremevalue(mu, sigma).random()` |
Expand Down
4 changes: 2 additions & 2 deletions core/array_manipulation/smooth/movingAverage.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@ class MovingAverage implements ISmooth {
* @param {RandomArray} data
* @param {{[string]: any}} options
*/
smooth(data: RandomArray, options: {[string]: any} = {}): RandomArray {
const smoothData: MASmoothData = this._getSmoothData(options);
smooth(data: RandomArray, options: ?{[string]: any} = {}): RandomArray {
const smoothData: MASmoothData = this._getSmoothData(options || {});
if (smoothData.minDataLength > data.length) {
throw new Error(`Smooth (Moving Average): data size must NOT be less then ${smoothData.minDataLength}, given ${data.length}`);
}
Expand Down
21 changes: 8 additions & 13 deletions core/array_manipulation/smooth/smoothProxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,17 @@ const DEFAULT_SMOOTH_ALGORITHM = 'moving_average';

class SmoothProxy implements ISmoothProxy {

_allowedSmoothAlgorithms: {[string]: true};
_allowedSmoothAlgorithms: {[string]: any};
_smoothAlgorithms: {[string]: ISmooth};
_currentSmoothAlgorithm: ISmooth;
_currentSmoothAlgorithmName: string;

constructor() {
this._allowedSmoothAlgorithms = {
'moving_average': true
'moving_average': MovingAverage
};

this._smoothAlgorithms = {
'moving_average': new MovingAverage()
};

this._currentSmoothAlgorithmName = DEFAULT_SMOOTH_ALGORITHM;
this._currentSmoothAlgorithm = this._smoothAlgorithms[DEFAULT_SMOOTH_ALGORITHM];
this._smoothAlgorithms = {};
this.setSmoothAlgorithm(DEFAULT_SMOOTH_ALGORITHM);
}

/**
Expand Down Expand Up @@ -70,7 +65,7 @@ class SmoothProxy implements ISmoothProxy {
* Sets algorithm
* @param {string} name
*/
setSmoothAlgorithm(name: string) {
setSmoothAlgorithm(name: string): void {
if (!this._allowedSmoothAlgorithms[name]) {
throw new Error(`Smooth: algorithm ${name} is not allowed`);
}
Expand All @@ -87,23 +82,23 @@ class SmoothProxy implements ISmoothProxy {
* Returns current algorithm name
* @returns {string}
*/
getAlgorithmName() {
getAlgorithmName(): string {
return this._currentSmoothAlgorithm.getName();
}

/**
* Returns a list of allowed algorithms
* @returns {Array<string>}
*/
listSmoothAlgorithms() {
listSmoothAlgorithms(): Array<string> {
return Object.keys(this._allowedSmoothAlgorithms);
}

/**
* Returns default smooth algorithm name
* @returns {string}
*/
getDefaultAlgorithmName() {
getDefaultAlgorithmName(): string {
return DEFAULT_SMOOTH_ALGORITHM;
}

Expand Down
2 changes: 1 addition & 1 deletion core/methods/beta.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class Beta implements IDistribution {
gammaA: number,
gammaB: number,
gammaADist: RandomArray,
gammaBDist : RandomArray;
gammaBDist: RandomArray;

this.gammaA.refresh(this.alpha, 1);
this.gammaB.refresh(this.beta, 1);
Expand Down
2 changes: 1 addition & 1 deletion core/methods/binomial.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class Binomial implements IDistribution {
let binomialArray: RandomArray = [],
random: RandomArray = (prng.random(n * this.trials): any),
res: number;
for(let i:number = 0; i < n; i += 1){
for(let i: number = 0; i < n; i += 1){
res = 0;
for(let k: number = 0; k < this.trials; k += 1){
if(random[i * this.trials + k] < this.successProb) {
Expand Down
2 changes: 1 addition & 1 deletion core/methods/chi.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class Chi implements IDistribution {
this.chiSquare.refresh(this.degrees);
let chiArray: RandomArray = [],
random: RandomArray = this.chiSquare.distribution(n);
for(let i:number = 0; i < n; i += 1){
for(let i: number = 0; i < n; i += 1){
chiArray[i] = Math.sqrt(random[i]);
}
return chiArray;
Expand Down
2 changes: 1 addition & 1 deletion core/methods/chisquare.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class ChiSquare implements IDistribution {
let chiSquareArray: RandomArray = [],
res: number,
random: RandomArray = this.normal.distribution(n * this.degrees);
for(let i:number = 0; i < n; i += 1){
for(let i: number = 0; i < n; i += 1){
res = 0;
for(let j: number = 0; j < this.degrees; j += 1){
res += Math.pow(random[i * this.degrees + j], 2);
Expand Down
112 changes: 112 additions & 0 deletions core/methods/compertz.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// @flow
/**
* Compertz Distribution
* This is continuous distribution
* https://en.wikipedia.org/wiki/Gompertz_distribution
* @param nu: number - shape > 0
* @param b: number - scale > 0
* @returns Compertz Distributed value
* Created by Alexey S. Kiselev
*/

import prng from '../prng/prngProxy';
import type { MethodError, RandomArray } from '../types';
import type { IDistribution } from '../interfaces';

class Compertz implements IDistribution {
nu: number;
b: number;

constructor(nu: number, b: number): void {
this.nu = Number(nu);
this.b = Number(b);
}

/**
* Generates a random number
* @returns a Compertz distributed number
*/
_random(u: number): number {
return Math.log(1 - Math.log(1 - u) / this.nu) / this.b;
}

random(): number {
return this._random(prng.random());
}

next(): number {
return this._random(prng.next());
}

/**
* Generates Compertz distributed numbers
* @param n: number - Number of elements in resulting array, n > 0
* @returns Array<number> - Compertz distributed numbers
*/
distribution(n: number): RandomArray {
let compertzArray: RandomArray = [],
random: RandomArray = (prng.random(n): any);
for(let i: number = 0; i < n; i += 1){
compertzArray[i] = this._random(random[i]);
}
return compertzArray;
}

/**
* Error handling
* @returns {boolean}
*/
isError(): MethodError {
if(!this.nu || this.nu <= 0) {
return {error: 'Compertz distribution: you should point parameter "nu" (shape) with positive numerical value'};
}
if(!this.b || this.b <= 0) {
return {error: 'Compertz distribution: you should point parameter "b" (scale) with positive numerical value'};
}
return { error: false };
}

/**
* Refresh method
* @param newNu: number - new parameter "bu"
* @param newB: number - new parameter "b"
* This method does not return values
*/
refresh(newNu: number, newB: number): void {
this.nu = Number(newNu);
this.b = Number(newB);
}

/**
* Class .toString method
* @returns {string}
*/
toString(): string {
let info = [
'Compertz Distribution',
`Usage: unirand.compertz(${this.nu}, ${this.b}).random()`
];
return info.join('\n');
}

/**
* Median value
* Information only
* For calculating real median value use analyzer
*/
get median(): number {
return this._random(0.5);
}

/**
* All parameters of distribution in one object
* Information only
*/
get parameters(): {} {
return {
median: this.median
};
}
}

module.exports = Compertz;
Loading

3 comments on commit 5789ab3

@dirkesquire
Copy link

@dirkesquire dirkesquire commented on 5789ab3 Jul 19, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello,
Is there any reason you added the line to packages.json:

  "engines": {
     "node": "^8.12"
   },

The error I'm not getting when I add unirand to my project is:
error unirand@2.8.2: The engine "node" is incompatible with this module. Expected version "^8.12". Got "14.5.0"

@AlexeySKiselev
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for Your feedback. Issue has been fixed here.

@dirkesquire
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks so much! It is working now. Also while I'm here, thank you for this great project!

Please sign in to comment.