/// <summary> /// CalculateDistance is a function that claculates the distance between two elements of same size. /// </summary> /// <param name="FirstElement">first element</param> /// <param name="SecondElement">second element</param> /// <returns> /// - Item 1: distance between the two elements /// - Item 2: AnomalyDetectionResponse: a code and a message that state whether the function succeeded or encountered an error /// when the function succeeds, it will return: /// - Code: 0, "OK"</returns> internal static Tuple <double, AnomalyDetectionResponse> CalculateDistance(double[] FirstElement, double[] SecondElement) { AnomalyDetectionResponse ADResponse; try { if (FirstElement == null || SecondElement == null) { ADResponse = new AnomalyDetectionResponse(101, "Function <CalculateDistance>: At least one input is null"); return(Tuple.Create(-1.0, ADResponse)); } if (FirstElement.Length != SecondElement.Length) { ADResponse = new AnomalyDetectionResponse(115, "Function <CalculateDistance>: Inputs have different dimensions"); return(Tuple.Create(-1.0, ADResponse)); } double SquaredDistance = 0; for (int i = 0; i < FirstElement.Length; i++) { SquaredDistance += Math.Pow(FirstElement[i] - SecondElement[i], 2); } ADResponse = new AnomalyDetectionResponse(0, "OK"); return(Tuple.Create(Math.Sqrt(SquaredDistance), ADResponse)); } catch (Exception Ex) { ADResponse = new AnomalyDetectionResponse(400, "Function <CalculateDistance>: Unhandled exception:\t" + Ex.ToString()); return(Tuple.Create(-1.0, ADResponse)); } }
/// <summary> /// CalculateNearestCluster is a function that determines the nearest cluster and calculates the distance between those two clusters. /// </summary> /// <param name="Centroids">the centroids of the clusters</param> /// <param name="SamplesInClusters">number of samples in each cluster</param> /// <returns>Tuple of three Items: /// - Item 1: contains the number of nearest cluster /// - Item 2: contains the distance to the nearest cluster /// - Item 3: AnomalyDetectionResponse: a code and a message that state whether the function succeeded or encountered an error /// when the function succeeds, it will return: /// - Code: 0, "OK"</returns> private static Tuple <int[], double[], AnomalyDetectionResponse> CalculateNearestCluster(double[][] Centroids, int[] SamplesInClusters) { AnomalyDetectionResponse ADResponse; int[] NearestClustersArray = new int[Centroids.Length]; double[] DistanceToNearestClusterArray = new double[Centroids.Length]; try { Tuple <double, AnomalyDetectionResponse> CDResponse; for (int i = 0; i < Centroids.Length; i++) { //in case of empty cluster if (SamplesInClusters[i] == 0) { NearestClustersArray[i] = -1; DistanceToNearestClusterArray[i] = -1; continue; } DistanceToNearestClusterArray[i] = double.MaxValue; for (int j = 0; j < Centroids.Length; j++) { if (i == j || SamplesInClusters[j] == 0) { continue; } CDResponse = CalculateDistance(Centroids[i], Centroids[j]); if (CDResponse.Item2.Code != 0) { NearestClustersArray = null; DistanceToNearestClusterArray = null; return(Tuple.Create(NearestClustersArray, DistanceToNearestClusterArray, CDResponse.Item2)); } if (CDResponse.Item1 < DistanceToNearestClusterArray[i]) { DistanceToNearestClusterArray[i] = CDResponse.Item1; NearestClustersArray[i] = j; } } } ADResponse = new AnomalyDetectionResponse(0, "OK"); return(Tuple.Create(NearestClustersArray, DistanceToNearestClusterArray, ADResponse)); } catch (Exception Ex) { NearestClustersArray = null; DistanceToNearestClusterArray = null; ADResponse = new AnomalyDetectionResponse(400, "Function <CalculateNearestCluster>: Unhandled exception:\t" + Ex.ToString()); return(Tuple.Create(NearestClustersArray, DistanceToNearestClusterArray, ADResponse)); } }
/// <summary> /// SamplesInCLusterNumber is a function that counts the number of samples of each cluster. /// </summary> /// <param name="RawData">data to be clustered</param> /// <param name="DataToClusterMapping">contains the assigned cluster number for each sample of the RawData</param> /// <param name="NumberOfClusters">the number of clusters</param> /// <returns>Tuple of two Items: /// - Item 1: contains the number of samples for each cluster /// - Item 2: AnomalyDetectionResponse: a code and a message that state whether the function succeeded or encountered an error /// when the function succeeds, it will return: /// - Code: 0, "OK"</returns> private static Tuple <int[], AnomalyDetectionResponse> SamplesInClusterNumber(double[][] RawData, int[] DataToClusterMapping, int NumberOfClusters) { AnomalyDetectionResponse ADResponse; int[] ClusterSamplesCounter; try { ClusterSamplesCounter = new int[NumberOfClusters]; for (int i = 0; i < DataToClusterMapping.Length; i++) { ClusterSamplesCounter[DataToClusterMapping[i]]++; } ADResponse = new AnomalyDetectionResponse(0, "OK"); return(Tuple.Create(ClusterSamplesCounter, ADResponse)); } catch (Exception Ex) { ClusterSamplesCounter = null; ADResponse = new AnomalyDetectionResponse(400, "Function <SamplesInClusterNumber>: Unhandled exception:\t" + Ex.ToString()); return(Tuple.Create(ClusterSamplesCounter, ADResponse)); } }
/// <summary> /// CreateClusteringResult is a function that generates the clustering results using several subfunctions. /// </summary> /// <param name="RawData">data to be clustered</param> /// <param name="DataToClusterMapping">contains the assigned cluster number for each sample of the RawData</param> /// <param name="Centroids">the centroids of the clusters</param> /// <param name="NumberOfClusters">the number of clusters</param> /// <returns>Tuple of two Items: /// - Item 1: contains the results for each cluster /// - Item 2: AnomalyDetectionResponse: a code and a message that state whether the function succeeded or encountered an error /// when the function succeeds, it will return: /// - Code: 0, "OK"</returns> public static Tuple <ClusteringResults[], AnomalyDetectionResponse> CreateClusteringResult(double[][] RawData, int[] DataToClusterMapping, double[][] Centroids, int NumberOfClusters) { ClusteringResults[] Results; AnomalyDetectionResponse ADResponse; try { //get how many samples are there in each cluster Tuple <int[], AnomalyDetectionResponse> SICNResponse = SamplesInClusterNumber(RawData, DataToClusterMapping, NumberOfClusters); if (SICNResponse.Item2.Code != 0) { Results = null; return(Tuple.Create(Results, SICNResponse.Item2)); } int[] ClusterSamplesCounter = SICNResponse.Item1; Results = new ClusteringResults[NumberOfClusters]; for (int i = 0; i < NumberOfClusters; i++) { Results[i] = new ClusteringResults(); Results[i].Centroid = Centroids[i]; Results[i].ClusterData = new double[ClusterSamplesCounter[i]][]; Results[i].ClusterDataOriginalIndex = new int[ClusterSamplesCounter[i]]; //group the samples of the cluster ADResponse = Results[i].AssignSamplesToClusters(RawData, DataToClusterMapping, i); if (ADResponse.Code != 0) { Results = null; return(Tuple.Create(Results, ADResponse)); } ADResponse = Results[i].CalculateStatistics(); if (ADResponse.Code != 0) { Results = null; return(Tuple.Create(Results, ADResponse)); } } //use functions to calculate the properties and statistics of each clusters. Tuple <int[], double[], AnomalyDetectionResponse> CNCResponse = CalculateNearestCluster(Centroids, ClusterSamplesCounter); if (CNCResponse.Item3.Code != 0) { Results = null; return(Tuple.Create(Results, CNCResponse.Item3)); } for (int i = 0; i < NumberOfClusters; i++) { Results[i].NearestCluster = CNCResponse.Item1[i]; Results[i].DistanceToNearestClusterCentroid = CNCResponse.Item2[i]; } double[][] NearestForeignSampleInNearestClusterArray; double[] DistanceToNearestForeignSampleInNearestClusterArray; double[][] NearestForeignSampleArray; double[] DistanceToNearestForeignSampleArray; int[] ClusterOfNearestForeignSampleArray; ADResponse = CalculateMoreStatistics(RawData, DataToClusterMapping, Centroids, CNCResponse.Item1, out NearestForeignSampleInNearestClusterArray, out DistanceToNearestForeignSampleInNearestClusterArray, out NearestForeignSampleArray, out DistanceToNearestForeignSampleArray, out ClusterOfNearestForeignSampleArray); if (ADResponse.Code != 0) { Results = null; return(Tuple.Create(Results, ADResponse)); } for (int i = 0; i < NumberOfClusters; i++) { Results[i].NearestForeignSampleInNearestCluster = NearestForeignSampleInNearestClusterArray[i]; Results[i].DistanceToNearestForeignSampleInNearestCluster = DistanceToNearestForeignSampleInNearestClusterArray[i]; Results[i].NearestForeignSample = NearestForeignSampleArray[i]; Results[i].DistanceToNearestForeignSample = DistanceToNearestForeignSampleArray[i]; Results[i].ClusterOfNearestForeignSample = ClusterOfNearestForeignSampleArray[i]; } ADResponse = new AnomalyDetectionResponse(0, "OK"); return(Tuple.Create(Results, ADResponse)); } catch (Exception Ex) { Results = null; ADResponse = new AnomalyDetectionResponse(400, "Function <CreateClusteringResult>: Unhandled exception:\t" + Ex.ToString()); return(Tuple.Create(Results, ADResponse)); } }