use of com.linkedin.thirdeye.anomaly.views.AnomalyTimelinesView in project pinot by linkedin.
the class AbstractModularizedAnomalyFunction method getTimeSeriesView.
// TODO: Generate time series view using ViewModel
@Override
public AnomalyTimelinesView getTimeSeriesView(MetricTimeSeries timeSeries, long bucketMillis, String metric, long viewWindowStartTime, long viewWindowEndTime, List<MergedAnomalyResultDTO> knownAnomalies) {
AnomalyDetectionContext anomalyDetectionContext = BackwardAnomalyFunctionUtils.buildAnomalyDetectionContext(this, timeSeries, spec.getTopicMetric(), null, spec.getBucketSize(), spec.getBucketUnit(), new DateTime(viewWindowStartTime), new DateTime(viewWindowEndTime));
String mainMetric = anomalyDetectionContext.getAnomalyDetectionFunction().getSpec().getTopicMetric();
this.transformAndPredictTimeSeries(mainMetric, anomalyDetectionContext);
TimeSeries observedTS = anomalyDetectionContext.getTransformedCurrent(mainMetric);
TimeSeries expectedTS = ((ExpectedTimeSeriesPredictionModel) anomalyDetectionContext.getTrainedPredictionModel(mainMetric)).getExpectedTimeSeries();
long expectedTSStartTime = expectedTS.getTimeSeriesInterval().getStartMillis();
// Construct AnomalyTimelinesView
AnomalyTimelinesView anomalyTimelinesView = new AnomalyTimelinesView();
int bucketCount = (int) ((viewWindowEndTime - viewWindowStartTime) / bucketMillis);
for (int i = 0; i < bucketCount; ++i) {
long currentBucketMillis = viewWindowStartTime + i * bucketMillis;
long baselineBucketMillis = expectedTSStartTime + i * bucketMillis;
double observedValue = 0d;
if (observedTS.hasTimestamp(currentBucketMillis)) {
observedValue = observedTS.get(currentBucketMillis);
}
double expectedValue = 0d;
if (expectedTS.hasTimestamp(baselineBucketMillis)) {
expectedValue = expectedTS.get(baselineBucketMillis);
}
TimeBucket timebucket = new TimeBucket(currentBucketMillis, currentBucketMillis + bucketMillis, baselineBucketMillis, baselineBucketMillis + bucketMillis);
anomalyTimelinesView.addTimeBuckets(timebucket);
anomalyTimelinesView.addCurrentValues(observedValue);
anomalyTimelinesView.addBaselineValues(expectedValue);
}
return anomalyTimelinesView;
}
use of com.linkedin.thirdeye.anomaly.views.AnomalyTimelinesView in project pinot by linkedin.
the class AnomalyResource method getAnomalyMergedResultTimeSeries.
/**
* Returns the time series for the given anomaly.
*
* If viewWindowStartTime and/or viewWindowEndTime is not given, then a window is padded automatically. The padded
* windows is half of the anomaly window size. For instance, if the anomaly lasts for 4 hours, then the pad window
* size is 2 hours. The max padding size is 1 day.
*
* @param anomalyResultId the id of the given anomaly
* @param viewWindowStartTime start time of the time series, inclusive
* @param viewWindowEndTime end time of the time series, inclusive
* @return the time series of the given anomaly
* @throws Exception when it fails to retrieve collection, i.e., dataset, information
*/
@GET
@Path("/anomaly-merged-result/timeseries/{anomaly_merged_result_id}")
public AnomalyTimelinesView getAnomalyMergedResultTimeSeries(@NotNull @PathParam("anomaly_merged_result_id") long anomalyResultId, @NotNull @QueryParam("aggTimeGranularity") String aggTimeGranularity, @QueryParam("start") long viewWindowStartTime, @QueryParam("end") long viewWindowEndTime) throws Exception {
boolean loadRawAnomalies = false;
MergedAnomalyResultDTO anomalyResult = anomalyMergedResultDAO.findById(anomalyResultId, loadRawAnomalies);
DimensionMap dimensions = anomalyResult.getDimensions();
AnomalyFunctionDTO anomalyFunctionSpec = anomalyResult.getFunction();
BaseAnomalyFunction anomalyFunction = anomalyFunctionFactory.fromSpec(anomalyFunctionSpec);
// By default, the padding window size is half of the anomaly window.
if (viewWindowStartTime == 0 || viewWindowEndTime == 0) {
long anomalyWindowStartTime = anomalyResult.getStartTime();
long anomalyWindowEndTime = anomalyResult.getEndTime();
long bucketMillis = TimeUnit.MILLISECONDS.convert(anomalyFunctionSpec.getBucketSize(), anomalyFunctionSpec.getBucketUnit());
long bucketCount = (anomalyWindowEndTime - anomalyWindowStartTime) / bucketMillis;
long paddingMillis = Math.max(1, (bucketCount / 2)) * bucketMillis;
if (paddingMillis > TimeUnit.DAYS.toMillis(1)) {
paddingMillis = TimeUnit.DAYS.toMillis(1);
}
if (viewWindowStartTime == 0) {
viewWindowStartTime = anomalyWindowStartTime - paddingMillis;
}
if (viewWindowEndTime == 0) {
viewWindowEndTime = anomalyWindowEndTime + paddingMillis;
}
}
TimeGranularity timeGranularity = Utils.getAggregationTimeGranularity(aggTimeGranularity, anomalyFunctionSpec.getCollection());
long bucketMillis = timeGranularity.toMillis();
// ThirdEye backend is end time exclusive, so one more bucket is appended to make end time inclusive for frontend.
viewWindowEndTime += bucketMillis;
long maxDataTime = collectionMaxDataTimeCache.get(anomalyResult.getCollection());
if (viewWindowEndTime > maxDataTime) {
viewWindowEndTime = (anomalyResult.getEndTime() > maxDataTime) ? anomalyResult.getEndTime() : maxDataTime;
}
AnomalyDetectionInputContext adInputContext = TimeBasedAnomalyMerger.fetchDataByDimension(viewWindowStartTime, viewWindowEndTime, dimensions, anomalyFunction, anomalyMergedResultDAO, overrideConfigDAO, false);
MetricTimeSeries metricTimeSeries = adInputContext.getDimensionKeyMetricTimeSeriesMap().get(dimensions);
if (metricTimeSeries == null) {
// the timeseries for the given anomaly
return new AnomalyTimelinesView();
}
// Transform time series with scaling factor
List<ScalingFactor> scalingFactors = adInputContext.getScalingFactors();
if (CollectionUtils.isNotEmpty(scalingFactors)) {
Properties properties = anomalyFunction.getProperties();
MetricTransfer.rescaleMetric(metricTimeSeries, viewWindowStartTime, scalingFactors, anomalyFunctionSpec.getTopicMetric(), properties);
}
List<MergedAnomalyResultDTO> knownAnomalies = adInputContext.getKnownMergedAnomalies().get(dimensions);
// Known anomalies are ignored (the null parameter) because 1. we can reduce users' waiting time and 2. presentation
// data does not need to be as accurate as the one used for detecting anomalies
AnomalyTimelinesView anomalyTimelinesView = anomalyFunction.getTimeSeriesView(metricTimeSeries, bucketMillis, anomalyFunctionSpec.getTopicMetric(), viewWindowStartTime, viewWindowEndTime, knownAnomalies);
// Generate summary for frontend
List<TimeBucket> timeBuckets = anomalyTimelinesView.getTimeBuckets();
if (timeBuckets.size() > 0) {
TimeBucket firstBucket = timeBuckets.get(0);
anomalyTimelinesView.addSummary("currentStart", Long.toString(firstBucket.getCurrentStart()));
anomalyTimelinesView.addSummary("baselineStart", Long.toString(firstBucket.getBaselineStart()));
TimeBucket lastBucket = timeBuckets.get(timeBuckets.size() - 1);
anomalyTimelinesView.addSummary("currentEnd", Long.toString(lastBucket.getCurrentStart()));
anomalyTimelinesView.addSummary("baselineEnd", Long.toString(lastBucket.getBaselineEnd()));
}
return anomalyTimelinesView;
}
use of com.linkedin.thirdeye.anomaly.views.AnomalyTimelinesView in project pinot by linkedin.
the class BaseAnomalyFunction method getTimeSeriesView.
/**
* This method provides a view of current time series, i.e., no baseline time series.
*
* @param timeSeries the time series that contains the metric to be processed
* @param bucketMillis the size of a bucket in milli-seconds
* @param metric the metric name to retrieve the data from the given time series
* @param viewWindowStartTime the start time bucket of current time series, inclusive
* @param viewWindowEndTime the end time buckets of current time series, exclusive
* @param knownAnomalies it is assumed to be null for presentational purpose.
* @return
*/
@Override
public AnomalyTimelinesView getTimeSeriesView(MetricTimeSeries timeSeries, long bucketMillis, String metric, long viewWindowStartTime, long viewWindowEndTime, List<MergedAnomalyResultDTO> knownAnomalies) {
AnomalyTimelinesView anomalyTimelinesView = new AnomalyTimelinesView();
// Construct Week-over-Week AnomalyTimelinesView
int bucketCount = (int) ((viewWindowEndTime - viewWindowStartTime) / bucketMillis);
for (int i = 0; i < bucketCount; ++i) {
long currentBucketMillis = viewWindowStartTime + i * bucketMillis;
long baselineBucketMillis = currentBucketMillis - TimeUnit.DAYS.toMillis(7);
TimeBucket timebucket = new TimeBucket(currentBucketMillis, currentBucketMillis + bucketMillis, baselineBucketMillis, baselineBucketMillis + bucketMillis);
anomalyTimelinesView.addTimeBuckets(timebucket);
anomalyTimelinesView.addCurrentValues(timeSeries.get(currentBucketMillis, metric).doubleValue());
anomalyTimelinesView.addBaselineValues(timeSeries.get(baselineBucketMillis, metric).doubleValue());
}
return anomalyTimelinesView;
}
use of com.linkedin.thirdeye.anomaly.views.AnomalyTimelinesView in project pinot by linkedin.
the class RatioOutlierFunction method getTimeSeriesView.
@Override
public AnomalyTimelinesView getTimeSeriesView(MetricTimeSeries timeSeries, long bucketMillis, String metric, long viewWindowStartTime, long viewWindowEndTime, List<MergedAnomalyResultDTO> knownAnomalies) {
double min = 0.0d;
try {
// Parse function properties
Properties props = getProperties();
// Get min / max props
if (props.containsKey(MIN_VAL)) {
min = Double.valueOf(props.getProperty(MIN_VAL));
}
} catch (IOException e) {
LOG.warn("Error extracting min value, using 0.0 instead");
}
String m_a = getSpec().getMetrics().get(0);
String m_b = getSpec().getMetrics().get(1);
AnomalyTimelinesView view = new AnomalyTimelinesView();
int bucketCount = (int) ((viewWindowEndTime - viewWindowStartTime) / bucketMillis);
for (int i = 0; i < bucketCount; ++i) {
long currentBucketMillis = viewWindowStartTime + i * bucketMillis;
long baselineBucketMillis = currentBucketMillis - TimeUnit.DAYS.toMillis(7);
TimeBucket timebucket = new TimeBucket(currentBucketMillis, currentBucketMillis + bucketMillis, baselineBucketMillis, baselineBucketMillis + bucketMillis);
view.addTimeBuckets(timebucket);
double value_a = timeSeries.get(currentBucketMillis, m_a).doubleValue();
double value_b = timeSeries.get(currentBucketMillis, m_b).doubleValue();
if (value_b != 0.0d) {
double ratio = value_a / value_b;
view.addCurrentValues(ratio);
} else {
view.addCurrentValues(Double.NaN);
}
view.addBaselineValues(min);
}
return view;
}
use of com.linkedin.thirdeye.anomaly.views.AnomalyTimelinesView in project pinot by linkedin.
the class AnomaliesResource method getAnomalyDetails.
/**
* Generates Anomaly Details for each merged anomaly
* @param mergedAnomaly
* @param datasetConfig
* @param timeSeriesDateFormatter
* @param startEndDateFormatterHours
* @param startEndDateFormatterDays
* @param externalUrl
* @return
*/
private AnomalyDetails getAnomalyDetails(MergedAnomalyResultDTO mergedAnomaly, DatasetConfigDTO datasetConfig, DateTimeFormatter timeSeriesDateFormatter, DateTimeFormatter startEndDateFormatterHours, DateTimeFormatter startEndDateFormatterDays, String externalUrl) throws Exception {
String dataset = datasetConfig.getDataset();
String metricName = mergedAnomaly.getMetric();
AnomalyFunctionDTO anomalyFunctionSpec = anomalyFunctionDAO.findById(mergedAnomaly.getFunctionId());
BaseAnomalyFunction anomalyFunction = anomalyFunctionFactory.fromSpec(anomalyFunctionSpec);
String aggGranularity = constructAggGranularity(datasetConfig);
long anomalyStartTime = mergedAnomaly.getStartTime();
long anomalyEndTime = mergedAnomaly.getEndTime();
TimeRange range = getTimeseriesOffsetedTimes(anomalyStartTime, anomalyEndTime, datasetConfig);
long currentStartTime = range.getStart();
long currentEndTime = range.getEnd();
DimensionMap dimensions = mergedAnomaly.getDimensions();
TimeGranularity timeGranularity = Utils.getAggregationTimeGranularity(aggGranularity, anomalyFunctionSpec.getCollection());
long bucketMillis = timeGranularity.toMillis();
AnomalyDetails anomalyDetails = null;
try {
AnomalyDetectionInputContext adInputContext = TimeBasedAnomalyMerger.fetchDataByDimension(currentStartTime, currentEndTime, dimensions, anomalyFunction, mergedAnomalyResultDAO, overrideConfigDAO, true);
MetricTimeSeries metricTimeSeries = adInputContext.getDimensionKeyMetricTimeSeriesMap().get(dimensions);
// Transform time series with scaling factor
List<ScalingFactor> scalingFactors = adInputContext.getScalingFactors();
if (CollectionUtils.isNotEmpty(scalingFactors)) {
Properties properties = anomalyFunction.getProperties();
MetricTransfer.rescaleMetric(metricTimeSeries, currentStartTime, scalingFactors, anomalyFunctionSpec.getTopicMetric(), properties);
}
List<MergedAnomalyResultDTO> knownAnomalies = adInputContext.getKnownMergedAnomalies().get(dimensions);
// Known anomalies are ignored (the null parameter) because 1. we can reduce users' waiting time and 2. presentation
// data does not need to be as accurate as the one used for detecting anomalies
AnomalyTimelinesView anomalyTimelinesView = anomalyFunction.getTimeSeriesView(metricTimeSeries, bucketMillis, anomalyFunctionSpec.getTopicMetric(), currentStartTime, currentEndTime, knownAnomalies);
anomalyDetails = constructAnomalyDetails(metricName, dataset, datasetConfig, mergedAnomaly, anomalyFunctionSpec, currentStartTime, currentEndTime, anomalyTimelinesView, timeSeriesDateFormatter, startEndDateFormatterHours, startEndDateFormatterDays, externalUrl);
} catch (Exception e) {
LOG.error("Exception in constructing anomaly wrapper for anomaly {}", mergedAnomaly.getId(), e);
}
return anomalyDetails;
}
Aggregations