CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-simple-statistics

A JavaScript implementation of descriptive, regression, and inference statistics

Pending
Overview
Eval results
Files

math-utilities.mddocs/

Mathematical Utilities

Root finding, special functions, and numerical utilities for advanced mathematical operations.

Core Imports

import { 
  bisect,
  factorial,
  gamma,
  gammaln,
  sign,
  numericSort,
  quickselect,
  epsilon,
  combinations,
  combinationsReplacement,
  permutationsHeap,
  equalIntervalBreaks
} from "simple-statistics";

Root Finding

bisect { .api }

function bisect(
  func: (x: any) => number, 
  start: number, 
  end: number, 
  maxIterations: number, 
  errorTolerance: number
): number;

Finds roots of a function using the bisection method - a robust numerical algorithm for finding where f(x) = 0.

Parameters:

  • func: (x: any) => number - Function to find the root of
  • start: number - Left boundary of search interval
  • end: number - Right boundary of search interval
  • maxIterations: number - Maximum number of iterations
  • errorTolerance: number - Acceptable error tolerance

Returns: number - Root value where f(x) ≈ 0

Requirements: Function must be continuous and f(start) and f(end) must have opposite signs.

import { bisect } from "simple-statistics";

// Find where x² - 4 = 0 (should be x = 2)
const quadratic = (x: number) => x * x - 4;
const root = bisect(quadratic, 0, 5, 100, 0.0001);
console.log(`Root of x² - 4 = 0: ${root}`); // ≈ 2.0000

// Find break-even point for business model
const profit = (price: number) => (price - 10) * (100 - price) - 200;
const breakEven = bisect(profit, 5, 50, 100, 0.01);
console.log(`Break-even price: $${breakEven.toFixed(2)}`);

Special Functions

factorial { .api }

function factorial(n: number): number;

Calculates the factorial of a non-negative integer (n!).

Parameters:

  • n: number - Non-negative integer

Returns: number - n! = n × (n-1) × ... × 2 × 1

import { factorial } from "simple-statistics";

const fact5 = factorial(5); // 120
const fact0 = factorial(0); // 1 (by definition)

console.log(`5! = ${fact5}`);

// Combinatorics: number of ways to arrange 8 people
const arrangements = factorial(8); // 40,320
console.log(`Ways to arrange 8 people: ${arrangements.toLocaleString()}`);

gamma { .api }

function gamma(n: number): number;

Calculates the gamma function, which extends factorials to real numbers.

Parameters:

  • n: number - Input value

Returns: number - Γ(n) value

Properties:

  • Γ(n) = (n-1)! for positive integers
  • Γ(1/2) = √π
  • Γ(n+1) = n × Γ(n)
import { gamma } from "simple-statistics";

const gamma4 = gamma(4); // 6 (same as 3!)
const gammaHalf = gamma(0.5); // √π ≈ 1.772

console.log(`Γ(4) = ${gamma4}`);
console.log(`Γ(0.5) = ${gammaHalf.toFixed(3)}`);

// Used in probability distributions
const betaFunction = (a: number, b: number) => (gamma(a) * gamma(b)) / gamma(a + b);
console.log(`Beta(2,3) = ${betaFunction(2, 3).toFixed(3)}`);

gammaln { .api }

function gammaln(n: number): number;

Calculates the natural logarithm of the gamma function. More numerically stable for large values.

Parameters:

  • n: number - Input value

Returns: number - ln(Γ(n))

import { gammaln } from "simple-statistics";

// For large values, use log form to avoid overflow
const largeGammaLn = gammaln(100); // ln(Γ(100))
console.log(`ln(Γ(100)) = ${largeGammaLn.toFixed(2)}`);

// Convert back if needed (for smaller results)
const largeGamma = Math.exp(largeGammaLn);
console.log(`Γ(100) ≈ ${largeGamma.toExponential(3)}`);

Utility Functions

sign { .api }

function sign(x: number): number;

Returns the sign of a number.

Parameters:

  • x: number - Input number

Returns: number

  • 1 if x > 0
  • -1 if x < 0
  • 0 if x = 0
import { sign } from "simple-statistics";

console.log(`sign(5) = ${sign(5)}`);    // 1
console.log(`sign(-3) = ${sign(-3)}`);  // -1
console.log(`sign(0) = ${sign(0)}`);    // 0

// Direction of price movement
const priceChange = -2.5;
const direction = sign(priceChange) === 1 ? "up" : sign(priceChange) === -1 ? "down" : "flat";
console.log(`Price moved ${direction}`);

numericSort { .api }

function numericSort(array: number[]): number[];

Sorts an array of numbers in ascending order using proper numeric comparison.

Parameters:

  • array: number[] - Array of numbers to sort

Returns: number[] - New sorted array (original array unchanged)

import { numericSort } from "simple-statistics";

const unsorted = [10, 2, 30, 4, 5];
const sorted = numericSort(unsorted);
console.log(`Sorted: ${sorted}`); // [2, 4, 5, 10, 30]

// Proper numeric sorting vs. lexicographic
const badSort = [10, 2, 30, 4, 5].sort(); // ["10", "2", "30", "4", "5"] - wrong!
const goodSort = numericSort([10, 2, 30, 4, 5]); // [2, 4, 5, 10, 30] - correct!

quickselect { .api }

function quickselect<T>(array: T[], k: number, compare?: (a: T, b: T) => number): T;

Finds the k-th smallest element in an array using the quickselect algorithm (faster than full sorting).

Parameters:

  • array: T[] - Array to search in
  • k: number - Index of element to find (0-based)
  • compare?: (a: T, b: T) => number - Optional comparison function

Returns: T - The k-th smallest element

import { quickselect } from "simple-statistics";

const data = [7, 3, 1, 9, 2, 8, 5];

// Find median (middle element) without full sorting
const median = quickselect(data, Math.floor(data.length / 2));
console.log(`Median: ${median}`); // 5

// Find 3rd smallest element
const thirdSmallest = quickselect(data, 2); // 0-based index
console.log(`3rd smallest: ${thirdSmallest}`); // 3

// Custom comparison for objects
const people = [{age: 25}, {age: 30}, {age: 20}, {age: 35}];
const secondYoungest = quickselect(people, 1, (a, b) => a.age - b.age);
console.log(`Second youngest age: ${secondYoungest.age}`); // 25

epsilon { .api }

const epsilon: number;

Machine epsilon - the smallest representable positive number such that 1 + epsilon ≠ 1 in floating-point arithmetic.

import { epsilon } from "simple-statistics";

console.log(`Machine epsilon: ${epsilon}`);

// Use for floating-point comparisons
function almostEqual(a: number, b: number): boolean {
  return Math.abs(a - b) < epsilon * Math.max(Math.abs(a), Math.abs(b));
}

console.log(`0.1 + 0.2 === 0.3: ${0.1 + 0.2 === 0.3}`); // false
console.log(`0.1 + 0.2 ≈ 0.3: ${almostEqual(0.1 + 0.2, 0.3)}`); // true

Combinatorics

combinations { .api }

function combinations<T>(array: T[], k: number): T[][];

Generates all possible combinations of k elements from an array.

Parameters:

  • array: T[] - Source array
  • k: number - Number of elements in each combination

Returns: T[][] - Array of all possible combinations

import { combinations } from "simple-statistics";

const fruits = ['apple', 'banana', 'cherry', 'date'];
const pairs = combinations(fruits, 2);

console.log("All fruit pairs:");
pairs.forEach(pair => console.log(pair.join(' + ')));
// apple + banana, apple + cherry, apple + date, banana + cherry, etc.

// Team selection
const players = ['Alice', 'Bob', 'Charlie', 'David', 'Eve'];
const teams = combinations(players, 3);
console.log(`Possible 3-person teams: ${teams.length}`);

combinationsReplacement { .api }

function combinationsReplacement<T>(array: T[], k: number): T[][];

Generates combinations with replacement - elements can be repeated.

Parameters:

  • array: T[] - Source array
  • k: number - Number of elements in each combination

Returns: T[][] - Array of combinations with replacement

import { combinationsReplacement } from "simple-statistics";

const dice = [1, 2, 3, 4, 5, 6];
const twoRolls = combinationsReplacement(dice, 2);

console.log("Possible dice combinations (with replacement):");
console.log(`Total combinations: ${twoRolls.length}`); // 21 combinations

permutationsHeap { .api }

function permutationsHeap<T>(array: T[]): T[][];

Generates all permutations of an array using Heap's algorithm.

Parameters:

  • array: T[] - Source array

Returns: T[][] - Array of all permutations

import { permutationsHeap } from "simple-statistics";

const letters = ['A', 'B', 'C'];
const perms = permutationsHeap(letters);

console.log("All permutations of ABC:");
perms.forEach(perm => console.log(perm.join('')));
// ABC, ACB, BAC, BCA, CAB, CBA

console.log(`Total permutations: ${perms.length}`); // 6 = 3!

Data Binning

equalIntervalBreaks { .api }

function equalIntervalBreaks(values: number[], nClasses: number): number[];

Creates equal-width intervals for data binning and histogram creation.

Parameters:

  • values: number[] - Data values
  • nClasses: number - Number of intervals/classes

Returns: number[] - Array of break points defining intervals

import { equalIntervalBreaks, min, max } from "simple-statistics";

// Age distribution binning
const ages = [22, 25, 28, 31, 35, 42, 48, 52, 58, 61, 67, 73];
const ageBreaks = equalIntervalBreaks(ages, 4);

console.log("Age group breaks:", ageBreaks);
// Example: [22, 34.75, 47.5, 60.25, 73]

// Create age groups
const ageGroups = [];
for (let i = 0; i < ageBreaks.length - 1; i++) {
  const group = ages.filter(age => age >= ageBreaks[i] && age < ageBreaks[i + 1]);
  ageGroups.push({
    range: `${ageBreaks[i]}-${ageBreaks[i + 1]}`,
    count: group.length,
    ages: group
  });
}

console.log("Age distribution:");
ageGroups.forEach(group => {
  console.log(`${group.range}: ${group.count} people`);
});

Usage Examples

Numerical Analysis

