nano-benchmark 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/stats.js ADDED
@@ -0,0 +1,116 @@
1
+ export const mean = data => {
2
+ let m = 0;
3
+ const size = data.length;
4
+ for (let i = 0; i < size; ++i) {
5
+ m += data[i] / size;
6
+ }
7
+ return m;
8
+ };
9
+
10
+ export const variance = (data, meanValue = mean(data)) => {
11
+ let s = 0;
12
+ const size = data.length;
13
+ for (let i = 0; i < size; ++i) {
14
+ const diff = data[i] - meanValue;
15
+ s += (diff * diff) / size;
16
+ }
17
+ return s;
18
+ };
19
+
20
+ export const stdDev = (data, meanValue = mean(data)) => Math.sqrt(variance(data, meanValue));
21
+
22
+ export const zScore = (x, mean, stdDev) => (x - mean) / stdDev;
23
+
24
+ export const makeZScoreFn = data => {
25
+ const m = mean(data),
26
+ s = stdDev(data, m);
27
+ return x => (x - m) / s;
28
+ };
29
+
30
+ export const skewness = (data, meanValue = mean(data), stdDevValue = stdDev(data, meanValue)) => {
31
+ let s = 0;
32
+ const size = data.length;
33
+ for (let i = 0; i < size; ++i) {
34
+ const z = (data[i] - meanValue) / stdDevValue;
35
+ s += (z * z * z) / size;
36
+ }
37
+ return s;
38
+ };
39
+
40
+ export const adjustedSkewness = (
41
+ data,
42
+ meanValue = mean(data),
43
+ stdDevValue = stdDev(data, meanValue)
44
+ ) => {
45
+ const size = data.length;
46
+ return (Math.sqrt(size * (size - 1)) / (size - 2)) * skewness(data, meanValue, stdDevValue);
47
+ };
48
+
49
+ export const kurtosis = (data, meanValue = mean(data), stdDevValue = stdDev(data, meanValue)) => {
50
+ let s = 0;
51
+ const size = data.length;
52
+ for (let i = 0; i < size; ++i) {
53
+ const z = (data[i] - meanValue) / stdDevValue,
54
+ z2 = z * z;
55
+ s += (z2 * z2) / size;
56
+ }
57
+ return s;
58
+ };
59
+
60
+ export const excessKurtosis = (
61
+ data,
62
+ meanValue = mean(data),
63
+ stdDevValue = stdDev(data, meanValue)
64
+ ) => kurtosis(data, meanValue, stdDevValue) - 3;
65
+
66
+ export const getPercentile = (sortedArray, value) => {
67
+ // getting percentile (index) by value
68
+ let lowerIndex = 0,
69
+ upperIndex = sortedArray.length - 1;
70
+ while (lowerIndex < upperIndex) {
71
+ let middleIndex = (lowerIndex + upperIndex) >> 1;
72
+ if (sortedArray[middleIndex] < value) {
73
+ lowerIndex = middleIndex + 1;
74
+ } else {
75
+ upperIndex = middleIndex;
76
+ }
77
+ }
78
+ return lowerIndex < sortedArray.length && value < sortedArray[lowerIndex]
79
+ ? lowerIndex
80
+ : lowerIndex + 1;
81
+ };
82
+
83
+ export const getWeightedValue = (sortedArray, weight = 0.5) => {
84
+ // getting weighted data from a sorted array
85
+ let pos = weight * (sortedArray.length - 1),
86
+ upperIndex = Math.ceil(pos),
87
+ lowerIndex = upperIndex - 1;
88
+ if (lowerIndex <= 0) {
89
+ // return first element
90
+ return sortedArray[0];
91
+ }
92
+ if (upperIndex >= sortedArray.length) {
93
+ // return last element
94
+ return sortedArray[sortedArray.length - 1];
95
+ }
96
+ // linear approximation
97
+ return (
98
+ sortedArray[lowerIndex] * (upperIndex - pos) + sortedArray[upperIndex] * (pos - lowerIndex)
99
+ );
100
+ };
101
+
102
+ export const bootstrap = (fn, data, n = 1000) => {
103
+ const size = data.length,
104
+ samples = new Array(data.length),
105
+ results = new Array(n);
106
+
107
+ for (let i = 0; i < n; ++i) {
108
+ // resample
109
+ for (let j = 0; j < size; ++j) {
110
+ samples[j] = data[Math.floor(Math.random() * size)];
111
+ }
112
+ results[i] = fn(samples);
113
+ }
114
+
115
+ return results;
116
+ };
@@ -0,0 +1,93 @@
1
+ export class MedianCounter {
2
+ constructor(limit = 10) {
3
+ this.limit = limit;
4
+ this.array = [];
5
+ }
6
+
7
+ add(value) {
8
+ for (let i = 0; i < this.array.length; ++i) {
9
+ const counter = this.array[i];
10
+
11
+ if (counter.length < 3) {
12
+ counter.push(value);
13
+ return;
14
+ }
15
+
16
+ this.array[i] = [value];
17
+
18
+ const a = counter[0],
19
+ b = counter[1],
20
+ c = counter[2];
21
+ if (a < b) {
22
+ value = b < c ? b : a < c ? c : a;
23
+ } else {
24
+ value = a < c ? a : b < c ? c : b;
25
+ }
26
+ }
27
+
28
+ if (this.array.length < this.limit) {
29
+ this.array.push([value]);
30
+ } else {
31
+ this.add(value);
32
+ }
33
+ }
34
+
35
+ get() {
36
+ const copy = this.clone();
37
+
38
+ let value;
39
+
40
+ while (copy.array.length) {
41
+ const counter = copy.array.shift();
42
+
43
+ switch (counter.length) {
44
+ case 1:
45
+ value = counter[0];
46
+ break;
47
+ case 2:
48
+ {
49
+ const a = counter[0],
50
+ b = counter[1];
51
+ value = a < b ? a : b;
52
+ }
53
+ break;
54
+ case 3:
55
+ {
56
+ const a = counter[0],
57
+ b = counter[1],
58
+ c = counter[2];
59
+ if (a < b) {
60
+ value = b < c ? b : a < c ? c : a;
61
+ } else {
62
+ value = a < c ? a : b < c ? c : b;
63
+ }
64
+ }
65
+ break;
66
+ }
67
+
68
+ if (!copy.array.length) break;
69
+
70
+ copy.add(value);
71
+ }
72
+
73
+ return value;
74
+ }
75
+
76
+ clone() {
77
+ const newMedian = new MedianCounter();
78
+ for (const counter of this.array) {
79
+ newMedian.array.push(counter.slice());
80
+ }
81
+ return newMedian;
82
+ }
83
+ }
84
+
85
+ export const streamMedian = iterable => {
86
+ const medianCounter = new MedianCounter();
87
+ for (const value of iterable) {
88
+ medianCounter.add(value);
89
+ }
90
+ return medianCounter.get();
91
+ };
92
+
93
+ export default streamMedian;
@@ -0,0 +1,67 @@
1
+ // based on https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
2
+
3
+ export class StatCounter {
4
+ constructor() {
5
+ this.count = this.mean = this.M2 = this.M3 = this.M4 = 0;
6
+ }
7
+
8
+ add(value) {
9
+ const n = ++this.count,
10
+ delta = value - this.mean,
11
+ term1 = delta / n,
12
+ term2 = delta * term1 * (n - 1),
13
+ term3 = term1 * term1;
14
+
15
+ this.mean += delta / n;
16
+ this.M4 += term3 * term2 * ((n - 3) * n + 3) + 6 * term3 * this.M2 - 4 * term1 * this.M3;
17
+ this.M3 += term1 * term2 * (n - 2) - 3 * term1 * this.M2;
18
+ this.M2 += term2;
19
+ }
20
+
21
+ get variance() {
22
+ return this.M2 / this.count;
23
+ }
24
+
25
+ get sampleVariance() {
26
+ return this.M2 / (this.count - 1);
27
+ }
28
+
29
+ get skewness() {
30
+ return (Math.sqrt(this.count) * this.M3) / Math.pow(this.M2, 1.5);
31
+ }
32
+
33
+ get kurtosis() {
34
+ return (this.count * this.M4) / (this.M2 * this.M2) - 3;
35
+ }
36
+
37
+ clone() {
38
+ const newStatCounter = new StatCounter();
39
+
40
+ newStatCounter.count = this.count;
41
+ newStatCounter.mean = this.mean;
42
+ newStatCounter.M2 = this.M2;
43
+ newStatCounter.M3 = this.M3;
44
+ newStatCounter.M4 = this.M4;
45
+
46
+ return newStatCounter;
47
+ }
48
+ }
49
+
50
+ export const streamStats = iterable => {
51
+ const statCounter = new StatCounter();
52
+
53
+ for (const value of iterable) {
54
+ statCounter.add(value);
55
+ }
56
+
57
+ return {
58
+ count: statCounter.count,
59
+ mean: statCounter.mean,
60
+ variance: statCounter.variance,
61
+ sampleVariance: statCounter.sampleVariance,
62
+ skewness: statCounter.skewness,
63
+ kurtosis: statCounter.kurtosis
64
+ };
65
+ };
66
+
67
+ export default streamStats;
@@ -0,0 +1,9 @@
1
+ const bsearch = (sortedArray, lessFn, l = 0, r = sortedArray.length) => {
2
+ while (l < r) {
3
+ const m = (l + r) >> 1;
4
+ if (lessFn(sortedArray[m])) l = m + 1;
5
+ else r = m;
6
+ }
7
+ return r;
8
+ };
9
+ export default bsearch;
@@ -0,0 +1,36 @@
1
+ const ONE_THIRD = 1 / 3;
2
+
3
+ export const rk23 = (fn, {a = 0, b = 1, tolerance = 1e-6, initialValue = 0} = {}) => {
4
+ const ts = [a],
5
+ us = [initialValue];
6
+ let t = ts[0],
7
+ u = us[0],
8
+ h = Math.min(0.5 * Math.pow(tolerance, ONE_THIRD), b - t),
9
+ s1 = fn(a, initialValue);
10
+
11
+ while (t < b) {
12
+ if (t + h == t) {
13
+ // underflow
14
+ console.warn('Warning: step is too small near:', t);
15
+ break;
16
+ }
17
+
18
+ let s2 = fn(t + h / 2, u + (h / 2) * s1),
19
+ s3 = fn(t + 0.75 * h, u + 0.75 * h * s2),
20
+ uNew = u + (h * (2 * s1 + 3 * s2 + 4 * s3)) / 9, // 2nd order solution
21
+ s4 = fn(t + h, uNew),
22
+ error = Math.abs(h * ((-5 / 72) * s1 + s2 / 12 + s3 / 9 - s4 / 8)), // 2nd/3rd diff error estimate
23
+ maxError = tolerance * (1 + Math.abs(u)); // relative/absolute blend
24
+
25
+ if (error < maxError) {
26
+ ts.push((t += h));
27
+ us.push(u = uNew);
28
+ s1 = s4;
29
+ }
30
+
31
+ const q = Math.min(4, 0.8 * Math.pow(maxError / error, ONE_THIRD));
32
+ h = Math.min(q * h, b - t);
33
+ }
34
+
35
+ return {ts, us, finalValue: us[us.length - 1]};
36
+ };