聚类算法--DBSCAN算法

article/2025/10/2 3:24:46

一、DBSCAN算法

  1. 简介

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

在DBSCAN 算法中有两个重要的参数:Eps 和 MinPtS。Eps 是定义密度时的邻域半径,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. 代码

//定义点类
public class Point {private int x;private int y;private boolean isKey;private boolean isClassed;public boolean isKey() {return isKey;}public void setKey(boolean isKey) {this.isKey = isKey;this.isClassed = true;}public boolean isClassed() {return isClassed;}public void setClassed(boolean isClassed) {this.isClassed = isClassed;}public int getX() {return x;}public void setX(int x) {this.x = x;}public int getY() {return y;}public void setY(int y) {this.y = y;}public Point() {x = 0;y = 0;}public Point(int x, int y) {this.x = x;this.y = y;}public Point(String str) {String[] p = str.split(",");this.x = Integer.parseInt(p[0]);this.y = Integer.parseInt(p[1]);}public String print() {return "(" + this.x + "," + this.y + ")";}
}
//对点进行操作
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;
public class Utility {/*** 测试两个点之间的距离* @param p 点* @param q 点* @return 返回两个点之间的距离*/public static double getDistance(Point p,Point q){int dx=p.getX()-q.getX();int dy=p.getY()-q.getY();double distance=Math.sqrt(dx*dx+dy*dy);return distance;}/*** 检查给定点是不是核心点* @param lst 存放点的链表* @param p 待测试的点* @param e e半径* @param minp 密度阈值* @return*/public static List<Point> isKeyPoint(List<Point> lst,Point p,int e,int minp){int count=0;List<Point> tmpLst=new ArrayList<Point>();for (Point q : lst) {if (getDistance(p, q) <= e) {++count;if (!tmpLst.contains(q)) {tmpLst.add(q);}}}if(count>=minp){p.setKey(true);return tmpLst;}return null;}/*** 设置已经分类点的标志* @param lst*/public static void setListClassed(List<Point> lst){for (Point p : lst) {if (!p.isClassed()) {p.setClassed(true);}}}/*** 如果b中含有a中包含的元素,则把两个集合合并* @param a* @param b* @return a*/public static boolean mergeList(List<Point> a,List<Point> b){boolean merge=false;for (Point value : b) {if (a.contains(value)) {merge = true;break;}}if(merge){for (Point point : b) {if (!a.contains(point)) {a.add(point);}}}return merge;}/*** 读取数据* @return 返回文本中点的集合*/public static List<Point> getPointsList() throws IOException{List<Point> lst=new ArrayList<Point>();//String txtPath="D:"+File.separatorChar+"Points.txt";String txtPath="E:\\myfile\\Points.txt";BufferedReader br=new BufferedReader(new FileReader(txtPath));String str="";while((str = br.readLine()) != null && !str.equals("")){lst.add(new Point(str));}br.close();return lst;}
}
//主要算法import java.io.*;
import java.util.*;public class DBScan {private static List<Point> pointsList = new ArrayList<Point>();// 初始点列表private static List<List<Point>> resultList = new ArrayList<List<Point>>();// 分类结果集private static int e = 2;// e半径//private static int e = 2;// e半径private static int minp = 3;// 密度阈值//private static int minp = 4;// 密度阈值/*** 打印结果**/private static void display() {int index = 1;for (List<Point> lst : resultList) {if (lst.isEmpty()) {continue;}System.out.println("*****第" + index + "个分类*****");for (Point p : lst) {System.out.print(p.print());}System.out.print("\n");index++;}}/*** 调用算法*/private static void applyDbscan() {try {pointsList = Utility.getPointsList();for (Point p : pointsList) {if (!p.isClassed()) {List<Point> tmpLst = new ArrayList<Point>();if ((tmpLst = Utility.isKeyPoint(pointsList, p, e, minp)) != null) {// 为所有聚类完毕的点做标示Utility.setListClassed(tmpLst);resultList.add(tmpLst);}}}} catch (IOException e) {e.printStackTrace();}}/*** 合并结果集* @return*/private static List<List<Point>> getResult() {applyDbscan();// 找到所有直达的聚类int length = resultList.size();for (int i = 0; i < length; ++i) {for (int j = i + 1; j < length; ++j) {if (Utility.mergeList(resultList.get(i), resultList.get(j))) {resultList.get(j).clear();}}}return resultList;}/*** 程序主函数* @param args*/public static void main(String[] args) {getResult();display();}
}

3.运行结果

三、总结

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

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

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

参考

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

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


http://chatgpt.dhexx.cn/article/27VKtOId.shtml

相关文章

DBSCAN聚类算法

DBSCAN是一种基于密度的聚类算法 首先&#xff0c;DBSCAN算法会以任何尚未访问过的任意起始数据点为核心点&#xff0c;并对该核心点进行扩充。这时我们给定一个半径/距离ε&#xff0c;任何和核心点的距离小于ε的点都是它的相邻点。如果核心点附近有足够数量的点&#xff0…

DBSCAN 聚类算法

DBSCAN 聚类算法 DBSCAN 算法是一种基于密度的聚类算法&#xff0c;它能够发现任意形状的类别 (database 2)&#xff0c;而 k k k-means 只能发现凸 (convex) 的形状 (database 1)&#xff0c;同时 DBSCAN 还有很强的抗噪性 (database 3)&#xff0c;在具有噪声的数据中发现任…

基于密度的聚类算法(1)——DBSCAN详解

基于密度的聚类算法&#xff08;1&#xff09;——DBSCAN详解 基于密度的聚类算法&#xff08;2&#xff09;——OPTICS详解 基于密度的聚类算法&#xff08;3&#xff09;——DPC详解 1. DBSCAN简介 DBSCAN&#xff08;Density-Based Spatial Clustering of Applications wit…

聚类算法之DBSCAN

DBSCAN聚类算法 1. DBSCAN算法基本概念 DBSCAN是一种典型的基于密度的聚类算法&#xff0c;基于一组邻域 ( ϵ , M i n P t s ) (\epsilon, MinPts) (ϵ,MinPts)来描述样本集的紧密程度。其中 ϵ \epsilon ϵ描述了某一样本的邻域距离阈值&#xff0c; M i n P t s MinPts Mi…

char型和int型之间的类型转换

char转换为int型数据 通过赋值方式将char类型变量转换为int型变量&#xff0c;变量值为char类型变量的ASCII码值 例如&#xff1a;int a ‘0’ 那么打印a的结果为48&#xff0c;如果想要得到正确的数字&#xff0c;需要减去ASCII码值。 int型转换为char型 char类型和int类…

C++:char转换为int(char to int )

文章目录 1.通过ascii码&#xff1a;2.直接转换&#xff08;更简单&#xff0c;推荐&#xff09; 1.通过ascii码&#xff1a; char a 0; int ia (int)a; /* note that the int cast is not necessary -- int ia a would suffice */ cout<<ia<<endl;结果如下&a…

c++ char[]与int之间的类型转换

char数组转int&#xff0c;int转char数组。 #include <cstdio> #include <iostream> #include <stdlib.h> using namespace std;int main(int argc , char *argv[]){int n 0; char str[110] "1234";//char[]转int n atoi(str);printf("%d…

c/c++,char型数组转化为int类型

char型数组转int类型 这几天遇到需要将int等类型转换并保存在char数组中&#xff0c;同时还需要将char数组转换为int等类型进行显示。 1、int等类型转换并保存在char数组中 int为4字节&#xff0c;char为1字节&#xff0c;由长变短&#xff0c;容易发出截断&#xff0c;数据…

【c++】int与char相互转换

1 通过ASCII码转换 首先先看一下ASCII表 其中数字字符对应的位置为&#xff1a;48 - 57。 需要记住的几个数值&#xff1a; 2 char ->int char 转 int 之前&#xff0c;先将运算式中的每个字符都转换成 ASCII 码值&#xff0c;再进行计算。 char c 0;int i1 c; …

c++中int与char相互转换

一、ASCII 表 了解 int 与 char 相互转换之前&#xff0c;先让我们看一下 ASCII 码表。 其中数字字符对应的位置为&#xff1a;48 - 57。 二、char 转 int char 转 int 之前&#xff0c;先将运算式中的每个字符都转换成 ASCII 码值&#xff0c;再进行计算。 以下代码为例&a…

java中char类型转换成int类型的方法

java中&#xff0c;需要对输入进行一些判断&#xff0c;比如需要输入的是数字&#xff0c;而用户输入了字符&#xff0c;那么就会报错&#xff0c;因此用char或者String类型接收输入的数据就不会报错&#xff0c;但是问题来了&#xff1a;如何让输入的char或者String类型变为数…

C++ char转换为int(char to int )

1.通过ascii码&#xff1a; char a 0; int ia (int)a; /* note that the int cast is not necessary -- int ia a would suffice */ cout<<ia<<endl; 结果如下&#xff1a; 可以看出这种方法得到的其实是char对应的ascii码。 因为ascii码的数字&#xff08;…

char 与 int之间的转换

转载自&#xff1a; 1.首先char与int都分为signed与unsigned类型&#xff0c;默认情况下都是signed类型。 2.从长字节数据类型转换为短字节数据类型&#xff0c;会产生截断&#xff1a; 如从4字节的int类型转换成1个字节的char类型&#xff0c;则取int数据的最低的一个字节&…

派生类的构造函数和析构函数

派生类的构造和析构函数 派生类的构造函数 在定义派生类时&#xff0c;派生类并没有把基类的构造函数和析构函数继承下来。因此&#xff0c;对继承的基类成员初始化的工作要由派生类的构造函数承担&#xff0c;同时基类的析构函数也要被派生类的析构函数来调用 1.派生类构造…

C++构造函数和析构函数总结

构造函数&#xff1a; <1>作用&#xff1a;赋初值,初始化对象的数据成员,由编译器帮我们调用。 <2>特点&#xff1a;①函数名和类名一样。②没有返回值。③支持有参/无参。④可以重载。 <3>调用时机&#xff1a;在类的对象创建时刻,编译器帮我们调用构造函数…

C++基础:构造函数与析构函数

数据成员多为私有的&#xff0c;要对他们进行初始化&#xff0c;必须用一个公有函数来进行。同时这个函数应该在定义对象时自动执行一次。称为构造函数。 构造函数的用途&#xff1a;1&#xff09;创建对象&#xff1b;2&#xff09;初始化对象中的属性&#xff1b;3&#xff…

类的构造函数和析构函数、默认构造函数

前言 程序只能通过成员函数来访问数据成员&#xff0c;因此需要设计合适成员函数&#xff0c;才能成功地将对象初始化。 类构造函数专门用于构造新对象&#xff0c;将值赋给他们的数据成员&#xff0c;进行初始化。 构造函数名称与类名相同&#xff0c;没有返回值&#xff0…

c++中的构造函数和析构函数

类和对象中&#xff0c;包括构造函数和析构函数&#xff0c;比较重要&#xff0c;通过学习总结一下&#xff0c;以便以后可以回顾&#xff01; 目录 构造函数 1.默认构造函数 2.有参构造函数 3.委托构造函数 4.复制(拷贝)构造函数 5.移动构造函数 左值引用与右值引用 析…

C++类构造函数和析构函数

11.3 类构造函数和析构函数 构造函数&#xff1a;是为了在定义对象时自动初始化其成员变量的值。 构造函数没有返回值&#xff0c;也没有被声明为void类型&#xff1b;因此&#xff0c;构造函数没有声明类型。 11.3.1 声明和定义一个构造函数 构造函数原型&#xff1a;在这…

C++ 构造函数和析构函数可以是虚函数嘛?

简单总结就是&#xff1a;构造函数不可以是虚函数&#xff0c;而析构函数可以且常常是虚函数。 构造函数不能是虚函数 1. 从vptr角度解释 虚函数的调用是通过虚函数表来查找的&#xff0c;而虚函数表由类的实例化对象的vptr指针(vptr可以参考C的虚函数表指针vptr)指向&#x…