Skip to content

Commit 335b69a

Browse files
committed
WIP: add grubbs functions
1 parent 9367274 commit 335b69a

File tree

6 files changed

+422
-85
lines changed

6 files changed

+422
-85
lines changed

grubbs/criticalValueTable.js

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
module.exports = {
2+
0.01: {
3+
3: 1.155,
4+
4: 1.496,
5+
5: 1.764,
6+
6: 1.973,
7+
7: 2.139,
8+
8: 2.274,
9+
9: 2.387,
10+
10: 2.482,
11+
11: 2.564,
12+
12: 2.636,
13+
13: 2.699,
14+
14: 2.755,
15+
15: 2.806,
16+
16: 2.852,
17+
17: 2.894,
18+
18: 2.932,
19+
19: 2.968,
20+
20: 3.001,
21+
21: 3.031,
22+
22: 3.06,
23+
23: 3.087,
24+
24: 3.112,
25+
25: 3.135,
26+
26: 3.157,
27+
27: 3.178,
28+
28: 3.199,
29+
29: 3.218,
30+
30: 3.236,
31+
31: 3.253,
32+
32: 3.27,
33+
33: 3.286,
34+
34: 3.301,
35+
35: 3.316,
36+
36: 3.33,
37+
37: 3.343,
38+
38: 3.356,
39+
39: 3.369,
40+
40: 3.381,
41+
},
42+
0.05: {
43+
3: 1.155,
44+
4: 1.481,
45+
5: 1.715,
46+
6: 1.887,
47+
7: 2.02,
48+
8: 2.126,
49+
9: 2.215,
50+
10: 2.29,
51+
11: 2.355,
52+
12: 2.412,
53+
13: 2.462,
54+
14: 2.507,
55+
15: 2.549,
56+
16: 2.585,
57+
17: 2.62,
58+
18: 2.651,
59+
19: 2.681,
60+
20: 2.709,
61+
21: 2.733,
62+
22: 2.758,
63+
23: 2.781,
64+
24: 2.802,
65+
25: 2.822,
66+
26: 2.841,
67+
27: 2.859,
68+
28: 2.876,
69+
29: 2.893,
70+
30: 2.908,
71+
31: 2.924,
72+
32: 2.938,
73+
33: 2.952,
74+
34: 2.965,
75+
35: 2.979,
76+
36: 2.991,
77+
37: 3.003,
78+
38: 3.014,
79+
39: 3.025,
80+
40: 3.036,
81+
},
82+
};

