Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@sgratzl/boxplots

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@sgratzl/boxplots - npm Package Compare versions

Comparing version 1.2.2 to 1.3.0

build/index.cjs

74

build/index.d.ts

@@ -8,2 +8,9 @@ /**

declare type KernelDensityEstimator = (v: number) => number;
declare function kde(stats: {
items: ArrayLike<number>;
iqr: number;
variance: number;
}): KernelDensityEstimator;
interface IBoxPlot {

@@ -53,2 +60,6 @@ /**

/**
* variance
*/
readonly variance: number;
/**
* number of missing values (NaN, null, undefined) in the data

@@ -65,2 +76,3 @@ */

readonly items: ArrayLike<number>;
readonly kde: KernelDensityEstimator;
}

@@ -106,2 +118,7 @@ declare interface QuantileMethod {

interface QuantilesResult {
q1: number;
median: number;
q3: number;
}
/**

@@ -112,7 +129,3 @@ * computes the boxplot stats using the given interpolation function if needed

*/
declare function quantilesInterpolate(arr: ArrayLike<number>, length: number, interpolate: (i: number, j: number, fraction: number) => number): {
q1: number;
median: number;
q3: number;
};
declare function quantilesInterpolate(arr: ArrayLike<number>, length: number, interpolate: (i: number, j: number, fraction: number) => number): QuantilesResult;
/**

@@ -122,7 +135,3 @@ * Uses R's quantile algorithm type=7.

*/
declare function quantilesType7(arr: ArrayLike<number>, length?: number): {
q1: number;
median: number;
q3: number;
};
declare function quantilesType7(arr: ArrayLike<number>, length?: number): QuantilesResult;
/**

@@ -132,39 +141,19 @@ * ‘linear’: i + (j - i) * fraction, where fraction is the fractional part of the index surrounded by i and j.

*/
declare function quantilesLinear(arr: ArrayLike<number>, length?: number): {
q1: number;
median: number;
q3: number;
};
declare function quantilesLinear(arr: ArrayLike<number>, length?: number): QuantilesResult;
/**
* ‘lower’: i.
*/
declare function quantilesLower(arr: ArrayLike<number>, length?: number): {
q1: number;
median: number;
q3: number;
};
declare function quantilesLower(arr: ArrayLike<number>, length?: number): QuantilesResult;
/**
* 'higher': j.
*/
declare function quantilesHigher(arr: ArrayLike<number>, length?: number): {
q1: number;
median: number;
q3: number;
};
declare function quantilesHigher(arr: ArrayLike<number>, length?: number): QuantilesResult;
/**
* ‘nearest’: i or j, whichever is nearest
*/
declare function quantilesNearest(arr: ArrayLike<number>, length?: number): {
q1: number;
median: number;
q3: number;
};
declare function quantilesNearest(arr: ArrayLike<number>, length?: number): QuantilesResult;
/**
* ‘midpoint’: (i + j) / 2
*/
declare function quantilesMidpoint(arr: ArrayLike<number>, length?: number): {
q1: number;
median: number;
q3: number;
};
declare function quantilesMidpoint(arr: ArrayLike<number>, length?: number): QuantilesResult;
/**

@@ -177,7 +166,3 @@ * The hinges equal the quartiles for odd n (where n <- length(x))

*/
declare function quantilesFivenum(arr: ArrayLike<number>, length?: number): {
q1: number;
median: number;
q3: number;
};
declare function quantilesFivenum(arr: ArrayLike<number>, length?: number): QuantilesResult;
/**

@@ -188,10 +173,5 @@ * alias for quantilesFivenum

*/
declare function quantilesHinges(arr: ArrayLike<number>, length?: number): {
q1: number;
median: number;
q3: number;
};
declare function quantilesHinges(arr: ArrayLike<number>, length?: number): QuantilesResult;
export default boxplot;
export { BoxplotStatsOptions, IBoxPlot, QuantileMethod, boxplot, quantilesFivenum, quantilesHigher, quantilesHinges, quantilesInterpolate, quantilesLinear, quantilesLower, quantilesMidpoint, quantilesNearest, quantilesType7 };
export { BoxplotStatsOptions, IBoxPlot, KernelDensityEstimator, QuantileMethod, QuantilesResult, boxplot, boxplot as default, kde, quantilesFivenum, quantilesHigher, quantilesHinges, quantilesInterpolate, quantilesLinear, quantilesLower, quantilesMidpoint, quantilesNearest, quantilesType7 };
//# sourceMappingURL=index.d.ts.map

@@ -8,6 +8,30 @@ /**

'use strict';
const HELPER = Math.sqrt(2 * Math.PI);
function gaussian(u) {
return Math.exp(-0.5 * u * u) / HELPER;
}
function toSampleVariance(variance, len) {
return (variance * len) / (len - 1);
}
function nrd(iqr, variance, len) {
let s = Math.sqrt(toSampleVariance(variance, len));
if (typeof iqr === 'number') {
s = Math.min(s, iqr / 1.34);
}
return 1.06 * s * Math.pow(len, -0.2);
}
function kde(stats) {
const len = stats.items.length;
const bandwidth = nrd(stats.iqr, stats.variance, len);
return (x) => {
let i = 0;
let sum = 0;
for (i = 0; i < len; i++) {
const v = stats.items[i];
sum += gaussian((x - v) / bandwidth);
}
return sum / bandwidth / len;
};
}
Object.defineProperty(exports, '__esModule', { value: true });
function quantilesInterpolate(arr, length, interpolate) {

@@ -61,9 +85,6 @@ const n1 = length - 1;

function createSortedData(data) {
let min = Number.POSITIVE_INFINITY;
let max = Number.NEGATIVE_INFINITY;
let sum = 0;
let valid = 0;
const length = data.length;
const { length } = data;
const vs = data instanceof Float64Array ? new Float64Array(length) : new Float32Array(length);
for (let i = 0; i < length; ++i) {
for (let i = 0; i < length; i += 1) {
const v = data[i];

@@ -74,10 +95,3 @@ if (v == null || Number.isNaN(v)) {

vs[valid] = v;
valid++;
if (v < min) {
min = v;
}
if (v > max) {
max = v;
}
sum += v;
valid += 1;
}

@@ -87,5 +101,4 @@ const missing = length - valid;

return {
sum,
min,
max,
min: Number.NaN,
max: Number.NaN,
missing,

@@ -95,10 +108,11 @@ s: [],

}
const s = valid === length ? vs : vs.subarray(0, valid);
s.sort((a, b) => (a === b ? 0 : a < b ? -1 : 1));
const validData = valid === length ? vs : vs.subarray(0, valid);
validData.sort((a, b) => (a === b ? 0 : a < b ? -1 : 1));
const min = validData[0];
const max = validData[validData.length - 1];
return {
sum,
min: s[0],
max: s[s.length - 1],
min,
max,
missing,
s,
s: validData,
};

@@ -109,3 +123,2 @@ }

return {
sum: Number.NaN,
min: Number.NaN,

@@ -119,10 +132,3 @@ max: Number.NaN,

const max = data[data.length - 1];
const red = (acc, v) => acc + v;
const sum = data instanceof Float32Array
? data.reduce(red, 0)
: data instanceof Float64Array
? data.reduce(red, 0)
: data.reduce(red, 0);
return {
sum,
min,

@@ -134,4 +140,67 @@ max,

}
function computeWhiskers(s, valid, min, max, { eps, quantiles, coef, whiskersMode }) {
const same = (a, b) => Math.abs(a - b) < eps;
const { median, q1, q3 } = quantiles(s, valid);
const iqr = q3 - q1;
const isCoefValid = typeof coef === 'number' && coef > 0;
let whiskerLow = isCoefValid ? Math.max(min, q1 - coef * iqr) : min;
let whiskerHigh = isCoefValid ? Math.min(max, q3 + coef * iqr) : max;
const outlierLow = [];
for (let i = 0; i < valid; i += 1) {
const v = s[i];
if (v >= whiskerLow || same(v, whiskerLow)) {
if (whiskersMode === 'nearest') {
whiskerLow = v;
}
break;
}
if (outlierLow.length === 0 || !same(outlierLow[outlierLow.length - 1], v)) {
outlierLow.push(v);
}
}
const reversedOutlierHigh = [];
for (let i = valid - 1; i >= 0; i -= 1) {
const v = s[i];
if (v <= whiskerHigh || same(v, whiskerHigh)) {
if (whiskersMode === 'nearest') {
whiskerHigh = v;
}
break;
}
if ((reversedOutlierHigh.length === 0 || !same(reversedOutlierHigh[reversedOutlierHigh.length - 1], v)) &&
(outlierLow.length === 0 || !same(outlierLow[outlierLow.length - 1], v))) {
reversedOutlierHigh.push(v);
}
}
const outlier = outlierLow.concat(reversedOutlierHigh.reverse());
return {
median,
q1,
q3,
iqr,
outlier,
whiskerHigh,
whiskerLow,
};
}
function computeStats(s, valid) {
let mean = 0;
for (let i = 0; i < valid; i++) {
const v = s[i];
mean += v;
}
mean /= valid;
let variance = 0;
for (let i = 0; i < valid; i++) {
const v = s[i];
variance += (v - mean) * (v - mean);
}
variance /= valid;
return {
mean,
variance,
};
}
function boxplot(data, options = {}) {
const { quantiles, validAndSorted, coef, whiskersMode, eps } = Object.assign({
const fullOptions = {
coef: 1.5,

@@ -142,4 +211,5 @@ eps: 10e-3,

whiskersMode: 'nearest',
}, options);
const { missing, s, min, max, sum } = validAndSorted ? withSortedData(data) : createSortedData(data);
...options,
};
const { missing, s, min, max } = fullOptions.validAndSorted ? withSortedData(data) : createSortedData(data);
const invalid = {

@@ -158,5 +228,6 @@ min: Number.NaN,

q3: Number.NaN,
variance: 0,
items: [],
kde: () => 0,
};
const same = (a, b) => Math.abs(a - b) < eps;
const valid = data.length - missing;

@@ -166,35 +237,3 @@ if (valid === 0) {

}
const { median, q1, q3 } = quantiles(s, valid);
const iqr = q3 - q1;
const isCoefValid = typeof coef === 'number' && coef > 0;
let whiskerLow = isCoefValid ? Math.max(min, q1 - coef * iqr) : min;
let whiskerHigh = isCoefValid ? Math.min(max, q3 + coef * iqr) : max;
const outlier = [];
for (let i = 0; i < valid; ++i) {
const v = s[i];
if (v >= whiskerLow || same(v, whiskerLow)) {
if (whiskersMode === 'nearest') {
whiskerLow = v;
}
break;
}
if (outlier.length === 0 || !same(outlier[outlier.length - 1], v)) {
outlier.push(v);
}
}
const reversedOutliers = [];
for (let i = valid - 1; i >= 0; --i) {
const v = s[i];
if (v <= whiskerHigh || same(v, whiskerHigh)) {
if (whiskersMode === 'nearest') {
whiskerHigh = v;
}
break;
}
if ((reversedOutliers.length === 0 || !same(reversedOutliers[reversedOutliers.length - 1], v)) &&
(outlier.length === 0 || !same(outlier[outlier.length - 1], v))) {
reversedOutliers.push(v);
}
}
return {
const result = {
min,

@@ -204,25 +243,13 @@ max,

missing,
mean: sum / valid,
whiskerHigh,
whiskerLow,
outlier: outlier.concat(reversedOutliers.reverse()),
median,
q1,
q3,
iqr,
items: s,
...computeStats(s, valid),
...computeWhiskers(s, valid, min, max, fullOptions),
};
return {
...result,
kde: kde(result),
};
}
exports.boxplot = boxplot;
exports.default = boxplot;
exports.quantilesFivenum = quantilesFivenum;
exports.quantilesHigher = quantilesHigher;
exports.quantilesHinges = quantilesHinges;
exports.quantilesInterpolate = quantilesInterpolate;
exports.quantilesLinear = quantilesLinear;
exports.quantilesLower = quantilesLower;
exports.quantilesMidpoint = quantilesMidpoint;
exports.quantilesNearest = quantilesNearest;
exports.quantilesType7 = quantilesType7;
export { boxplot, boxplot as default, kde, quantilesFivenum, quantilesHigher, quantilesHinges, quantilesInterpolate, quantilesLinear, quantilesLower, quantilesMidpoint, quantilesNearest, quantilesType7 };
//# sourceMappingURL=index.js.map

@@ -1,2 +0,2 @@

!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).BoxPlots={})}(this,(function(e){"use strict";function n(e,n,t){const r=n-1,i=n=>{const i=n*r,s=Math.floor(i),o=i-s,u=e[s];return 0===o?u:t(u,e[Math.min(s+1,r)],o)};return{q1:i(.25),median:i(.5),q3:i(.75)}}function t(e,t=e.length){return n(e,t,(e,n,t)=>e+t*(n-e))}function r(e,n=e.length){const t=n,r=Math.floor((t+3)/2)/2,i=n=>.5*(e[Math.floor(n)-1]+e[Math.ceil(n)-1]);return{q1:i(r),median:i((t+1)/2),q3:i(t+1-r)}}function i(e,n={}){const{quantiles:r,validAndSorted:i,coef:s,whiskersMode:o,eps:u}=Object.assign({coef:1.5,eps:.01,quantiles:t,validAndSorted:!1,whiskersMode:"nearest"},n),{missing:a,s:l,min:m,max:N,sum:c}=i?function(e){if(0===e.length)return{sum:Number.NaN,min:Number.NaN,max:Number.NaN,missing:0,s:[]};const n=e[0],t=e[e.length-1],r=(e,n)=>e+n;return{sum:(e instanceof Float32Array||Float64Array,e.reduce(r,0)),min:n,max:t,missing:0,s:e}}(e):function(e){let n=Number.POSITIVE_INFINITY,t=Number.NEGATIVE_INFINITY,r=0,i=0;const s=e.length,o=e instanceof Float64Array?new Float64Array(s):new Float32Array(s);for(let u=0;u<s;++u){const s=e[u];null==s||Number.isNaN(s)||(o[i]=s,i++,s<n&&(n=s),s>t&&(t=s),r+=s)}const u=s-i;if(0===i)return{sum:r,min:n,max:t,missing:u,s:[]};const a=i===s?o:o.subarray(0,i);return a.sort((e,n)=>e===n?0:e<n?-1:1),{sum:r,min:a[0],max:a[a.length-1],missing:u,s:a}}(e),f={min:Number.NaN,max:Number.NaN,mean:Number.NaN,missing:a,iqr:Number.NaN,count:e.length,whiskerHigh:Number.NaN,whiskerLow:Number.NaN,outlier:[],median:Number.NaN,q1:Number.NaN,q3:Number.NaN,items:[]},h=(e,n)=>Math.abs(e-n)<u,g=e.length-a;if(0===g)return f;const{median:d,q1:b,q3:q}=r(l,g),p=q-b,x="number"==typeof s&&s>0;let y=x?Math.max(m,b-s*p):m,M=x?Math.min(N,q+s*p):N;const w=[];for(let e=0;e<g;++e){const n=l[e];if(n>=y||h(n,y)){"nearest"===o&&(y=n);break}0!==w.length&&h(w[w.length-1],n)||w.push(n)}const I=[];for(let e=g-1;e>=0;--e){const n=l[e];if(n<=M||h(n,M)){"nearest"===o&&(M=n);break}0!==I.length&&h(I[I.length-1],n)||0!==w.length&&h(w[w.length-1],n)||I.push(n)}return{min:m,max:N,count:e.length,missing:a,mean:c/g,whiskerHigh:M,whiskerLow:y,outlier:w.concat(I.reverse()),median:d,q1:b,q3:q,iqr:p,items:l}}e.boxplot=i,e.default=i,e.quantilesFivenum=r,e.quantilesHigher=function(e,t=e.length){return n(e,t,(e,n)=>n)},e.quantilesHinges=function(e,n=e.length){return r(e,n)},e.quantilesInterpolate=n,e.quantilesLinear=function(e,t=e.length){return n(e,t,(e,n,t)=>e+(n-e)*t)},e.quantilesLower=function(e,t=e.length){return n(e,t,e=>e)},e.quantilesMidpoint=function(e,t=e.length){return n(e,t,(e,n)=>.5*(e+n))},e.quantilesNearest=function(e,t=e.length){return n(e,t,(e,n,t)=>t<.5?e:n)},e.quantilesType7=t,Object.defineProperty(e,"__esModule",{value:!0})}));
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).BoxPlots={})}(this,(function(e){"use strict";const n=Math.sqrt(2*Math.PI);function t(e){const t=e.items.length,r=function(e,n,t){let r=Math.sqrt(function(e,n){return e*n/(n-1)}(n,t));return"number"==typeof e&&(r=Math.min(r,e/1.34)),1.06*r*Math.pow(t,-.2)}(e.iqr,e.variance,t);return i=>{let o=0,s=0;for(o=0;o<t;o++){const t=e.items[o];s+=(a=(i-t)/r,Math.exp(-.5*a*a)/n)}var a;return s/r/t}}function r(e,n,t){const r=n-1,i=n=>{const i=n*r,o=Math.floor(i),s=i-o,a=e[o];return 0===s?a:t(a,e[Math.min(o+1,r)],s)};return{q1:i(.25),median:i(.5),q3:i(.75)}}function i(e,n=e.length){return r(e,n,((e,n,t)=>e+t*(n-e)))}function o(e,n=e.length){const t=n,r=Math.floor((t+3)/2)/2,i=n=>.5*(e[Math.floor(n)-1]+e[Math.ceil(n)-1]);return{q1:i(r),median:i((t+1)/2),q3:i(t+1-r)}}function s(e,n,t,r,{eps:i,quantiles:o,coef:s,whiskersMode:a}){const u=(e,n)=>Math.abs(e-n)<i,{median:l,q1:m,q3:c}=o(e,n),f=c-m,h="number"==typeof s&&s>0;let N=h?Math.max(t,m-s*f):t,g=h?Math.min(r,c+s*f):r;const d=[];for(let t=0;t<n;t+=1){const n=e[t];if(n>=N||u(n,N)){"nearest"===a&&(N=n);break}0!==d.length&&u(d[d.length-1],n)||d.push(n)}const q=[];for(let t=n-1;t>=0;t-=1){const n=e[t];if(n<=g||u(n,g)){"nearest"===a&&(g=n);break}0!==q.length&&u(q[q.length-1],n)||0!==d.length&&u(d[d.length-1],n)||q.push(n)}return{median:l,q1:m,q3:c,iqr:f,outlier:d.concat(q.reverse()),whiskerHigh:g,whiskerLow:N}}function a(e,n){let t=0;for(let r=0;r<n;r++){t+=e[r]}t/=n;let r=0;for(let i=0;i<n;i++){const n=e[i];r+=(n-t)*(n-t)}return r/=n,{mean:t,variance:r}}function u(e,n={}){const r={coef:1.5,eps:.01,quantiles:i,validAndSorted:!1,whiskersMode:"nearest",...n},{missing:o,s:u,min:l,max:m}=r.validAndSorted?function(e){return 0===e.length?{min:Number.NaN,max:Number.NaN,missing:0,s:[]}:{min:e[0],max:e[e.length-1],missing:0,s:e}}(e):function(e){let n=0;const{length:t}=e,r=e instanceof Float64Array?new Float64Array(t):new Float32Array(t);for(let i=0;i<t;i+=1){const t=e[i];null==t||Number.isNaN(t)||(r[n]=t,n+=1)}const i=t-n;if(0===n)return{min:Number.NaN,max:Number.NaN,missing:i,s:[]};const o=n===t?r:r.subarray(0,n);return o.sort(((e,n)=>e===n?0:e<n?-1:1)),{min:o[0],max:o[o.length-1],missing:i,s:o}}(e),c={min:Number.NaN,max:Number.NaN,mean:Number.NaN,missing:o,iqr:Number.NaN,count:e.length,whiskerHigh:Number.NaN,whiskerLow:Number.NaN,outlier:[],median:Number.NaN,q1:Number.NaN,q3:Number.NaN,variance:0,items:[],kde:()=>0},f=e.length-o;if(0===f)return c;const h={min:l,max:m,count:e.length,missing:o,items:u,...a(u,f),...s(u,f,l,m,r)};return{...h,kde:t(h)}}e.boxplot=u,e.default=u,e.kde=t,e.quantilesFivenum=o,e.quantilesHigher=function(e,n=e.length){return r(e,n,((e,n)=>n))},e.quantilesHinges=function(e,n=e.length){return o(e,n)},e.quantilesInterpolate=r,e.quantilesLinear=function(e,n=e.length){return r(e,n,((e,n,t)=>e+(n-e)*t))},e.quantilesLower=function(e,n=e.length){return r(e,n,(e=>e))},e.quantilesMidpoint=function(e,n=e.length){return r(e,n,((e,n)=>.5*(e+n)))},e.quantilesNearest=function(e,n=e.length){return r(e,n,((e,n,t)=>t<.5?e:n))},e.quantilesType7=i,Object.defineProperty(e,"__esModule",{value:!0})}));
//# sourceMappingURL=index.umd.min.js.map
{
"name": "@sgratzl/boxplots",
"description": "simple boxplot stats helper",
"version": "1.2.2",
"version": "1.3.0",
"publishConfig": {

@@ -21,2 +21,3 @@ "access": "public"

"whisker",
"kde",
"quantiles"

@@ -29,4 +30,4 @@ ],

"global": "BoxPlots",
"module": "build/index.esm.js",
"main": "build/index.js",
"module": "build/index.js",
"main": "build/index.cjs",
"unpkg": "build/index.umd.min.js",

@@ -41,36 +42,38 @@ "jsdelivr": "build/index.umd.min.js",

"devDependencies": {
"@rollup/plugin-commonjs": "^17.0.0",
"@rollup/plugin-node-resolve": "^11.0.0",
"@rollup/plugin-replace": "^2.3.4",
"@rollup/plugin-typescript": "^8.0.0",
"@types/jest": "^26.0.19",
"@typescript-eslint/eslint-plugin": "^4.9.1",
"@typescript-eslint/parser": "^4.9.1",
"@yarnpkg/pnpify": "^2.4.0",
"eslint": "^7.15.0",
"eslint-config-prettier": "^7.0.0",
"@babel/core": "^7.16.0",
"@babel/preset-env": "^7.16.0",
"@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-commonjs": "^21.0.1",
"@rollup/plugin-node-resolve": "^13.0.6",
"@rollup/plugin-replace": "^3.0.0",
"@rollup/plugin-typescript": "^8.3.0",
"@types/jest": "^27.0.2",
"@typescript-eslint/eslint-plugin": "^5.3.0",
"@typescript-eslint/parser": "^5.3.0",
"@yarnpkg/sdks": "^2.5.0",
"eslint": "^8.2.0",
"eslint-config-airbnb-typescript": "^15.0.0",
"eslint-config-prettier": "^8.3.0",
"eslint-config-react-app": "^6.0.0",
"eslint-plugin-flowtype": "^5.2.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-flowtype": "^8.0.3",
"eslint-plugin-import": "^2.25.2",
"eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-plugin-prettier": "^3.2.0",
"eslint-plugin-react": "^7.21.5",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.26.1",
"eslint-plugin-react-hooks": "^4.2.0",
"jest": "^26.6.3",
"prettier": "^2.2.1",
"release-it": "^14.2.2",
"jest": "^27.3.1",
"prettier": "^2.4.1",
"rimraf": "^3.0.2",
"rollup": "^2.34.2",
"rollup-plugin-cleanup": "^3.2.1",
"rollup-plugin-dts": "^2.0.1",
"rollup": "^2.59.0",
"rollup-plugin-dts": "^4.0.1",
"rollup-plugin-terser": "^7.0.2",
"ts-jest": "^26.4.4",
"tslib": "^2.0.3",
"typedoc": "^0.19.2",
"typescript": "^4.1.2"
"ts-jest": "^27.0.7",
"tslib": "^2.3.1",
"typedoc": "^0.22.8",
"typescript": "^4.4.4"
},
"scripts": {
"clean": "rimraf build node_modules \"*.tgz\"",
"compile": "tsc -p tsconfig.json --noEmit",
"start": "npm run watch",
"clean": "rimraf build docs node_modules \"*.tgz\" \"*.tsbuildinfo\"",
"compile": "tsc -b tsconfig.c.json",
"start": "yarn run watch",
"watch": "rollup -c -w",

@@ -81,13 +84,12 @@ "build": "rollup -c",

"test:coverage": "jest --passWithNoTests --coverage",
"lint": "npm run eslint && npm run prettier",
"fix": "npm run eslint:fix && npm run prettier:write",
"prettier:write": "prettier */** --write",
"prettier": "prettier */** --check",
"lint": "yarn run eslint && yarn run prettier",
"fix": "yarn run eslint:fix && yarn run prettier:write",
"prettier:write": "prettier \"*\" \"*/**\" --write",
"prettier": "prettier \"*\" \"*/**\" --check",
"eslint": "eslint src --ext .ts,.tsx",
"eslint:fix": "npm run eslint -- --fix",
"docs": "typedoc",
"prepare": "npm run build",
"release": "release-it --disable-metrics --npm.skipChecks",
"release:pre": "release-it --disable-metrics --npm.skipChecks --preRelease=alpha --npm.tag=next"
}
"eslint:fix": "yarn run eslint --fix",
"docs": "typedoc src/index.ts",
"prepare": "yarn run build"
},
"packageManager": "yarn@3.1.0"
}

