当前位置:   article > 正文

聚类算法--DBSCAN算法_dbscan聚类算法

dbscan聚类算法

一、DBSCAN算法

  1. 简介

DBSCAN(Density-Based Spatial Clustering of Applications with Noise)是一个基于密度的聚类算法。算法把簇看作数据空间中由低密度区域分割开的高密度对象区域;将足够高密度的区域划为簇,可以在有噪音的数据集中发现任意形状的聚类。
  1. 基本概念

在DBSCAN 算法中有两个重要的参数:Eps 和 MinPtSEps 是定义密度时的邻域半径,MinPts 为定义核心点时的阈值。

基于中心定义密度的方法可将点分为三类:

(1)核心点:给定用户指定阈值MinPts,如果一个点的给定邻域内的点的数目超过给定阈值MinPts, 那么该点称为核心点。

(2)边界点:边界点不是核心点,但它落在某个核心点的Eps邻域内。

(3)噪声点:噪声点既不是核心点,也不是边界点。

基于密度的簇定义如下:

(1)密度直达:给定一个对象集合D,如果p是在q的邻域内,且q是一个核心对象,则称p从对象q出发是直接密度直达的。

(2)密度可达:如果存在一个对象链 , ,对于

是从关于Eps和MinPts直接密度可达的,则对象p是从对象q关于Eps和MinPts密度可达的。

(3)密度连接:如果对象集D中存在一个对象o,使得对象p和q是从o关于Eps和MinPts密度可达的,那么对象p和q是关于Eps和MinPts密度连接的。

(4)密度可达是密度直达的传递闭包,这种关系非对称的,只有核心对象之间是相互密度直达的。

  1. 算法过程

通俗的来讲,算法流程如下:

(1)将所有数据对象标记为核心对象、边界对象或噪声对象。

(2)删除噪声对象。

(3)为距离在Eps之内的所有核心对象之间赋予一条边。

(4)每组联通的核心对象形成一个簇。

(5)将每个边界对象指派到一个与之关联的核心对象的簇中。

二、DBSCAN算法举例

  1. 案例说明和数据

给定一组二维数据(x,y)作为点,可以自己定义也可以利用下面的数据,将数据存入文本文档中,放入相应目录下即可。Eps设为2,MinPts为3。使用DBSCAN算法进行分类操作。