grubbs/index.js

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
var stdev = require('compute-stdev');
2+
var average = require('average');
3+
var criticalValueTable = require('./criticalValueTable');
4+
5+
function test(originDataSet, originOptions) {
6+
if (typeof originDataSet === 'undefined') {
7+
throw new Error('dataSet MUST be passed');
8+
}
9+
if (originDataSet.filter(isValidData).length > 100) {
10+
throw new Error('dataSet.length MUST less than 100');
11+
}
12+
if (originDataSet.filter(isValidData).length <= 2) {
13+
throw new Error('dataSet.length MUST greater than 2');
14+
}
15+
// defaultOptions
16+
var options = {
17+
alpha: 0.01,
18+
recursion: true
19+
};
20+
// Merge options
21+
if (typeof originOptions !== 'undefined') {
22+
if (typeof originOptions.alpha !== 'undefined') {
23+
options.alpha = originOptions.alpha;
24+
}
25+
// TODO no recursion mode is not support yet
26+
// if (typeof options_.recursion !== 'undefined') {
27+
// options.recursion = options_.recursion;
28+
// }
29+
}
30+
var criticalValue = criticalValueTable[options.alpha];
31+
if (typeof criticalValue === 'undefined') {
32+
throw new Error('alpha ' + options.alpha + ' is not support');
33+
}
34+
var digit = getDigit(originDataSet);
35+
var powDigit = Math.pow(10, digit);
36+
37+
// Main algorithm
38+
var result = [];
39+
var done = false;
40+
var dataSet = originDataSet.slice();
41+
var currentRound = {};
42+
var i;
43+
var gResult;
44+
// If no outlier, done
45+
while (!done) {
46+
done = true;
47+
currentRound = {};
48+
currentRound.dataSet = dataSet.slice();
49+
currentRound.stdev = stdev(currentRound.dataSet.filter(isValidData));
50+
currentRound.average =
51+
Math.round(average(currentRound.dataSet.filter(isValidData)) * powDigit) / powDigit;
52+
currentRound.criticalValue = criticalValue[currentRound.dataSet.filter(isValidData).length];
53+
currentRound.gSet = [];
54+
// true if pass, false if unpass, undefined if no data
55+
currentRound.gPass = [];
56+
currentRound.outliers = [];
57+
currentRound.outlierIndexes = [];
58+
for (i = 0; i < currentRound.dataSet.length; i++) {
59+
if (typeof currentRound.dataSet[i] === 'undefined') {
60+
currentRound.gSet.push(undefined);
61+
currentRound.gPass.push(undefined);
62+
continue;
63+
}
64+
if (typeof currentRound.dataSet[i] !== 'number') {
65+
throw new Error('data MUST be number');
66+
}
67+
gResult = (currentRound.dataSet[i] - currentRound.average) / currentRound.stdev;
68+
currentRound.gSet.push(gResult);
69+
if (Math.abs(gResult) > currentRound.criticalValue) {
70+
done = false;
71+
currentRound.gPass.push(false);
72+
currentRound.outliers.push(currentRound.dataSet[i]);
73+
currentRound.outlierIndexes.push(i);
74+
dataSet[i] = undefined;
75+
} else {
76+
currentRound.gPass.push(true);
77+
}
78+
}
79+
result.push(currentRound);
80+
}
81+
return result;
82+
}
83+
84+
function isValidData(data) {
85+
return (
86+
typeof data !== 'undefined' &&
87+
!isNaN(data) &&
88+
data !== null
89+
);
90+
}
91+
92+
function getDigit(dataSet) {
93+
if (!dataSet) return 0;
94+
var filteredDataSet = dataSet.filter(isValidData);
95+
var filteredDataSetLength = filteredDataSet.length;
96+
if (filteredDataSetLength === 0) return 0;
97+
var digit = 0;
98+
filteredDataSet.forEach(function (data) {
99+
var dataString = data.toString();
100+
var dotIndex = dataString.indexOf('.');
101+
if (dotIndex === -1) return;
102+
var currentDigit = dataString.length - dotIndex - 1;
103+
if (currentDigit > digit) {
104+
digit = currentDigit;
105+
}
106+
});
107+
return digit;
108+
}
109+
110+
module.exports = {
111+
test: test,
112+
isValidData: isValidData
113+
};

index.ts

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,6 @@
1-
function getStandardDeviation(arr: number[]) {
2-
const n = arr.length;
3-
const mean = arr.reduce((a, b) => a + b) / n;
4-
return Math.sqrt(arr.map((x) => Math.pow(x - mean, 2)).reduce((a, b) => a + b) / n);
5-
}
6-
function calculateMedian(arr: number[]) {
7-
const s = [...arr].sort((a, b) => a - b);
8-
const mid = Math.floor(s.length / 2);
9-
return s.length % 2 === 0 ? (s[mid - 1] + s[mid]) / 2 : s[mid];
10-
}
11-
function Mestimator(arr: number[], x: number) {
12-
var sumSeries = 0;
13-
for (var i = arr.length - 1; i >= 0; i--) {
14-
sumSeries += Number(Math.abs(arr[i] - x));
15-
}
16-
return (1 / (0.798 * arr.length)) * sumSeries;
17-
}
18-
function Uvalue(s: number, arr: number[]) {
19-
return (1.25 * s) / Math.sqrt(arr.length);
20-
}
1+
import {calculateUvalue} from './mad'
212

223
var arr = [1, 2, 3, 4, 5, 6, 3, 8, 2, 20];
23-
var standartDeviation = getStandardDeviation(arr);
24-
var madex = Math.abs(standartDeviation - calculateMedian(arr)) * 1.483;
4+
var Uvalue = calculateUvalue(arr);
255

26-
var result = Uvalue(Mestimator(arr, madex), arr);
27-
console.log(result);
6+
console.log(Uvalue);

mad/index.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { standardDeviation, median } from "simple-statistics";
2+
3+
function Mestimator(arr: number[], x: number) {
4+
var sumSeries = 0;
5+
for (var i = arr.length - 1; i >= 0; i--) {
6+
sumSeries += Number(Math.abs(arr[i] - x));
7+
}
8+
return (1 / (0.798 * arr.length)) * sumSeries;
9+
}
10+
function Uvalue(s: number, arr: number[]) {
11+
return (1.25 * s) / Math.sqrt(arr.length);
12+
}
13+
14+
function calculateUvalue(arr: number[]) {
15+
var madex = Math.abs(standardDeviation(arr) - median(arr)) * 1.483;
16+
var result = Uvalue(Mestimator(arr, madex), arr);
17+
return result;
18+
}
19+
20+
export { calculateUvalue };

0 commit comments

Comments
 (0)