聚类算法之DBSCAN

article/2025/10/2 3:22:00

DBSCAN聚类算法

1. DBSCAN算法基本概念

DBSCAN是一种典型的基于密度的聚类算法,基于一组邻域 ( ϵ , M i n P t s ) (\epsilon, MinPts) (ϵ,MinPts)来描述样本集的紧密程度。其中 ϵ \epsilon ϵ描述了某一样本的邻域距离阈值, M i n P t s MinPts MinPts描述了某一样本的距离为 ϵ \epsilon ϵ的邻域中样本个数的阈值。

DBSCAN算法中将数据点分为以下三类:

  • 核心点:若样本 x i x_i xi ϵ \epsilon ϵ邻域内至少包含 M i n P t s MinPts MinPts样本,即 ∣ N ϵ ( x i ) ∣ ≥ M i n P t s |N_\epsilon(x_i)| \geq MinPts Nϵ(xi)MinPts,则称样本点 x i x_i xi为核心点
  • 边界点:若样本点 x i x_i xi ϵ \epsilon ϵ邻域内包含的样本数目小于 M i n P t s MinPts MinPts,但是它在其他核心点的邻域内,则称样本点 x i x_i xi为边界点
  • 噪音点:既不是核心点也不是边界点的点

DBSCAN算法中还定义了如下概念:

  • 密度直达:若样本点 x j x_j xj在核心点 x i x_i xi ϵ \epsilon ϵ邻域内,则称样本点 x j x_j xj x i x_i xi密度直达。
  • 密度可达:若在样本点 x i , 1 x_{i,1} xi,1和样本点 x i , n x_{i,n} xi,n之间存在序列 x i , 2 , . . . , x i , n − 1 x_{i,2},...,x_{i,n-1} xi,2,...,xi,n1,且 x i , j + 1 x_{i,j+1} xi,j+1 x i , j x_{i,j} xi,j密度直达,则称 x i , n x_{i,n} xi,n x i , 1 x_{i,1} xi,1密度可达。由密度直达的定义可知,样本点 x i , 1 , x i , 2 , . . . , x i , n − 1 x_{i,1},x_{i,2},...,x_{i,n-1} xi,1,xi,2,...,xi,n1均为核心点
  • 密度连接:对于样本点 x i x_i xi和样本点 x j x_j xj,若存在样本点 x k x_k xk,使得 x i x_i xi x j x_j xj都由 x k x_k xk密度可达,则称 x i x_i xi x j x_j xj密度相连

在这里插入图片描述

上图 M i n P t s = 5 MinPts=5 MinPts=5,红色的样本都是核心点,因为其 ϵ \epsilon ϵ邻域至少有5个样本。黑色的样本是非核心点,其中红色样本邻域内的黑色样本为边界点,其他黑色样本为噪音点。所有核心点密度直达的样本在以红色样本为中心的超球体内,如果不在超球体内,则不能密度直达。图中用绿色箭头连起来的核心点组成了密度可达的样本序列。在这些密度可达的样本序列的 ϵ \epsilon ϵ邻域内所有的样本相互都是密度相连的。

2. DBSCAN聚类算法流程

输 入 : 样 本 集 D = { x 1 , x 2 , . . . , x n } , 邻 域 参 数 ( ϵ , M i n P t s ) , 样 本 距 离 度 量 方 式 输入:样本集D=\{x_1,x_2,...,x_n\},邻域参数(\epsilon,MinPts),样本距离度量方式 D={x1,x2,...,xn}(ϵ,MinPts)

输 出 : 簇 划 分 C = { C 1 , C 2 , . . . , C k } 输出:簇划分C=\{C_1,C_2,...,C_k\} C={C1,C2,...,Ck}

  1. 初始化核心点集合 Ω = ∅ \Omega=\varnothing Ω=,初始化聚类簇数 k = 0 k=0 k=0,初始化为访问集合 Γ = D \Gamma=D Γ=D,簇划分 C = ∅ C=\varnothing C=

  2. 对于 i = 1 , 2 , . . . , n i=1,2,...,n i=1,2,...,n,按下面步骤找出所有的核心点:

    • 通过距离度量方式,找到样本 x i x_i xi ϵ \epsilon ϵ邻域子样本集 N ϵ ( x i ) N_\epsilon(x_i) Nϵ(xi)
    • 如果子样本集样本个数满足 ∣ N ϵ ( x i ) ∣ ≥ M i n P t s |N_\epsilon(x_i)| \geq MinPts Nϵ(xi)MinPts,将样本 x i x_i xi加入核心点集合: Ω = Ω ∪ { x i } \Omega=\Omega \cup \{x_i\} Ω=Ω{xi}
  3. 如果核心点集合 Ω = ∅ \Omega=\varnothing Ω=,结束,否则转入步骤4

  4. 在核心点集合 Ω \Omega Ω中,随机选择一个核心点 o o o,初始化当前簇核心点队列 Ω c u r = { o } \Omega_{cur}=\{o\} Ωcur={o},初始化类别序号 k = k + 1 k=k+1 k=k+1,初始化当前簇样本集合 C k = { o } C_k=\{o\} Ck={o},更新为访问样本集合 Γ = Γ − { o } \Gamma=\Gamma-\{o\} Γ=Γ{o}

  5. 如果当前核心点队列 Ω c u r = ∅ \Omega_{cur}=\varnothing Ωcur=,则当前簇 C k C_k Ck生成完毕,更新簇划分 C = { C 1 , C 2 , . . . , C k } C=\{C_1,C_2,...,C_k\} C={C1,C2,...,Ck},更新核心点集合 Ω = Ω − C k \Omega=\Omega-C_k Ω=ΩCk,转入步骤3。否则更新核心点集合 Ω = Ω − C k \Omega=\Omega-C_k Ω=ΩCk

  6. 在当前簇核心点队列 Ω c u r \Omega_{cur} Ωcur中取出一个核心点 o ′ o' o,通过邻域阈值 ϵ \epsilon ϵ找出所有的 ϵ \epsilon ϵ邻域子样本集 N ϵ ( o ′ ) N_\epsilon(o') Nϵ(o),令 Δ = N ϵ ( o ′ ) ∩ Γ \Delta=N_\epsilon(o') \cap \Gamma Δ=Nϵ(o)Γ,更新当前簇样本集合 C k = C k ∪ Δ C_k=C_k \cup \Delta Ck=CkΔ,更新为访问样本集合 Γ = Γ − Δ \Gamma = \Gamma - \Delta Γ=ΓΔ,更新 Ω c u r = Ω c u r ∪ ( Δ ∩ Ω ) − { o ′ } \Omega_{cur}=\Omega_{cur} \cup (\Delta \cap \Omega)-\{o'\} Ωcur=Ωcur(ΔΩ){o},转入步骤5

简单来说:

  1. 根据给定的邻域参数 ϵ \epsilon ϵ M i n P t s MinPts MinPts确定所有的核心点
  2. 对每一个核心点
  3. 选择一个未处理过的核心点,找到由其密度可达的样本生成聚类‘簇’
  4. 重复以上过程

3. 实例演示

import numpy as np 
import matplotlib.pyplot as plt from sklearn import cluster, datasets
from sklearn.preprocessing import StandardScalernp.random.seed(0)# 构建数据
n_samples = 1500
noisy_circles = datasets.make_circles(n_samples=n_samples, factor=0.5, noise=0.05)
noisy_moons = datasets.make_moons(n_samples=n_samples, noise=0.05)
blobs = datasets.make_blobs(n_samples=n_samples, random_state=8)data_sets = [(noisy_circles,{"eps": 0.3,"min_samples": 5}),(noisy_moons,{"eps": 0.3, "min_samples": 5}), (blobs, {"eps": 0.3, "min_samples": 5})
]
colors = ["#377eb8", "#ff7f00", "#4daf4a"]plt.figure(figsize=(15, 5))for i_dataset, (dataset, algo_params) in enumerate(data_sets):# 模型参数params = algo_params# 数据X, y = datasetX = StandardScaler().fit_transform(X)# 创建DBSCANdbscan = cluster.DBSCAN(eps=params["eps"], min_samples=params['min_samples'])# 训练dbscan.fit(X)# 预测y_pred = dbscan.labels_.astype(int)y_pred_colors = []for i in y_pred:y_pred_colors.append(colors[i])plt.subplot(1, 3, i_dataset+1)plt.scatter(X[:, 0], X[:, 1], color=y_pred_colors)plt.show()

在这里插入图片描述

4. DBSCAN小结

优点:

  1. 可以对任意形状的稠密数据集进行聚类,相对的,K-MeansMean Shift之类的聚类算法一般只适用于凸数据集
  2. 可以在聚类的同时发现异常点,对数据集中的异常点不敏感。
  3. 聚类结果没有偏倚,相对的,K-Means之类的聚类算法初始值对聚类结果有很大影响。

缺点:

  1. 如果样本集的密度不均匀、聚类间距差相差很大时,聚类质量较差,这时用DBSCAN聚类一般不适合。
  2. 如果样本集较大时,聚类收敛时间较长,此时可以对搜索最近邻时建立的KD树或者球树进行规模限制来改进。
  3. 调参相对于传统的K-Means之类的聚类算法稍复杂,主要需要对距离阈值 ϵ \epsilon ϵ,邻域样本数阈值 M i n P t s MinPts MinPts联合调参,不同的参数组合对最后的聚类效果有较大影响。

http://chatgpt.dhexx.cn/article/XgvjgGWS.shtml

相关文章

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

char转换为int型数据 通过赋值方式将char类型变量转换为int型变量,变量值为char类型变量的ASCII码值 例如:int a ‘0’ 那么打印a的结果为48,如果想要得到正确的数字,需要减去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…

构造函数和析构函数顺序

父子类 1、构造顺序&#xff1a; 创建一个子类对象&#xff0c;则父类、子类的构造方法都执行&#xff0c;且是 先父类 构造方法&#xff0c;再子类构造方法 派生类的构造顺序&#xff1a;先父类&#xff0c;后子类&#xff0c;因为子类很有可能会用到从父类继承来的成员 2、析…

【C++】构造函数与析构函数

1. 概述 构造函数&#xff1a;用于初始化对象&#xff0c;没有返回值&#xff0c;函数名和类名相同&#xff0c;只有在对象初始化的时候才会被调用。构造函数的分类&#xff1a; 默认构造函数&#xff1a;是编译器自动生成&#xff0c;没有任何参数的构造函数。 有参构造函数&…

何时调用构造函数和析构函数

何时调用构造函数和析构函数 构造函数的作用是保证每个对象的数据成员都有何时的初始值。 析构函数的作用是回收内存和资源&#xff0c;通常用于释放在构造函数或对象生命期内获取的资源。 一般我们都知道构造和析构的次序&#xff1a; 构造从类层次的最根处开始&#xff0c…

C++篇----构造函数和析构函数

在很多时候&#xff0c;当写了初始化&#xff0c;动态开辟的&#xff0c;需要写销毁函数&#xff0c;写了销毁函数之后&#xff0c;但是却忘记了调用这些函数&#xff0c;忘记调用初始化函数还好&#xff0c;编译器会报错&#xff0c;但是如果是忘记调用销毁函数&#xff0c;那…