function OutlierDetector (varianceDecayTime, meanDecayTime, outlierCutoff, filterOutliers) {
var mean = null
var tPrev = null
var n = 0;
var count = 0
var M2n = 0
var M20 = 1e-5
this.assimilate = sample => {
var result = {};
result.t = sample.t;
var dt = sample.t - tPrev;
var deviationsFromMean = Math.abs(sample.y - mean) / Math.sqrt(M2n / n);
var varianceDecayFactor = count < 2 ? 1 : Math.exp(-dt / varianceDecayTime * Math.log(2.0));
count++;
n = n * varianceDecayFactor + 1;
switch(count) {
case 1:
mean = sample.y;
M2n = M20;
result.mean = mean;
result.deviationsFromMean = 0.0;
result.isOutlier = false;
break;
case 2:
mean = (mean + sample.y) * 0.5;
M2n = Math.pow((mean - sample.y) * 10.0, 2)
result.mean = mean
result.deviationsFromMean = 0.0;
result.isOutlier = false;
break;
default:
var meanDecayFactor = Math.exp(-dt / meanDecayTime * Math.log(2.0));
if (!filterOutliers || (M2n === 0.0 || deviationsFromMean <= outlierCutoff)) {
var prevMean = mean;
mean = mean * meanDecayFactor + sample.y * (1.0 - meanDecayFactor);
M2n = M2n * varianceDecayFactor + (sample.y - prevMean) * (sample.y - mean);
}
result.mean = mean;
result.isOutlier = true;
}
result.y = sample.y
result.variance = M2n / n;
result.deviationsFromMean = Math.abs(result.y - result.mean) / Math.sqrt(result.variance)
result.outlierScore = result.deviationsFromMean / outlierCutoff
tPrev = sample.t;
return result;
}
this.reset = () => {
tPrev = null
mean = null
count = 0
M2n = M20
return this
}
}