def computeStatsAndAnomalies()

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
  }