-
Notifications
You must be signed in to change notification settings - Fork 548
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Exposing Helpful Anomaly Detection Metadata from Anomaly Strategies (ie Anomaly Check Range/Thresholds) through backwards compatible function #593
base: master
Are you sure you want to change the base?
Changes from all commits
5338fe4
6040cef
02ed720
a8780b7
185ce01
e48f97a
efeec97
9fa5096
c7fa635
abb54bc
c2e862f
6538ef3
b69f2b8
efd33f0
a4a8aa6
572d776
dc9ba7e
2a02afe
ee26d1c
97f7a3e
9d92d94
0f81982
fdebce5
5da25c4
198a41f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -56,12 +56,8 @@ case class AnomalyDetector(strategy: AnomalyDetectionStrategy) { | |
|
||
val allDataPoints = sortedDataPoints :+ newPoint | ||
|
||
// Run anomaly | ||
val anomalies = detectAnomaliesInHistory(allDataPoints, (newPoint.time, Long.MaxValue)) | ||
.anomalies | ||
|
||
// Create a Detection result with all anomalies | ||
DetectionResult(anomalies) | ||
// Run anomaly and create a Detection result with all anomalies | ||
detectAnomaliesInHistory(allDataPoints, (newPoint.time, Long.MaxValue)) | ||
} | ||
|
||
/** | ||
|
@@ -100,3 +96,86 @@ case class AnomalyDetector(strategy: AnomalyDetectionStrategy) { | |
DetectionResult(anomalies.map { case (index, anomaly) => (sortedTimestamps(index), anomaly) }) | ||
} | ||
} | ||
|
||
case class AnomalyDetectorWithExtendedResults(strategy: AnomalyDetectionStrategyWithExtendedResults) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same question here - Can the methods inside this case class be a part of the |
||
|
||
|
||
/** | ||
* Given a sequence of metrics and a current value, detects if there is an anomaly by using the | ||
* given algorithm and returns extended results. | ||
* | ||
* @param historicalDataPoints Sequence of tuples (Points in time with corresponding Metric). | ||
* @param newPoint A new data point to check if there are anomalies | ||
* @return | ||
*/ | ||
def isNewPointAnomalousWithExtendedResults( | ||
historicalDataPoints: Seq[DataPoint[Double]], | ||
newPoint: DataPoint[Double]) | ||
: ExtendedDetectionResult = { | ||
|
||
require(historicalDataPoints.nonEmpty, "historicalDataPoints must not be empty!") | ||
|
||
val sortedDataPoints = historicalDataPoints.sortBy(_.time) | ||
|
||
val firstDataPointTime = sortedDataPoints.head.time | ||
val lastDataPointTime = sortedDataPoints.last.time | ||
|
||
val newPointTime = newPoint.time | ||
|
||
require(lastDataPointTime < newPointTime, | ||
s"Can't decide which range to use for anomaly detection. New data point with time " + | ||
s"$newPointTime is in history range ($firstDataPointTime - $lastDataPointTime)!") | ||
|
||
val allDataPoints = sortedDataPoints :+ newPoint | ||
|
||
// Run anomaly and create an Extended Detection result with all data points and anomaly details | ||
detectAnomaliesInHistoryWithExtendedResults(allDataPoints, (newPoint.time, Long.MaxValue)) | ||
} | ||
|
||
|
||
/** | ||
* Given a strategy, detects anomalies in a time series after some preprocessing | ||
* and returns extended results. | ||
* | ||
* @param dataSeries Sequence of tuples (Points in time with corresponding value). | ||
* @param searchInterval The interval in which anomalies should be detected. [a, b). | ||
* @return A wrapper object, containing all data points with anomaly extended results. | ||
*/ | ||
def detectAnomaliesInHistoryWithExtendedResults( | ||
dataSeries: Seq[DataPoint[Double]], | ||
searchInterval: (Long, Long) = (Long.MinValue, Long.MaxValue)) | ||
: ExtendedDetectionResult = { | ||
|
||
def findIndexForBound(sortedTimestamps: Seq[Long], boundValue: Long): Int = { | ||
sortedTimestamps.search(boundValue).insertionPoint | ||
} | ||
|
||
val (searchStart, searchEnd) = searchInterval | ||
|
||
require(searchStart <= searchEnd, | ||
"The first interval element has to be smaller or equal to the last.") | ||
|
||
// Remove missing values and sort series by time | ||
val removedMissingValues = dataSeries.filter { | ||
_.metricValue.isDefined | ||
} | ||
val sortedSeries = removedMissingValues.sortBy { | ||
_.time | ||
} | ||
val sortedTimestamps = sortedSeries.map { | ||
_.time | ||
} | ||
|
||
// Find indices of lower and upper bound | ||
val lowerBoundIndex = findIndexForBound(sortedTimestamps, searchStart) | ||
val upperBoundIndex = findIndexForBound(sortedTimestamps, searchEnd) | ||
|
||
val anomalies = strategy.detectWithExtendedResults( | ||
sortedSeries.flatMap { | ||
_.metricValue | ||
}.toVector, (lowerBoundIndex, upperBoundIndex)) | ||
|
||
ExtendedDetectionResult(anomalies.map { case (index, anomaly) => (sortedTimestamps(index), anomaly) }) | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be part of
AnomalyDetectionStrategy
? Do we require a new trait ?