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
};
}
}
}