private void MergeClusters(int mergedId, int droppedId) { WeightedCentroid mergedCentroid = weightedCentroids[mergedId]; WeightedCentroid droppedCentroid = weightedCentroids[droppedId]; weightedCentroids[mergedId] = WeightedCentroid.Merge(mergedCentroid, droppedCentroid); isDropped[droppedId] = true; }
public virtual double[] Clusterize(int maxIterations, List <int> layersToExport) { int iterationCount = (Descriptors.Length - 1 < maxIterations) ? Descriptors.Length - 1 : maxIterations; List <double> iterationDistances = new List <double>(); List <Centroid[]> results = new List <Centroid[]>(); weightedCentroids = WeightedCentroid.FromDescriptors(Descriptors); Centroids = new Centroid[iterationCount][]; #if VERBOSE Console.WriteLine("Precomputing {0}x{0} = {1} distances.", centroidDistances.Length, centroidDistances.Length * centroidDistances.Length); #endif PrecomputeAllDistances(); #if VERBOSE Console.WriteLine("Running {0} iterations:", iterationCount); #endif // iterations for (int i = 0; i < iterationCount; i++) { #if VERBOSE Console.WriteLine("Iteration {0} / {1}.", i, iterationCount); #endif Tuple <int, int> idPair = FindMinimalDistanceIds(); int mergedId; int droppedId; // merge lighter into heavier if (weightedCentroids[idPair.Item1].Weight >= weightedCentroids[idPair.Item2].Weight) { mergedId = idPair.Item1; droppedId = idPair.Item2; } else { mergedId = idPair.Item2; droppedId = idPair.Item1; } MergeClusters(mergedId, droppedId); ComputeNewDistances(mergedId, droppedId); // export levels if (layersToExport.Contains((iterationCount - 1) - i)) { Centroid[] layerCentroids = ExportLayer(); AssignClosestDescriptors(layerCentroids); //Centroids[(iterationCount - 1) - i] = layerCentroids; results.Add(layerCentroids); } } Centroids = results.ToArray(); return(iterationDistances.ToArray()); }
public static double GetDistance(WeightedCentroid a, WeightedCentroid b) { double l2sqr = Descriptor.GetDistanceSQR(a.Centroid.Mean.Values, b.Centroid.Mean.Values); double weightMultiplied = a.Weight * b.Weight; double weightAdded = a.Weight + b.Weight; double result = l2sqr * weightMultiplied / weightAdded; return(result); }
public static WeightedCentroid[] FromDescriptors(Descriptor[] descriptors) { WeightedCentroid[] result = new WeightedCentroid[descriptors.Length]; for (int i = 0; i < descriptors.Length; i++) { result[i] = new WeightedCentroid(i, descriptors[i]); } return(result); }
public static WeightedCentroid Merge(WeightedCentroid merged, WeightedCentroid dropped) { Centroid mergedCentroid = new Centroid(merged.Centroid.Id); mergedCentroid.Descriptors.AddRange(merged.Centroid.Descriptors); mergedCentroid.Descriptors.AddRange(dropped.Centroid.Descriptors); mergedCentroid.ComputeMean(); WeightedCentroid mergedWeightedCentroid = new WeightedCentroid(mergedCentroid); return(mergedWeightedCentroid); }
private void PrecomputeAllDistances() { Parallel.For(0, centroidDistances.Length, i => { for (int j = 0; j < i; j++) { // precompute distance centroidDistances[i][j] = WeightedCentroid.GetDistance(weightedCentroids[i], weightedCentroids[j]); // precompute minimal distance in a row if (centroidDistances[i][j] < rowMinimalDistances[i]) { rowMinimalDistances[i] = centroidDistances[i][j]; rowMinimalDistanceIds[i] = j; } } }); }
private void ComputeNewDistances(int mergedCentroidId, int droppedCentroidId) { // invalidate dropped data rowMinimalDistances[droppedCentroidId] = double.NaN; rowMinimalDistanceIds[droppedCentroidId] = -1; Parallel.For(0, droppedCentroidId, j => { centroidDistances[droppedCentroidId][j] = double.NaN; }); Parallel.For(droppedCentroidId + 1, centroidDistances.Length, i => { centroidDistances[i][droppedCentroidId] = double.NaN; }); // compute new distances and minimal value in mergedId row rowMinimalDistances[mergedCentroidId] = double.MaxValue; Parallel.For(0, mergedCentroidId, j => { if (!isDropped[j]) { centroidDistances[mergedCentroidId][j] = WeightedCentroid.GetDistance(weightedCentroids[j], weightedCentroids[mergedCentroidId]); // update minimal distances if (centroidDistances[mergedCentroidId][j] < rowMinimalDistances[mergedCentroidId]) { rowMinimalDistances[mergedCentroidId] = centroidDistances[mergedCentroidId][j]; rowMinimalDistanceIds[mergedCentroidId] = j; } } }); if (rowMinimalDistances[mergedCentroidId] == double.MaxValue) { rowMinimalDistances[mergedCentroidId] = double.NaN; } // compute new distances in mergedId column Parallel.For(mergedCentroidId + 1, centroidDistances.Length, i => { if (!isDropped[i]) { centroidDistances[i][mergedCentroidId] = WeightedCentroid.GetDistance(weightedCentroids[i], weightedCentroids[mergedCentroidId]); // update minimal distances if (centroidDistances[i][mergedCentroidId] < rowMinimalDistances[i]) { rowMinimalDistances[i] = centroidDistances[i][mergedCentroidId]; rowMinimalDistanceIds[i] = mergedCentroidId; } } }); // find new minimal value in the row if the value from dropped column was minimal for (int i = droppedCentroidId + 1; i < centroidDistances.Length; i++) { if (!isDropped[i] && rowMinimalDistanceIds[i] == droppedCentroidId) { rowMinimalDistances[i] = double.MaxValue; rowMinimalDistanceIds[i] = -1; for (int j = 0; j < i; j++) { if (!isDropped[j] && centroidDistances[i][j] < rowMinimalDistances[i]) { rowMinimalDistances[i] = centroidDistances[i][j]; rowMinimalDistanceIds[i] = j; } } if (rowMinimalDistances[i] == double.MaxValue) { rowMinimalDistances[i] = double.NaN; } } } }