0,0
0,1
0,2
0,3
0,4
0,5
12,1
12,2
12,3
12,4
12,5
12,6
0,6
0,7
12,7
0,8
0,9
1,1
6,8
8,7
6,7
3,5
  1. 代码

  1. //定义点类
  2. public class Point {
  3. private int x;
  4. private int y;
  5. private boolean isKey;
  6. private boolean isClassed;
  7. public boolean isKey() {
  8. return isKey;
  9. }
  10. public void setKey(boolean isKey) {
  11. this.isKey = isKey;
  12. this.isClassed = true;
  13. }
  14. public boolean isClassed() {
  15. return isClassed;
  16. }
  17. public void setClassed(boolean isClassed) {
  18. this.isClassed = isClassed;
  19. }
  20. public int getX() {
  21. return x;
  22. }
  23. public void setX(int x) {
  24. this.x = x;
  25. }
  26. public int getY() {
  27. return y;
  28. }
  29. public void setY(int y) {
  30. this.y = y;
  31. }
  32. public Point() {
  33. x = 0;
  34. y = 0;
  35. }
  36. public Point(int x, int y) {
  37. this.x = x;
  38. this.y = y;
  39. }
  40. public Point(String str) {
  41. String[] p = str.split(",");
  42. this.x = Integer.parseInt(p[0]);
  43. this.y = Integer.parseInt(p[1]);
  44. }
  45. public String print() {
  46. return "(" + this.x + "," + this.y + ")";
  47. }
  48. }
  1. //对点进行操作
  2. import java.io.BufferedReader;
  3. import java.io.FileReader;
  4. import java.io.IOException;
  5. import java.util.*;
  6. public class Utility {
  7. /**
  8.   * 测试两个点之间的距离
  9.   * @param p 点
  10.   * @param q 点
  11.   * @return 返回两个点之间的距离
  12.   */
  13. public static double getDistance(Point p,Point q){
  14. int dx=p.getX()-q.getX();
  15. int dy=p.getY()-q.getY();
  16. double distance=Math.sqrt(dx*dx+dy*dy);
  17. return distance;
  18. }
  19. /**
  20.   * 检查给定点是不是核心点
  21.   * @param lst 存放点的链表
  22.   * @param p 待测试的点
  23.   * @param e e半径
  24.   * @param minp 密度阈值
  25.   * @return
  26.   */
  27. public static List<Point> isKeyPoint(List<Point> lst,Point p,int e,int minp){
  28. int count=0;
  29. List<Point> tmpLst=new ArrayList<Point>();
  30. for (Point q : lst) {
  31. if (getDistance(p, q) <= e) {
  32. ++count;
  33. if (!tmpLst.contains(q)) {
  34. tmpLst.add(q);
  35. }
  36. }
  37. }
  38. if(count>=minp){
  39. p.setKey(true);
  40. return tmpLst;
  41. }
  42. return null;
  43. }
  44. /**
  45. * 设置已经分类点的标志
  46. * @param lst
  47. */
  48. public static void setListClassed(List<Point> lst){
  49. for (Point p : lst) {
  50. if (!p.isClassed()) {
  51. p.setClassed(true);
  52. }
  53. }
  54. }
  55. /**
  56. * 如果b中含有a中包含的元素,则把两个集合合并
  57. * @param a
  58. * @param b
  59. * @return a
  60. */
  61. public static boolean mergeList(List<Point> a,List<Point> b){
  62. boolean merge=false;
  63. for (Point value : b) {
  64. if (a.contains(value)) {
  65. merge = true;
  66. break;
  67. }
  68. }
  69. if(merge){
  70. for (Point point : b) {
  71. if (!a.contains(point)) {
  72. a.add(point);
  73. }
  74. }
  75. }
  76. return merge;
  77. }
  78. /**
  79. * 读取数据
  80. * @return 返回文本中点的集合
  81. */
  82. public static List<Point> getPointsList() throws IOException{
  83. List<Point> lst=new ArrayList<Point>();
  84. //String txtPath="D:"+File.separatorChar+"Points.txt";
  85. String txtPath="E:\\myfile\\Points.txt";
  86. BufferedReader br=new BufferedReader(new FileReader(txtPath));
  87. String str="";
  88. while((str = br.readLine()) != null && !str.equals("")){
  89. lst.add(new Point(str));
  90. }
  91. br.close();
  92. return lst;
  93. }
  94. }
  1. //主要算法
  2. import java.io.*;
  3. import java.util.*;
  4. public class DBScan {
  5. private static List<Point> pointsList = new ArrayList<Point>();// 初始点列表
  6. private static List<List<Point>> resultList = new ArrayList<List<Point>>();// 分类结果集
  7. private static int e = 2;// e半径
  8. //private static int e = 2;// e半径
  9. private static int minp = 3;// 密度阈值
  10. //private static int minp = 4;// 密度阈值
  11. /**
  12. * 打印结果
  13. **/
  14. private static void display() {
  15. int index = 1;
  16. for (List<Point> lst : resultList) {
  17. if (lst.isEmpty()) {
  18. continue;
  19. }
  20. System.out.println("*****第" + index + "个分类*****");
  21. for (Point p : lst) {
  22. System.out.print(p.print());
  23. }
  24. System.out.print("\n");
  25. index++;
  26. }
  27. }
  28. /**
  29. * 调用算法
  30. */
  31. private static void applyDbscan() {
  32. try {
  33. pointsList = Utility.getPointsList();
  34. for (Point p : pointsList) {
  35. if (!p.isClassed()) {
  36. List<Point> tmpLst = new ArrayList<Point>();
  37. if ((tmpLst = Utility.isKeyPoint(pointsList, p, e, minp)) != null) {
  38. // 为所有聚类完毕的点做标示
  39. Utility.setListClassed(tmpLst);
  40. resultList.add(tmpLst);
  41. }
  42. }
  43. }
  44. } catch (IOException e) {
  45. e.printStackTrace();
  46. }
  47. }
  48. /**
  49. * 合并结果集
  50. * @return
  51. */
  52. private static List<List<Point>> getResult() {
  53. applyDbscan();// 找到所有直达的聚类
  54. int length = resultList.size();
  55. for (int i = 0; i < length; ++i) {
  56. for (int j = i + 1; j < length; ++j) {
  57. if (Utility.mergeList(resultList.get(i), resultList.get(j))) {
  58. resultList.get(j).clear();
  59. }
  60. }
  61. }
  62. return resultList;
  63. }
  64. /**
  65. * 程序主函数
  66. * @param args
  67. */
  68. public static void main(String[] args) {
  69. getResult();
  70. display();
  71. }
  72. }

3.运行结果

三、总结

DBSCAN算法可以对任意形状的稠密数据集进行聚类,相对于K-Means、Mean Shift之类的聚类算法一般只适用于凸数据集。除此之外该算法在聚类的同时发现异常点,对数据集中的异常点不敏感。

DBSCAN算法也存着缺点,如果样本集的密度不均匀、聚类间距差相差很大时,聚类质量比较差;样本集较大时,聚类收敛时间较长;以及对Eps和MinPts的联合调参是比较困难的。

在日常生活中,我们可以根据数据的类型进行合理选择该算法进行聚类分类。

参考

https://blog.csdn.net/qq_42735631/article/details/120983729

https://zhuanlan.zhihu.com/p/139926467

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/519969
推荐阅读
  

闽ICP备14008679号