in src/main/scala/com/amazon/deequ/anomalydetection/seasonal/HoltWinters.scala [202:248]
override def detect(
dataSeries: Vector[Double],
searchInterval: (Int, Int) = (0, Int.MaxValue))
: Seq[(Int, Anomaly)] = {
require(dataSeries.nonEmpty, "Provided data series is empty")
val (start, end) = searchInterval
require(start < end,
"Start must be before end")
require(start >= 0 && end >= 0,
"The search interval needs to be strictly positive")
require(start >= seriesPeriodicity * 2,
"Need at least two full cycles of data to estimate model")
val numberOfObservationsToForecast =
if (start >= dataSeries.size) {
1
} else {
math.min(end, dataSeries.size) - start
}
val trainingSeries = dataSeries.slice(0, start)
val optimalParameters = modelSelectionFor(trainingSeries, numberOfObservationsToForecast)
println("Found optimal parameters for level, trend and seasonality to be " +
s"${optimalParameters.alpha}, ${optimalParameters.beta} and ${optimalParameters.gamma} " +
"respectively.")
// Forecast with estimated parameters
val modelResults = additiveHoltWinters(
trainingSeries,
seriesPeriodicity,
numberOfObservationsToForecast,
optimalParameters.alpha,
optimalParameters.beta,
optimalParameters.gamma
)
val residualsStandardDeviation =
breeze.stats.stddev(modelResults.residuals.map(math.abs))
require(modelResults.forecasts.size == numberOfObservationsToForecast)
val testSeries = dataSeries.drop(start)
findAnomalies(testSeries, modelResults.forecasts, start, residualsStandardDeviation)
}