public override bool Equals(object obj) { if (this == obj) { return(true); } if (obj == null) { return(false); } if (GetType() != obj.GetType()) { return(false); } AveragedAnomalyRecordList other = (AveragedAnomalyRecordList)obj; if (AveragedRecords == null) { if (other.AveragedRecords != null) { return(false); } } else if (!AveragedRecords.Equals(other.AveragedRecords)) { return(false); } if (HistoricalValues == null) { if (other.HistoricalValues != null) { return(false); } } else if (!HistoricalValues.Equals(other.HistoricalValues)) { return(false); } if (BitConverter.DoubleToInt64Bits(Total) != BitConverter.DoubleToInt64Bits(other.Total)) { return(false); } return(true); }
/** * Given a series of anomaly scores, compute the likelihood for each score. This * function should be called once on a bunch of historical anomaly scores for an * initial estimate of the distribution. It should be called again every so often * (say every 50 records) to update the estimate. * * @param anomalyScores * @param averagingWindow * @param skipRecords * @return */ public AnomalyLikelihoodMetrics EstimateAnomalyLikelihoods(List <Sample> anomalyScores, int averagingWindow, int skipRecords) { if (anomalyScores.Count == 0) { throw new ArgumentException("Must have at least one anomaly score."); } // Compute averaged anomaly scores AveragedAnomalyRecordList records = AnomalyScoreMovingAverage(anomalyScores, averagingWindow); // Estimate the distribution of anomaly scores based on aggregated records Statistic distribution; if (records.AveragedRecords.Count <= skipRecords) { distribution = NullDistribution(); } else { List <double> samples = records.GetMetrics(); distribution = EstimateNormal(samples.Skip(skipRecords).Take(samples.Count).ToArray(), true); /* Taken from the Python Documentation * # HACK ALERT! The CLA model currently does not handle constant metric values # very well (time of day encoder changes sometimes lead to unstable SDR's # even though the metric is constant). Until this is resolved, we explicitly # detect and handle completely flat metric values by reporting them as not # anomalous. # */ samples = records.GetSamples(); Statistic metricDistribution = EstimateNormal(samples.Skip(skipRecords).Take(samples.Count).ToArray(), false); if (metricDistribution.variance < 1.5e-5) { distribution = NullDistribution(); } } // Estimate likelihoods based on this distribution int i = 0; double[] likelihoods = new double[records.AveragedRecords.Count]; foreach (Sample sample in records.AveragedRecords) { likelihoods[i++] = NormalProbability(sample.score, distribution); } // Filter likelihood values double[] filteredLikelihoods = FilterLikelihoods(likelihoods); int len = likelihoods.Length; Parameters anomalyParameters = Parameters.Empty(); anomalyParameters.SetParameterByKey(Parameters.KEY.ANOMALY_KEY_DIST, distribution); anomalyParameters.SetParameterByKey(Parameters.KEY.ANOMALY_KEY_MVG_AVG, new MovingAverage(records.HistoricalValues, records.Total, averagingWindow)); anomalyParameters.SetParameterByKey(Parameters.KEY.ANOMALY_KEY_HIST_LIKE, len > 0 ? Arrays.CopyOfRange(likelihoods, len - Math.Min(averagingWindow, len), len) : new double[0]); AnomalyParams @params = new AnomalyParams(anomalyParameters); //AnomalyParams @params = new AnomalyParams( // new string[] { "distribution", "movingAverage", "historicalLikelihoods" }, // distribution, // new MovingAverage(records.historicalValues, records.total, averagingWindow), // len > 0 ? // Arrays.CopyOfRange(likelihoods, len - Math.Min(averagingWindow, len), len) : // new double[0]); if (LOG.IsDebugEnabled) { LOG.Debug(string.Format("Discovered params={0} Number of likelihoods:{1} First 20 likelihoods:{2}", @params, len, Arrays.CopyOfRange(filteredLikelihoods, 0, 20))); } return(new AnomalyLikelihoodMetrics(filteredLikelihoods, records, @params)); }