@@ -19,12 +19,3 @@ # BoxPlots

const arr = [
-0.402253,
-1.4521869,
0.135228,
-1.8620118,
-0.5687531,
0.4218371,
-1.1165662,
0.5960255,
-0.5008038,
-0.394178,
-0.402253, -1.4521869, 0.135228, -1.8620118, -0.5687531, 0.4218371, -1.1165662, 0.5960255, -0.5008038, -0.394178,
1.3709885,

@@ -31,0 +22,0 @@ ];

@@ -11,12 +11,3 @@ /// <reference types="jest" />

const arr = [
-0.402253,
-1.4521869,
0.135228,
-1.8620118,
-0.5687531,
0.4218371,
-1.1165662,
0.5960255,
-0.5008038,
-0.394178,
-0.402253, -1.4521869, 0.135228, -1.8620118, -0.5687531, 0.4218371, -1.1165662, 0.5960255, -0.5008038, -0.394178,
1.3709885,

@@ -23,0 +14,0 @@ ];

@@ -0,1 +1,2 @@

import kde, { KernelDensityEstimator } from './kde';
import { quantilesType7 } from './quantiles';

@@ -50,2 +51,7 @@

/**
* variance
*/
readonly variance: number;
/**
* number of missing values (NaN, null, undefined) in the data

@@ -62,2 +68,4 @@ */