import { bisect, gamma, factorial } from "simple-statistics";

// Solve engineering problems numerically
class EngineeringCalculator {
  // Find optimal pipe diameter for fluid flow
  static findOptimalDiameter(flowRate: number, maxPressureDrop: number): number {
    // Darcy-Weisbach equation simplified
    const pressureDrop = (diameter: number) => {
      const velocity = flowRate / (Math.PI * diameter * diameter / 4);
      return 0.02 * (1 / diameter) * velocity * velocity * 1000 - maxPressureDrop;
    };
    
    return bisect(pressureDrop, 0.1, 2.0, 100, 0.001);
  }
  
  // Calculate reliability using gamma function
  static weibullReliability(time: number, shape: number, scale: number): number {
    return Math.exp(-Math.pow(time / scale, shape));
  }
  
  // Quality control calculations
  static binomialCoefficient(n: number, k: number): number {
    return factorial(n) / (factorial(k) * factorial(n - k));
  }
}

// Usage examples
const optimalDiameter = EngineeringCalculator.findOptimalDiameter(0.5, 100);
console.log(`Optimal pipe diameter: ${optimalDiameter.toFixed(3)} m`);

const reliability = EngineeringCalculator.weibullReliability(1000, 2, 1500);
console.log(`Reliability after 1000 hours: ${(reliability * 100).toFixed(1)}%`);

Combinatorial Optimization

import { combinations, permutationsHeap, factorial } from "simple-statistics";

// Traveling salesman problem (small scale)
class TSPSolver {
  private distances: number[][];
  
  constructor(cities: string[], distanceMatrix: number[][]) {
    this.distances = distanceMatrix;
  }
  
  // Brute force solution for small instances
  solveExact(cities: string[]): { route: string[], distance: number } {
    const routes = permutationsHeap(cities.slice(1)); // Fix first city
    let bestRoute = cities;
    let bestDistance = Infinity;
    
    for (const route of routes) {
      const fullRoute = [cities[0], ...route];
      const distance = this.calculateRouteDistance(fullRoute);
      
      if (distance < bestDistance) {
        bestDistance = distance;
        bestRoute = fullRoute;
      }
    }
    
    return { route: bestRoute, distance: bestDistance };
  }
  
  private calculateRouteDistance(route: string[]): number {
    let total = 0;
    for (let i = 0; i < route.length - 1; i++) {
      // Simplified - would need city name to index mapping
      total += Math.random() * 100; // Placeholder
    }
    return total;
  }
}

// Portfolio optimization - select best k stocks from n options
function selectOptimalPortfolio(stocks: any[], k: number): any[][] {
  const portfolioCombinations = combinations(stocks, k);
  
  // Evaluate each combination (simplified)
  return portfolioCombinations.map(portfolio => ({
    stocks: portfolio,
    expectedReturn: portfolio.reduce((sum, stock) => sum + stock.expectedReturn, 0) / k,
    risk: Math.sqrt(portfolio.reduce((sum, stock) => sum + stock.variance, 0) / k)
  }));
}

Scientific Computing

import { gamma, gammaln, bisect } from "simple-statistics";

// Statistical distributions using special functions
class Distributions {
  // Beta distribution probability density function
  static betaPDF(x: number, alpha: number, beta: number): number {
    if (x < 0 || x > 1) return 0;
    
    const logBeta = gammaln(alpha) + gammaln(beta) - gammaln(alpha + beta);
    return Math.exp((alpha - 1) * Math.log(x) + (beta - 1) * Math.log(1 - x) - logBeta);
  }
  
  // Find quantiles using bisection
  static betaQuantile(p: number, alpha: number, beta: number): number {
    const cdf = (x: number) => {
      // Simplified incomplete beta function - would need proper implementation
      return this.betaCDF(x, alpha, beta) - p;
    };
    
    return bisect(cdf, 0.001, 0.999, 100, 0.0001);
  }
  
  private static betaCDF(x: number, alpha: number, beta: number): number {
    // Placeholder - would need proper incomplete beta implementation
    return Math.pow(x, alpha) * Math.pow(1 - x, beta);
  }
}

// Physics simulations
class PhysicsUtils {
  // Stirling's approximation for large factorials
  static stirlingApproximation(n: number): number {
    return Math.sqrt(2 * Math.PI * n) * Math.pow(n / Math.E, n);
  }
  
  // Compare with actual gamma function
  static compareStirling(n: number): void {
    const actual = gamma(n + 1); // Γ(n+1) = n!
    const approx = this.stirlingApproximation(n);
    const error = Math.abs(actual - approx) / actual * 100;
    
    console.log(`n=${n}: Actual=${actual.toExponential(3)}, Stirling=${approx.toExponential(3)}, Error=${error.toFixed(2)}%`);
  }
}

// Test accuracy
[5, 10, 20, 50].forEach(n => PhysicsUtils.compareStirling(n));

Install with Tessl CLI

npx tessl i tessl/npm-simple-statistics

docs

array-operations.md

combinatorics.md

data-manipulation.md

descriptive-statistics.md

distributions.md

index.md

machine-learning.md

math-utilities.md

quantiles.md

regression.md

testing.md

tile.json