A JavaScript implementation of descriptive, regression, and inference statistics
—
Functions for calculating percentiles, quartiles, and range statistics.
import {
quantile,
quantileSorted,
quantileRank,
quantileRankSorted,
interquartileRange,
medianAbsoluteDeviation,
min,
max,
extent,
minSorted,
maxSorted,
extentSorted
} from "simple-statistics";function quantile(values: number[], p: number): number;
function quantile(values: number[], p: number[]): number[];Calculates quantiles (percentiles) from a dataset. Automatically sorts the data.
Parameters:
values: number[] - Array of numeric valuesp: number | number[] - Quantile(s) to calculate (0-1 range)Returns: number | number[] - Quantile value(s)
Common Quantiles:
import { quantile } from "simple-statistics";
const testScores = [65, 70, 75, 80, 85, 90, 95];
// Single quantile
const median = quantile(testScores, 0.5); // 80
const q1 = quantile(testScores, 0.25); // 70
const q3 = quantile(testScores, 0.75); // 90
// Multiple quantiles at once
const quartiles = quantile(testScores, [0.25, 0.5, 0.75]);
// [70, 80, 90]
console.log(`Q1: ${q1}, Median: ${median}, Q3: ${q3}`);function quantileSorted(values: number[], p: number): number;
function quantileSorted(values: number[], p: number[]): number[];Optimized quantile calculation for pre-sorted arrays.
Parameters:
values: number[] - Pre-sorted array of numeric valuesp: number | number[] - Quantile(s) to calculate (0-1 range)Returns: number | number[] - Quantile value(s)
import { quantileSorted } from "simple-statistics";
// Data must be pre-sorted
const sortedScores = [65, 70, 75, 80, 85, 90, 95];
const percentiles = quantileSorted(sortedScores, [0.1, 0.5, 0.9]);
// [67, 80, 93] - 10th, 50th, 90th percentilesfunction quantileRank(values: number[], value: number): number;Calculates what percentile a specific value represents in the dataset.
Parameters:
values: number[] - Array of numeric valuesvalue: number - Value to find the rank forReturns: number - Quantile rank (0-1 range)
import { quantileRank } from "simple-statistics";
const classScores = [60, 65, 70, 75, 80, 85, 90, 95];
const studentScore = 80;
const rank = quantileRank(classScores, studentScore); // 0.625
const percentile = Math.round(rank * 100); // 63rd percentile
console.log(`Score of ${studentScore} is at the ${percentile}th percentile`);function quantileRankSorted(values: number[], value: number): number;Optimized quantile rank calculation for pre-sorted arrays.
function interquartileRange(values: number[]): number;Calculates the interquartile range (Q3 - Q1), a robust measure of spread.
Parameters:
values: number[] - Array of numeric valuesReturns: number - IQR value
Use Cases:
import { interquartileRange, quantile } from "simple-statistics";
const salaries = [35000, 40000, 45000, 50000, 55000, 60000, 120000]; // One outlier
const iqr = interquartileRange(salaries); // 15000
const q1 = quantile(salaries, 0.25); // 42500
const q3 = quantile(salaries, 0.75); // 57500
console.log(`IQR: $${iqr.toLocaleString()}`);
console.log(`Middle 50% of salaries: $${q1.toLocaleString()} - $${q3.toLocaleString()}`);
// Outlier detection
const lowerFence = q1 - 1.5 * iqr; // 20000
const upperFence = q3 + 1.5 * iqr; // 80000
const outliers = salaries.filter(salary => salary < lowerFence || salary > upperFence);
console.log(`Outliers: $${outliers.map(s => s.toLocaleString()).join(', ')}`); // $120,000function medianAbsoluteDeviation(values: number[]): number;Calculates the median absolute deviation, another robust measure of variability.
Parameters:
values: number[] - Array of numeric valuesReturns: number - MAD value
Formula: MAD = median(|xi - median(x)|)
import { medianAbsoluteDeviation, median } from "simple-statistics";
const responseTime = [120, 125, 130, 135, 200]; // One slow response
const medianTime = median(responseTime); // 130ms
const mad = medianAbsoluteDeviation(responseTime); // 5ms
console.log(`Median response: ${medianTime}ms`);
console.log(`MAD: ${mad}ms`);
// MAD is less affected by the 200ms outlier than standard deviation would befunction min(values: number[]): number;Finds the minimum value in an array.
function max(values: number[]): number;Finds the maximum value in an array.
function extent(values: number[]): [number, number];Returns both minimum and maximum as a tuple.
Parameters:
values: number[] - Array of numeric valuesReturns: [number, number] - Tuple containing [minimum, maximum]
import { min, max, extent } from "simple-statistics";
const temperatures = [-5, 0, 12, 18, 25, 30];
const minTemp = min(temperatures); // -5
const maxTemp = max(temperatures); // 30
const tempRange = extent(temperatures); // [-5, 30]
console.log(`Temperature range: ${tempRange[0]}°C to ${tempRange[1]}°C`);
console.log(`Temperature span: ${tempRange[1] - tempRange[0]}°C`);For pre-sorted arrays, use these optimized versions:
function minSorted(values: number[]): number;
function maxSorted(values: number[]): number;
function extentSorted(values: number[]): [number, number];Optimized extent calculation for pre-sorted arrays.
Parameters:
values: number[] - Pre-sorted array of numeric valuesReturns: [number, number] - Tuple containing [minimum, maximum]
import { minSorted, maxSorted, extentSorted } from "simple-statistics";
// Pre-sorted data
const sortedPrices = [9.99, 12.50, 15.75, 22.00, 35.99];
const cheapest = minSorted(sortedPrices); // 9.99
const mostExpensive = maxSorted(sortedPrices); // 35.99
const priceRange = extentSorted(sortedPrices); // [9.99, 35.99]import { quantile, interquartileRange, quantileRank } from "simple-statistics";
// Website response times (ms)
const responseTimes = [
45, 52, 48, 67, 55, 50, 47, 53, 49, 51, 46, 54, 48, 52, 50,
49, 47, 53, 51, 48, 50, 65, 58, 44, 56, 49, 52, 47, 53, 150 // One slow outlier
];
// Calculate performance metrics
const percentiles = quantile(responseTimes, [0.5, 0.95, 0.99]);
const [median, p95, p99] = percentiles;
const iqr = interquartileRange(responseTimes);
const slaThreshold = 100; // 100ms SLA
const slaPercentile = quantileRank(responseTimes, slaThreshold);
console.log("Performance Dashboard:");
console.log(`Median response time: ${median}ms`);
console.log(`95th percentile: ${p95}ms`);
console.log(`99th percentile: ${p99}ms`);
console.log(`IQR: ${iqr}ms`);
console.log(`SLA compliance: ${(slaPercentile * 100).toFixed(1)}% under ${slaThreshold}ms`);
// Alerting based on percentiles
if (p95 > 80) {
console.log("🚨 Alert: 95th percentile exceeds 80ms threshold");
}import { quantile, quantileRank, interquartileRange } from "simple-statistics";
// Company salary data
const salaries = [
45000, 48000, 52000, 55000, 58000, 62000, 65000, 68000, 72000, 75000,
78000, 82000, 85000, 90000, 95000, 100000, 110000, 120000, 150000, 200000
];
// Define salary bands using quartiles
const salaryBands = quantile(salaries, [0.25, 0.5, 0.75]);
const [q1, median, q3] = salaryBands;
console.log("Salary Band Analysis:");
console.log(`Entry Level (0-25th): Up to $${q1.toLocaleString()}`);
console.log(`Mid Level (25-50th): $${q1.toLocaleString()} - $${median.toLocaleString()}`);
console.log(`Senior Level (50-75th): $${median.toLocaleString()} - $${q3.toLocaleString()}`);
console.log(`Executive (75th+): $${q3.toLocaleString()}+`);
// Individual salary analysis
const candidateSalary = 87000;
const candidatePercentile = quantileRank(salaries, candidateSalary);
console.log(`\nCandidate Analysis:`);
console.log(`Salary: $${candidateSalary.toLocaleString()}`);
console.log(`Percentile: ${Math.round(candidatePercentile * 100)}th`);
if (candidatePercentile > 0.75) {
console.log("Band: Executive Level");
} else if (candidatePercentile > 0.5) {
console.log("Band: Senior Level");
} else if (candidatePercentile > 0.25) {
console.log("Band: Mid Level");
} else {
console.log("Band: Entry Level");
}import { quantile, interquartileRange, medianAbsoluteDeviation } from "simple-statistics";
// Stock price changes (%)
const priceChanges = [
-2.1, -1.5, -0.8, 0.2, 0.5, 1.2, 1.8, 2.3, -1.2, 0.8,
1.5, -0.5, 2.1, -1.8, 0.3, 1.1, -15.2, 2.5, -0.9, 1.7 // -15.2% is outlier
];
// IQR method for outlier detection
const [q1, q3] = quantile(priceChanges, [0.25, 0.75]);
const iqr = interquartileRange(priceChanges);
const lowerFence = q1 - 1.5 * iqr;
const upperFence = q3 + 1.5 * iqr;
const iqrOutliers = priceChanges.filter(change =>
change < lowerFence || change > upperFence
);
// MAD method for outlier detection (more robust)
const mad = medianAbsoluteDeviation(priceChanges);
const median = quantile(priceChanges, 0.5);
const madThreshold = 3; // 3 MADs from median
const madOutliers = priceChanges.filter(change =>
Math.abs(change - median) > madThreshold * mad
);
console.log("Outlier Detection Results:");
console.log(`IQR method: ${iqrOutliers.length} outliers: ${iqrOutliers.map(x => x.toFixed(1)).join(', ')}%`);
console.log(`MAD method: ${madOutliers.length} outliers: ${madOutliers.map(x => x.toFixed(1)).join(', ')}%`);
console.log(`Normal range (IQR): ${lowerFence.toFixed(1)}% to ${upperFence.toFixed(1)}%`);Install with Tessl CLI
npx tessl i tessl/npm-simple-statistics