using System.Collections.Generic; using System.Linq; namespace Dbscan { /// /// Contains static methods to run the DBSCAN algorithm. /// public static class Dbscan { /// /// Run the DBSCAN algorithm on a collection of points, using the default index /// (). /// /// The type of elements to cluster. /// The collection of elements to cluster. /// The epsilon parameter to use in the algorithm; used to determine the radius of the circle to find neighboring points. /// The minimum number of points required to create a cluster or to add additional points to the cluster. /// A containing the list of s and a list of unclustered points. /// This method is an O(N^2) operation, where N is the Length of the dataset public static ClusterSet CalculateClusters(IEnumerable data, double epsilon, int minimumPointsPerCluster) where T : IPointData { return CalculateClusters(new ListSpatialIndex>(data.Select((T p) => new PointInfo(p)).ToList()), epsilon, minimumPointsPerCluster); } /// /// Run the DBSCAN algorithm on a collection of points, the specified pre-filled . /// /// The type of elements to cluster. /// The collection of elements to cluster. /// The epsilon parameter to use in the algorithm; used to determine the radius of the circle to find neighboring points. /// The minimum number of points required to create a cluster or to add additional points to the cluster. /// A containing the list of s and a list of unclustered points. public static ClusterSet CalculateClusters(ISpatialIndex> index, double epsilon, int minimumPointsPerCluster) where T : IPointData { List> list = index.Search().ToList(); List> list2 = new List>(); foreach (PointInfo item in list) { if (!item.Visited) { item.Visited = true; IPointData p2 = item; IReadOnlyList> readOnlyList = index.Search(in p2, epsilon); if (readOnlyList.Count >= minimumPointsPerCluster) { list2.Add(BuildCluster(index, item, readOnlyList, epsilon, minimumPointsPerCluster)); } } } return new ClusterSet { Clusters = list2, UnclusteredObjects = (from p in list where !p.Clustered select p.Item).ToList() }; } private static Cluster BuildCluster(ISpatialIndex> index, PointInfo point, IReadOnlyList> neighborhood, double epsilon, int minimumPointsPerCluster) where T : IPointData { List list = new List { point.Item }; point.Clustered = true; Queue> queue = new Queue>(neighborhood); while (queue.Any()) { PointInfo pointInfo = queue.Dequeue(); if (!pointInfo.Visited) { pointInfo.Visited = true; IPointData p = pointInfo; IReadOnlyList> readOnlyList = index.Search(in p, epsilon); if (readOnlyList.Count >= minimumPointsPerCluster) { foreach (PointInfo item in readOnlyList) { queue.Enqueue(item); } } } if (!pointInfo.Clustered) { pointInfo.Clustered = true; list.Add(pointInfo.Item); } } return new Cluster { Objects = list }; } } }