in src/main/scala/com/amazon/deequ/anomalydetection/OnlineNormalStrategy.scala [70:120]
def computeStatsAndAnomalies(
dataSeries: Vector[Double],
searchInterval: (Int, Int) = (0, Int.MaxValue)): Seq[OnlineCalculationResult] = {
val ret: ArrayBuffer[OnlineCalculationResult] = ArrayBuffer()
var currentMean = 0.0
var currentVariance = 0.0
var Sn = 0.0
val numValuesToSkip = dataSeries.length * ignoreStartPercentage
for (currentIndex <- dataSeries.indices) {
val currentValue = dataSeries(currentIndex)
val lastMean = currentMean
val lastVariance = currentVariance
val lastSn = Sn
if (currentIndex == 0) {
currentMean = currentValue
} else {
currentMean = lastMean + (1.0 / (currentIndex + 1)) * (currentValue - lastMean)
}
Sn += (currentValue - lastMean) * (currentValue - currentMean)
currentVariance = Sn / (currentIndex + 1)
val stdDev = sqrt(currentVariance)
val upperBound = currentMean + upperDeviationFactor.getOrElse(Double.MaxValue) * stdDev
val lowerBound = currentMean - lowerDeviationFactor.getOrElse(Double.MaxValue) * stdDev
val (searchStart, searchEnd) = searchInterval
if (currentIndex < numValuesToSkip ||
currentIndex < searchStart || currentIndex >= searchEnd ||
(currentValue <= upperBound && currentValue >= lowerBound)) {
ret += OnlineCalculationResult(currentMean, stdDev, isAnomaly = false)
} else {
if (ignoreAnomalies) {
// Anomaly doesn't affect mean and variance
currentMean = lastMean
currentVariance = lastVariance
Sn = lastSn
}
ret += OnlineCalculationResult(currentMean, stdDev, isAnomaly = true)
}
}
ret
}