in public/pages/utils/anomalyResultUtils.ts [680:788]
entity: get(rawAnomaly, 'entity'),
});
});
}
return anomalies;
};
export type FeatureDataPoint = {
isMissing: boolean;
plotTime: number;
startTime: number;
endTime: number;
};
export const FEATURE_DATA_CHECK_WINDOW_OFFSET = 2;
export const getFeatureDataPoints = (
featureData: FeatureAggregationData[],
interval: number,
dateRange: DateRange | undefined
): FeatureDataPoint[] => {
const featureDataPoints = [] as FeatureDataPoint[];
if (!dateRange) {
return featureDataPoints;
}
const existingTimes = isEmpty(featureData)
? []
: featureData
.map((feature) => getRoundedTimeInMin(feature.startTime))
.filter((featureTime) => featureTime != undefined);
for (
let currentTime = getRoundedTimeInMin(dateRange.startDate);
currentTime <
// skip checking for latest interval as data point for it may not be delivered in time
getRoundedTimeInMin(
dateRange.endDate -
FEATURE_DATA_CHECK_WINDOW_OFFSET * interval * MIN_IN_MILLI_SECS
);
currentTime += interval * MIN_IN_MILLI_SECS
) {
const isExisting = findTimeExistsInWindow(
existingTimes,
getRoundedTimeInMin(currentTime),
getRoundedTimeInMin(currentTime) + interval * MIN_IN_MILLI_SECS
);
featureDataPoints.push({
isMissing: !isExisting,
plotTime: currentTime + interval * MIN_IN_MILLI_SECS,
startTime: currentTime,
endTime: currentTime + interval * MIN_IN_MILLI_SECS,
});
}
return featureDataPoints;
};
const findTimeExistsInWindow = (
timestamps: any[],
startTime: number,
endTime: number
): boolean => {
// timestamps is in desc order
let result = false;
if (isEmpty(timestamps)) {
return result;
}
let left = 0;
let right = timestamps.length - 1;
while (left <= right) {
let middle = Math.floor((right + left) / 2);
if (timestamps[middle] >= startTime && timestamps[middle] < endTime) {
result = true;
break;
}
if (timestamps[middle] < startTime) {
right = middle - 1;
}
if (timestamps[middle] >= endTime) {
left = middle + 1;
}
}
return result;
};
const getRoundedTimeInMin = (timestamp: number): number => {
return Math.round(timestamp / MIN_IN_MILLI_SECS) * MIN_IN_MILLI_SECS;
};
const sampleFeatureMissingDataPoints = (
featureMissingDataPoints: FeatureDataPoint[],
dateRange?: DateRange
): FeatureDataPoint[] => {
if (!dateRange) {
return featureMissingDataPoints;
}
const sampleTimeWindows = calculateTimeWindowsWithMaxDataPoints(
MAX_FEATURE_ANNOTATIONS,
dateRange
);
const sampledResults = [] as FeatureDataPoint[];
for (const timeWindow of sampleTimeWindows) {
const sampledDataPoint = getMiddleDataPoint(
getDataPointsInWindow(featureMissingDataPoints, timeWindow)
);
if (sampledDataPoint) {
sampledResults.push({