private void BuildTree(PointTree tree, List <string> aggDims, TimeSeriesPoint point, Object aggSymbol) { int aggNum = 0; string nextDim = null; foreach (string dim in aggDims) { if (IsAggregationDimension(point.Dimension[dim], aggSymbol)) { aggNum++; } else { nextDim = dim; } } if (aggNum == aggDims.Count) { tree.ParentNode = point; } else if (aggNum == aggDims.Count - 1) { if (!tree.ChildrenNodes.ContainsKey(nextDim)) { tree.ChildrenNodes.Add(nextDim, new List <TimeSeriesPoint>()); } tree.ChildrenNodes[nextDim].Add(point); } if (aggNum == 0) { tree.Leaves.Add(point); } }
private Tuple <double, double> GetSurpriseAndExplanatoryScore(TimeSeriesPoint rootCausePoint, TimeSeriesPoint anomalyPoint) { double surprise = GetSurpriseScore(rootCausePoint, anomalyPoint); double ep = anomalyPoint.Value - anomalyPoint.ExpectedValue == 0 ? 0 : Math.Abs((rootCausePoint.Value - rootCausePoint.ExpectedValue) / (anomalyPoint.Value - anomalyPoint.ExpectedValue)); return(new Tuple <double, double>(surprise, ep)); }
private void GetRootCauseDirectionAndScore(Dictionary <Dictionary <string, Object>, TimeSeriesPoint> dimPointMapping, Dictionary <string, Object> anomalyRoot, RootCause dst, double beta, PointTree pointTree, AggregateType aggType, Object aggSymbol) { TimeSeriesPoint anomalyPoint = GetPointByDimension(dimPointMapping, anomalyRoot, pointTree, aggType, aggSymbol); if (dst.Items.Count > 1) { //get surprise value and explanatory power value List <RootCauseScore> scoreList = new List <RootCauseScore>(); foreach (RootCauseItem item in dst.Items) { TimeSeriesPoint rootCausePoint = GetPointByDimension(dimPointMapping, item.Dimension, pointTree, aggType, aggSymbol); if (anomalyPoint != null && rootCausePoint != null) { Tuple <double, double> scores = GetSurpriseAndExplanatoryScore(rootCausePoint, anomalyPoint); scoreList.Add(new RootCauseScore(scores.Item1, scores.Item2)); item.Direction = GetRootCauseDirection(rootCausePoint); } } //get final score for (int i = 0; i < scoreList.Count; i++) { if (aggType.Equals(AggregateType.Max) || aggType.Equals(AggregateType.Min)) { dst.Items[i].Score = 1; } else { dst.Items[i].Score = GetFinalScore(scoreList[i].Surprise, Math.Abs(scoreList[i].ExplanatoryScore), beta); } } } else if (dst.Items.Count == 1) { TimeSeriesPoint rootCausePoint = GetPointByDimension(dimPointMapping, dst.Items[0].Dimension, pointTree, aggType, aggSymbol); if (anomalyPoint != null && rootCausePoint != null) { Tuple <double, double> scores = GetSurpriseAndExplanatoryScore(rootCausePoint, anomalyPoint); if (aggType.Equals(AggregateType.Max) || aggType.Equals(AggregateType.Min)) { dst.Items[0].Score = 1; } else { dst.Items[0].Score = GetFinalScore(scores.Item1, scores.Item2, beta); } dst.Items[0].Direction = GetRootCauseDirection(rootCausePoint); } } }
private AnomalyDirection GetRootCauseDirection(TimeSeriesPoint rootCausePoint) { if (rootCausePoint.ExpectedValue < rootCausePoint.Value) { return(AnomalyDirection.Up); } else if (rootCausePoint.ExpectedValue > rootCausePoint.Value) { return(AnomalyDirection.Down); } else { return(AnomalyDirection.Same); } }
/// <summary> /// Calculate the surprise score according to root cause point and anomaly point /// </summary> /// <param name="rootCausePoint">A point which has been detected as root cause</param> /// <param name="anomalyPoint">The anomaly point</param> /// <remarks> /// <format type="text/markdown"> /// [!include[io](~/../docs/samples/docs/api-reference/time-series-root-cause-surprise-score.md)] /// </format> /// </remarks> /// <returns>Surprise score</returns> private double GetSurpriseScore(TimeSeriesPoint rootCausePoint, TimeSeriesPoint anomalyPoint) { double p; double q; if (anomalyPoint.ExpectedValue == 0) { p = 0; } else { p = rootCausePoint.ExpectedValue / anomalyPoint.ExpectedValue; } if (anomalyPoint.Value == 0) { q = 0; } else { q = rootCausePoint.Value / anomalyPoint.Value; } double surprise = 0; if (p == 0) { surprise = 0.5 * (q * Log2(2 * q / (p + q))); } else if (q == 0) { surprise = 0.5 * (p * Log2(2 * p / (p + q))); } else { surprise = 0.5 * (p * Log2(2 * p / (p + q)) + q * Log2(2 * q / (p + q))); } return(surprise); }
private TimeSeriesPoint GetPointByDimension(Dictionary <Dictionary <string, Object>, TimeSeriesPoint> dimPointMapping, Dictionary <string, Object> dimension, PointTree pointTree, AggregateType aggType, Object aggSymbol) { if (dimPointMapping.ContainsKey(dimension)) { return(dimPointMapping[dimension]); } int count = 0; TimeSeriesPoint p = new TimeSeriesPoint(dimension); DimensionInfo dimensionInfo = SeparateDimension(dimension, aggSymbol); Dictionary <string, Object> subDim = GetSubDim(dimension, dimensionInfo.DetailDims); foreach (TimeSeriesPoint leave in pointTree.Leaves) { if (ContainsAll(leave.Dimension, subDim)) { count++; p.Value = +leave.Value; p.ExpectedValue = +leave.ExpectedValue; p.Delta = +leave.Delta; } } if (aggType.Equals(AggregateType.Avg)) { p.Value = p.Value / count; p.ExpectedValue = p.ExpectedValue / count; p.Delta = p.Delta / count; } if (count > 0) { return(p); } else { return(null); } }
protected List <TimeSeriesPoint> GetTopAnomaly(List <TimeSeriesPoint> anomalyPoints, TimeSeriesPoint root, List <TimeSeriesPoint> totalPoints, string dimKey, bool isLeaveslevel = false) { Dictionary <string, int> pointDistribution = new Dictionary <string, int>(); UpdateDistribution(pointDistribution, totalPoints, dimKey); anomalyPoints = anomalyPoints.OrderBy(x => x.Delta).ToList(); if (root.Delta > 0) { anomalyPoints.Reverse(); } else { anomalyPoints = anomalyPoints.FindAll(x => x.Delta < 0); } if (anomalyPoints.Count == 1) { return(anomalyPoints); } double delta = 0; double preDelta = 0; List <TimeSeriesPoint> causeList = new List <TimeSeriesPoint>(); foreach (TimeSeriesPoint anomaly in anomalyPoints) { if (StopAnomalyComparison(delta, root.Delta, anomaly.Delta, preDelta)) { break; } delta += anomaly.Delta; causeList.Add(anomaly); preDelta = anomaly.Delta; } int pointSize = isLeaveslevel ? pointDistribution.Count : GetTotalNumber(pointDistribution); if (ShouldSeparateAnomaly(delta, root.Delta, pointSize, causeList.Count)) { return(causeList); } return(null); }