perfshield 0.0.9 → 0.0.10
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/lib/stats.js +98 -16
- package/package.json +1 -1
package/lib/stats.js
CHANGED
|
@@ -7,6 +7,102 @@ export const relativeMarginOfError = stats => {
|
|
|
7
7
|
return Math.abs(margin / stats.mean);
|
|
8
8
|
};
|
|
9
9
|
const sumOf = values => values.reduce((total, value) => total + value, 0);
|
|
10
|
+
const sortNumbers = values => [...values].sort((a, b) => a - b);
|
|
11
|
+
const medianOfSorted = values => {
|
|
12
|
+
if (values.length === 0) {
|
|
13
|
+
throw new Error("Cannot compute median of an empty sample set.");
|
|
14
|
+
}
|
|
15
|
+
const mid = Math.floor(values.length / 2);
|
|
16
|
+
if (values.length % 2 === 1) {
|
|
17
|
+
return values[mid];
|
|
18
|
+
}
|
|
19
|
+
return (values[mid - 1] + values[mid]) / 2;
|
|
20
|
+
};
|
|
21
|
+
const walshAverages = values => {
|
|
22
|
+
const averages = [];
|
|
23
|
+
for (let i = 0; i < values.length; i += 1) {
|
|
24
|
+
const base = values[i];
|
|
25
|
+
for (let j = i; j < values.length; j += 1) {
|
|
26
|
+
averages.push((base + values[j]) / 2);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return averages;
|
|
30
|
+
};
|
|
31
|
+
const normalQuantile = prob => {
|
|
32
|
+
if (prob <= 0 || prob >= 1) {
|
|
33
|
+
throw new Error("Probability must be between 0 and 1.");
|
|
34
|
+
}
|
|
35
|
+
const a1 = Number.parseFloat("-39.69683028665376");
|
|
36
|
+
const a2 = Number.parseFloat("220.9460984245205");
|
|
37
|
+
const a3 = Number.parseFloat("-275.9285104469687");
|
|
38
|
+
const a4 = Number.parseFloat("138.357751867269");
|
|
39
|
+
const a5 = Number.parseFloat("-30.66479806614716");
|
|
40
|
+
const a6 = Number.parseFloat("2.506628277459239");
|
|
41
|
+
const b1 = Number.parseFloat("-54.47609879822406");
|
|
42
|
+
const b2 = Number.parseFloat("161.5858368580409");
|
|
43
|
+
const b3 = Number.parseFloat("-155.6989798598866");
|
|
44
|
+
const b4 = Number.parseFloat("66.80131188771972");
|
|
45
|
+
const b5 = Number.parseFloat("-13.28068155288572");
|
|
46
|
+
const c1 = Number.parseFloat("-0.007784894002430293");
|
|
47
|
+
const c2 = Number.parseFloat("-0.3223964580411365");
|
|
48
|
+
const c3 = Number.parseFloat("-2.400758277161838");
|
|
49
|
+
const c4 = Number.parseFloat("-2.549732539343734");
|
|
50
|
+
const c5 = Number.parseFloat("4.374664141464968");
|
|
51
|
+
const c6 = Number.parseFloat("2.938163982698783");
|
|
52
|
+
const d1 = Number.parseFloat("0.007784695709041462");
|
|
53
|
+
const d2 = Number.parseFloat("0.3224671290700398");
|
|
54
|
+
const d3 = Number.parseFloat("2.445134137142996");
|
|
55
|
+
const d4 = Number.parseFloat("3.754408661907416");
|
|
56
|
+
const plow = Number.parseFloat("0.02425");
|
|
57
|
+
const phigh = 1 - plow;
|
|
58
|
+
if (prob < plow) {
|
|
59
|
+
const q = Math.sqrt(-2 * Math.log(prob));
|
|
60
|
+
return (((((c1 * q + c2) * q + c3) * q + c4) * q + c5) * q + c6) / ((((d1 * q + d2) * q + d3) * q + d4) * q + 1);
|
|
61
|
+
}
|
|
62
|
+
if (prob > phigh) {
|
|
63
|
+
const q = Math.sqrt(-2 * Math.log(1 - prob));
|
|
64
|
+
return -(((((c1 * q + c2) * q + c3) * q + c4) * q + c5) * q + c6) / ((((d1 * q + d2) * q + d3) * q + d4) * q + 1);
|
|
65
|
+
}
|
|
66
|
+
const q = prob - 0.5;
|
|
67
|
+
const r = q * q;
|
|
68
|
+
return (((((a1 * r + a2) * r + a3) * r + a4) * r + a5) * r + a6) * q / (((((b1 * r + b2) * r + b3) * r + b4) * r + b5) * r + 1);
|
|
69
|
+
};
|
|
70
|
+
const hodgesLehmannConfidenceInterval = (sortedWalsh, size) => {
|
|
71
|
+
if (sortedWalsh.length === 0) {
|
|
72
|
+
throw new Error("Cannot compute confidence interval for empty samples.");
|
|
73
|
+
}
|
|
74
|
+
if (size <= 1) {
|
|
75
|
+
const estimate = medianOfSorted(sortedWalsh);
|
|
76
|
+
return {
|
|
77
|
+
high: estimate,
|
|
78
|
+
low: estimate
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
const walshCount = sortedWalsh.length;
|
|
82
|
+
const alpha = 0.05;
|
|
83
|
+
const meanRank = size * (size + 1) / 4;
|
|
84
|
+
const varianceRank = size * (size + 1) * (2 * size + 1) / 24;
|
|
85
|
+
const stdDevRank = Math.sqrt(varianceRank);
|
|
86
|
+
const z = normalQuantile(1 - alpha / 2);
|
|
87
|
+
const critical = Math.floor(meanRank - z * stdDevRank);
|
|
88
|
+
const lowIndex = Math.max(0, Math.min(walshCount - 1, critical));
|
|
89
|
+
const highIndex = Math.max(lowIndex, Math.min(walshCount - 1, walshCount - critical - 1));
|
|
90
|
+
return {
|
|
91
|
+
high: sortedWalsh[highIndex],
|
|
92
|
+
low: sortedWalsh[lowIndex]
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
const hodgesLehmannStats = values => {
|
|
96
|
+
if (values.length === 0) {
|
|
97
|
+
throw new Error("Cannot compute stats for an empty sample set.");
|
|
98
|
+
}
|
|
99
|
+
const walsh = walshAverages(values);
|
|
100
|
+
const sortedWalsh = sortNumbers(walsh);
|
|
101
|
+
return {
|
|
102
|
+
ci: hodgesLehmannConfidenceInterval(sortedWalsh, values.length),
|
|
103
|
+
mean: medianOfSorted(sortedWalsh)
|
|
104
|
+
};
|
|
105
|
+
};
|
|
10
106
|
const squareResiduals = (values, mean) => values.map(value => {
|
|
11
107
|
const diff = value - mean;
|
|
12
108
|
return diff * diff;
|
|
@@ -99,14 +195,7 @@ const computePairedRelativeStats = (baselineSamples, currentSamples) => {
|
|
|
99
195
|
}
|
|
100
196
|
diffs.push((currentSamples[index] - baseline) / baseline);
|
|
101
197
|
}
|
|
102
|
-
|
|
103
|
-
return {
|
|
104
|
-
ci: confidenceInterval95(samplingDistributionOfTheMean({
|
|
105
|
-
mean: diffStats.mean,
|
|
106
|
-
variance: diffStats.variance
|
|
107
|
-
}, diffStats.size), diffStats.size),
|
|
108
|
-
mean: diffStats.mean
|
|
109
|
-
};
|
|
198
|
+
return hodgesLehmannStats(diffs);
|
|
110
199
|
};
|
|
111
200
|
const computePairedAbsoluteStats = (baselineSamples, currentSamples) => {
|
|
112
201
|
const size = Math.min(baselineSamples.length, currentSamples.length);
|
|
@@ -117,14 +206,7 @@ const computePairedAbsoluteStats = (baselineSamples, currentSamples) => {
|
|
|
117
206
|
for (let index = 0; index < size; index += 1) {
|
|
118
207
|
diffs.push(currentSamples[index] - baselineSamples[index]);
|
|
119
208
|
}
|
|
120
|
-
|
|
121
|
-
return {
|
|
122
|
-
ci: confidenceInterval95(samplingDistributionOfTheMean({
|
|
123
|
-
mean: diffStats.mean,
|
|
124
|
-
variance: diffStats.variance
|
|
125
|
-
}, diffStats.size), diffStats.size),
|
|
126
|
-
mean: diffStats.mean
|
|
127
|
-
};
|
|
209
|
+
return hodgesLehmannStats(diffs);
|
|
128
210
|
};
|
|
129
211
|
export const computeRelativeDifferenceFromSamples = (baselineSamples, currentSamples) => ({
|
|
130
212
|
absolute: computePairedAbsoluteStats(baselineSamples, currentSamples),
|