Dbscan.cs 3.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. using System.Collections.Generic;
  2. using System.Linq;
  3. namespace Dbscan
  4. {
  5. /// <summary>
  6. /// Contains static methods to run the DBSCAN algorithm.
  7. /// </summary>
  8. public static class Dbscan
  9. {
  10. /// <summary>
  11. /// Run the DBSCAN algorithm on a collection of points, using the default index
  12. /// (<see cref="T:Dbscan.ListSpatialIndex`1" />).
  13. /// </summary>
  14. /// <typeparam name="T">The type of elements to cluster.</typeparam>
  15. /// <param name="data">The collection of elements to cluster.</param>
  16. /// <param name="epsilon">The epsilon parameter to use in the algorithm; used to determine the radius of the circle to find neighboring points.</param>
  17. /// <param name="minimumPointsPerCluster">The minimum number of points required to create a cluster or to add additional points to the cluster.</param>
  18. /// <returns>A <see cref="T:Dbscan.ClusterSet`1" /> containing the list of <see cref="T:Dbscan.Cluster`1" />s and a list of unclustered points.</returns>
  19. /// <remarks>This method is an O(N^2) operation, where N is the Length of the dataset</remarks>
  20. public static ClusterSet<T> CalculateClusters<T>(IEnumerable<T> data, double epsilon, int minimumPointsPerCluster) where T : IPointData
  21. {
  22. return CalculateClusters(new ListSpatialIndex<PointInfo<T>>(data.Select((T p) => new PointInfo<T>(p)).ToList()), epsilon, minimumPointsPerCluster);
  23. }
  24. /// <summary>
  25. /// Run the DBSCAN algorithm on a collection of points, the specified pre-filled <see cref="T:Dbscan.ISpatialIndex`1" />.
  26. /// </summary>
  27. /// <typeparam name="T">The type of elements to cluster.</typeparam>
  28. /// <param name="index">The collection of elements to cluster.</param>
  29. /// <param name="epsilon">The epsilon parameter to use in the algorithm; used to determine the radius of the circle to find neighboring points.</param>
  30. /// <param name="minimumPointsPerCluster">The minimum number of points required to create a cluster or to add additional points to the cluster.</param>
  31. /// <returns>A <see cref="T:Dbscan.ClusterSet`1" /> containing the list of <see cref="T:Dbscan.Cluster`1" />s and a list of unclustered points.</returns>
  32. public static ClusterSet<T> CalculateClusters<T>(ISpatialIndex<PointInfo<T>> index, double epsilon, int minimumPointsPerCluster) where T : IPointData
  33. {
  34. List<PointInfo<T>> list = index.Search().ToList();
  35. List<Cluster<T>> list2 = new List<Cluster<T>>();
  36. foreach (PointInfo<T> item in list)
  37. {
  38. if (!item.Visited)
  39. {
  40. item.Visited = true;
  41. IPointData p2 = item;
  42. IReadOnlyList<PointInfo<T>> readOnlyList = index.Search(in p2, epsilon);
  43. if (readOnlyList.Count >= minimumPointsPerCluster)
  44. {
  45. list2.Add(BuildCluster(index, item, readOnlyList, epsilon, minimumPointsPerCluster));
  46. }
  47. }
  48. }
  49. return new ClusterSet<T>
  50. {
  51. Clusters = list2,
  52. UnclusteredObjects = (from p in list
  53. where !p.Clustered
  54. select p.Item).ToList()
  55. };
  56. }
  57. private static Cluster<T> BuildCluster<T>(ISpatialIndex<PointInfo<T>> index, PointInfo<T> point, IReadOnlyList<PointInfo<T>> neighborhood, double epsilon, int minimumPointsPerCluster) where T : IPointData
  58. {
  59. List<T> list = new List<T> { point.Item };
  60. point.Clustered = true;
  61. Queue<PointInfo<T>> queue = new Queue<PointInfo<T>>(neighborhood);
  62. while (queue.Any())
  63. {
  64. PointInfo<T> pointInfo = queue.Dequeue();
  65. if (!pointInfo.Visited)
  66. {
  67. pointInfo.Visited = true;
  68. IPointData p = pointInfo;
  69. IReadOnlyList<PointInfo<T>> readOnlyList = index.Search(in p, epsilon);
  70. if (readOnlyList.Count >= minimumPointsPerCluster)
  71. {
  72. foreach (PointInfo<T> item in readOnlyList)
  73. {
  74. queue.Enqueue(item);
  75. }
  76. }
  77. }
  78. if (!pointInfo.Clustered)
  79. {
  80. pointInfo.Clustered = true;
  81. list.Add(pointInfo.Item);
  82. }
  83. }
  84. return new Cluster<T>
  85. {
  86. Objects = list
  87. };
  88. }
  89. }
  90. }