readonly items: ArrayLike<number>;
readonly kde: KernelDensityEstimator;
}

@@ -105,25 +113,15 @@

function createSortedData(data: readonly number[] | Float32Array | Float64Array) {
let min = Number.POSITIVE_INFINITY;
let max = Number.NEGATIVE_INFINITY;
let sum = 0;
let valid = 0;
const length = data.length;
const { length } = data;
const vs = data instanceof Float64Array ? new Float64Array(length) : new Float32Array(length);
for (let i = 0; i < length; ++i) {
for (let i = 0; i < length; i += 1) {
const v = data[i];
if (v == null || Number.isNaN(v)) {
// eslint-disable-next-line no-continue
continue;
}
vs[valid] = v;
valid++;
if (v < min) {
min = v;
}
if (v > max) {
max = v;
}
sum += v;
valid += 1;
}

@@ -135,5 +133,4 @@

return {
sum,
min,
max,
min: Number.NaN,
max: Number.NaN,
missing,

@@ -145,13 +142,16 @@ s: [],

// add comparator since the polyfill doesn't to a real sorting
const s = valid === length ? vs : vs.subarray(0, valid);
const validData = valid === length ? vs : vs.subarray(0, valid);
// sort in place
s.sort((a, b) => (a === b ? 0 : a < b ? -1 : 1));
// eslint-disable-next-line no-nested-ternary
validData.sort((a, b) => (a === b ? 0 : a < b ? -1 : 1));
// use real number for better precision
const min = validData[0];
const max = validData[validData.length - 1];
return {
sum,
min: s[0],
max: s[s.length - 1],
min,
max,
missing,
s,
s: validData,
};

@@ -163,3 +163,2 @@ }

return {
sum: Number.NaN,
min: Number.NaN,

@@ -173,12 +172,4 @@ max: Number.NaN,

const max = data[data.length - 1];
const red = (acc: number, v: number) => acc + v;
const sum =
data instanceof Float32Array
? data.reduce(red, 0)
: data instanceof Float64Array
? data.reduce(red, 0)
: data.reduce(red, 0);
return {
sum,
min,

@@ -191,52 +182,21 @@ max,

export default function boxplot(
data: readonly number[] | Float32Array | Float64Array,
options: BoxplotStatsOptions = {}
): IBoxPlot {
const { quantiles, validAndSorted, coef, whiskersMode, eps }: Required<BoxplotStatsOptions> = Object.assign(
{
coef: 1.5,
eps: 10e-3,
quantiles: quantilesType7,
validAndSorted: false,
whiskersMode: 'nearest',
},
options
);
const { missing, s, min, max, sum } = validAndSorted ? withSortedData(data) : createSortedData(data);
const invalid = {
min: Number.NaN,
max: Number.NaN,
mean: Number.NaN,
missing,
iqr: Number.NaN,
count: data.length,
whiskerHigh: Number.NaN,
whiskerLow: Number.NaN,
outlier: [],
median: Number.NaN,
q1: Number.NaN,
q3: Number.NaN,
items: [],
};
function computeWhiskers(
s: ArrayLike<number>,
valid: number,
min: number,
max: number,
{ eps, quantiles, coef, whiskersMode }: Required<BoxplotStatsOptions>
) {
const same = (a: number, b: number) => Math.abs(a - b) < eps;
const valid = data.length - missing;
if (valid === 0) {
return invalid;
}
const { median, q1, q3 } = quantiles(s, valid);
const iqr = q3 - q1;
const isCoefValid = typeof coef === 'number' && coef > 0;
let whiskerLow = isCoefValid ? Math.max(min, q1 - coef * iqr) : min;
let whiskerHigh = isCoefValid ? Math.min(max, q3 + coef * iqr) : max;
const outlier: number[] = [];
const outlierLow: number[] = [];
// look for the closest value which is bigger than the computed left
for (let i = 0; i < valid; ++i) {
for (let i = 0; i < valid; i += 1) {
const v = s[i];

@@ -250,9 +210,9 @@ if (v >= whiskerLow || same(v, whiskerLow)) {

// outlier
if (outlier.length === 0 || !same(outlier[outlier.length - 1], v)) {
outlier.push(v);
if (outlierLow.length === 0 || !same(outlierLow[outlierLow.length - 1], v)) {
outlierLow.push(v);
}
}
// look for the closest value which is smaller than the computed right
const reversedOutliers: number[] = [];
for (let i = valid - 1; i >= 0; --i) {
const reversedOutlierHigh: number[] = [];
for (let i = valid - 1; i >= 0; i -= 1) {
const v = s[i];

@@ -267,10 +227,81 @@ if (v <= whiskerHigh || same(v, whiskerHigh)) {

if (
(reversedOutliers.length === 0 || !same(reversedOutliers[reversedOutliers.length - 1], v)) &&
(outlier.length === 0 || !same(outlier[outlier.length - 1], v))
(reversedOutlierHigh.length === 0 || !same(reversedOutlierHigh[reversedOutlierHigh.length - 1], v)) &&
(outlierLow.length === 0 || !same(outlierLow[outlierLow.length - 1], v))
) {
reversedOutliers.push(v);
reversedOutlierHigh.push(v);
}
}
const outlier = outlierLow.concat(reversedOutlierHigh.reverse());
return {
median,
q1,
q3,
iqr,
outlier,
whiskerHigh,
whiskerLow,
};
}
function computeStats(s: ArrayLike<number>, valid: number) {
let mean = 0;
for (let i = 0; i < valid; i++) {
const v = s[i];
mean += v;
}
mean /= valid;
let variance = 0;
for (let i = 0; i < valid; i++) {
const v = s[i];
variance += (v - mean) * (v - mean);
}
variance /= valid;
return {
mean,
variance,
};
}
export default function boxplot(
data: readonly number[] | Float32Array | Float64Array,
options: BoxplotStatsOptions = {}
): IBoxPlot {
const fullOptions: Required<BoxplotStatsOptions> = {
coef: 1.5,
eps: 10e-3,
quantiles: quantilesType7,
validAndSorted: false,
whiskersMode: 'nearest',
...options,
};
const { missing, s, min, max } = fullOptions.validAndSorted ? withSortedData(data) : createSortedData(data);
const invalid: IBoxPlot = {
min: Number.NaN,
max: Number.NaN,
mean: Number.NaN,
missing,
iqr: Number.NaN,
count: data.length,
whiskerHigh: Number.NaN,
whiskerLow: Number.NaN,
outlier: [],
median: Number.NaN,
q1: Number.NaN,
q3: Number.NaN,
variance: 0,
items: [],
kde: () => 0,
};
const valid = data.length - missing;
if (valid === 0) {
return invalid;
}
const result: Omit<IBoxPlot, 'kde'> = {
min,

@@ -280,12 +311,10 @@ max,

missing,
mean: sum / valid,
whiskerHigh,
whiskerLow,
outlier: outlier.concat(reversedOutliers.reverse()),
median,
q1,
q3,
iqr,
items: s,
...computeStats(s, valid),
...computeWhiskers(s, valid, min, max, fullOptions),
};
return {
...result,
kde: kde(result),
};
}
export * from './boxplot';
export { default as boxplot, default } from './boxplot';
export { default as kde, KernelDensityEstimator } from './kde';
export * from './quantiles';

@@ -20,12 +20,3 @@ /// <reference types="jest" />

const arr = [
-0.402253,
-1.4521869,
0.135228,
-1.8620118,
-0.5687531,
0.4218371,
-1.1165662,
0.5960255,
-0.5008038,
-0.394178,
-0.402253, -1.4521869, 0.135228, -1.8620118, -0.5687531, 0.4218371, -1.1165662, 0.5960255, -0.5008038, -0.394178,
1.3709885,

@@ -48,14 +39,4 @@ ].sort(asc);

const arr = [
1.086657167,
0.294672807,
1.462293013,
0.485641706,
1.57748264,
0.827809286,
-0.397192557,
-1.222111542,
1.071236583,
-1.182959319,
-0.003749222,
-0.360759239,
1.086657167, 0.294672807, 1.462293013, 0.485641706, 1.57748264, 0.827809286, -0.397192557, -1.222111542,
1.071236583, -1.182959319, -0.003749222, -0.360759239,
].sort(asc);

@@ -62,0 +43,0 @@ it('type7', () => {

@@ -0,1 +1,7 @@

export interface QuantilesResult {
q1: number;
median: number;
q3: number;
}
/**

@@ -10,3 +16,3 @@ * computes the boxplot stats using the given interpolation function if needed

interpolate: (i: number, j: number, fraction: number) => number
) {
): QuantilesResult {
const n1 = length - 1;

@@ -33,3 +39,3 @@ const compute = (q: number) => {

*/
export function quantilesType7(arr: ArrayLike<number>, length = arr.length) {
export function quantilesType7(arr: ArrayLike<number>, length = arr.length): QuantilesResult {
return quantilesInterpolate(arr, length, (a, b, alpha) => a + alpha * (b - a));

@@ -42,3 +48,3 @@ }

*/
export function quantilesLinear(arr: ArrayLike<number>, length = arr.length) {
export function quantilesLinear(arr: ArrayLike<number>, length = arr.length): QuantilesResult {
return quantilesInterpolate(arr, length, (i, j, fraction) => i + (j - i) * fraction);

@@ -50,3 +56,3 @@ }

*/
export function quantilesLower(arr: ArrayLike<number>, length = arr.length) {
export function quantilesLower(arr: ArrayLike<number>, length = arr.length): QuantilesResult {
return quantilesInterpolate(arr, length, (i) => i);

@@ -58,3 +64,3 @@ }

*/
export function quantilesHigher(arr: ArrayLike<number>, length = arr.length) {
export function quantilesHigher(arr: ArrayLike<number>, length = arr.length): QuantilesResult {
return quantilesInterpolate(arr, length, (_, j) => j);

@@ -66,3 +72,3 @@ }

*/
export function quantilesNearest(arr: ArrayLike<number>, length = arr.length) {
export function quantilesNearest(arr: ArrayLike<number>, length = arr.length): QuantilesResult {
return quantilesInterpolate(arr, length, (i, j, fraction) => (fraction < 0.5 ? i : j));

@@ -74,3 +80,3 @@ }

*/
export function quantilesMidpoint(arr: ArrayLike<number>, length = arr.length) {
export function quantilesMidpoint(arr: ArrayLike<number>, length = arr.length): QuantilesResult {
return quantilesInterpolate(arr, length, (i, j) => (i + j) * 0.5);

@@ -86,3 +92,3 @@ }

*/
export function quantilesFivenum(arr: ArrayLike<number>, length = arr.length) {
export function quantilesFivenum(arr: ArrayLike<number>, length = arr.length): QuantilesResult {
// based on R fivenum

@@ -107,4 +113,4 @@ const n = length;

*/
export function quantilesHinges(arr: ArrayLike<number>, length = arr.length) {
export function quantilesHinges(arr: ArrayLike<number>, length = arr.length): QuantilesResult {
return quantilesFivenum(arr, length);
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc