密度聚类DBSCAN

news/2024/5/20 6:23:10 标签: 聚类, python

1.相关概念

DBSCAN是基于密度的聚类算法,该类算法假设聚类结构能够通过样本分布的紧密程度确定(样本密度均匀分布),它通常考虑的是样本之间的可连接性,并以最大连接性确定聚类簇。要搞懂该算法,首先要理清楚几个概念:

  • 邻域:对于样本 x i ∈ D x_i \in D xiD,其邻域包含样本集D中距离 x i x_i xi不超过 ϵ \epsilon ϵ的样本,即 N ϵ ( x i ) = { x j ∈ D ∣ d i s t ( x i , x j ) ≤ ϵ } N_\epsilon(x_i)=\{x_j \in D | dist(x_i,x_j) \leq \epsilon \} Nϵ(xi)={xjDdist(xi,xj)ϵ}。若采用欧式距离,那么 N ϵ ( x i ) N_\epsilon(x_i) Nϵ(xi)就是以 x i x_i xi为圆心,以 ϵ \epsilon ϵ为半径的圆域。
  • 核心对象: x i x_i xi的邻域中至少包含 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 j x_j xj x i x_i xi的邻域中且 x i x_i xi是核心对象,则称 x j x_j xj x i x_i xi密度直达,记作 x i → x j x_i \rightarrow x_j xixj。不难理解,因为核心对象 x i x_i xi与邻域中的样本紧密的挨着,我们可以认为 x i x_i xi x j x_j xj是直接可达的。
  • 密度可达:对于样本 x i x_i xi x j x_j xj,若其间存在密度直达序列 p 1 , p 2 , . . . , p n p_1,p_2,...,p_n p1,p2,...,pn,其中 p 1 = x i , p n = x j p_1 = x_i,p_n=x_j p1=xi,pn=xj,则称 x j x_j xj x i x_i xi 密度可达。例如, x 1 → x 2 → x 3 → x 4 x_1 \rightarrow x_2 \rightarrow x_3 \rightarrow x_4 x1x2x3x4,则称 x 4 由 x 1 x_4由x_1 x4x1密度可达。而且 x 1 , x 2 , x 3 x_1,x_2, x_3 x1,x2,x3一定是核心对象, x 4 x_4 x4不一定。这说明,我们访问核心对象领域中的样本,就能访问到下一个核心对象,直至邻域中不再含有核心对象,这样我们能访问到所有可达的核心对象及其邻域中的样本。
  • 密度相连:若 x i 和 x j x_i和x_j xixj间存在样本 x k x_k xk,使得 x i 和 x j x_i和x_j xixj均由 x k x_k xk密度可达,称 x i 和 x j x_i和x_j xixj密度相连。注意,密度可达一定密度相连。
    请添加图片描述

上图给出了这些概念的示意图。其中, x 1 x_1 x1 x 2 x_2 x2是核心对象, x 2 x_2 x2 x 1 x_1 x1密度直达, x 3 x_3 x3 x 1 x_1 x1密度可达, x 3 x_3 x3 x 4 x_4 x4密度相连。基于这些概念,DBSCAN将簇定义为:由密度可达推导出最大密度相连样本的集合。 翻译成数学语言:

  • 连接性: 若 x i , x j ∈ C 若x_i,x_j \in C xi,xjC,则 x i x_i xi x j x_j xj密度相连。
  • 最大性: x i ∈ C x_i \in C xiC x j x_j xj x i x_i xi密度可达,则 x j ∈ C x_j \in C xjC

即同一簇内任意两点密度相连,分属不同簇的任意两点不可能密度可达。

2.算法流程

DBSCAN的思想:先找到样本中所有核心对象,再从任意核心对象出发,逐个访问其邻域的样本找到下一个核心对象及其邻域,如此迭代下去能访问到所有密度可达的样本,这样构成的集合满足连接性和最大性是一个簇,所有核心对象都访问过之后能划分出所有簇。具体流程如下:

  1. 输入:样本集D和邻域参数( ϵ , M i n P t s \epsilon,MinPts ϵ,MinPts)
  2. 初始化核心对象集合 Ω = ∅ \Omega = \varnothing Ω=
  3. for i =1,2,3,…,m do
  4. \enspace\enspace 找到 x i x_i xi的邻域 N ϵ ( x i ) N_\epsilon(x_i) Nϵ(xi)
  5. \enspace\enspace if ∣ N ϵ ( x i ) ∣ ≥ m i n P t s |N_\epsilon(x_i)| \geq minPts Nϵ(xi)minPts then
  6. \enspace\enspace\enspace 将核心对象 x i x_i xi添加到 Ω \Omega Ω
  7. \enspace\enspace end if
  8. end for
  9. 初始化聚类数:k=0
  10. 初始化为本访问的样本集合: T = D T = D T=D
  11. while Ω ≠ ∅ \Omega \neq \varnothing Ω= do
  12. \enspace\enspace T o l d = T T_{old} = T Told=T
  13. \enspace\enspace 随机选择一个核心对象 O ∈ Ω O \in \Omega OΩ
  14. \enspace\enspace 初始化一个队列Q = < O >
  15. \enspace\enspace T = T − O T = T - O T=TO
  16. \enspace\enspace while Q ≠ ∅ Q \neq \varnothing Q= do
  17. \enspace\enspace\enspace 取出对首元素q = Q.get()
  18. \enspace\enspace\enspace if ∣ N ϵ ( q ) ∣ ≥ m i n P t s |N_\epsilon(q)| \geq minPts Nϵ(q)minPts then
  19. \enspace\enspace\enspace\enspace Δ = N ϵ ( q ) ∩ T \Delta = N_\epsilon(q)\cap T Δ=Nϵ(q)T
  20. \enspace\enspace\enspace\enspace Δ \Delta Δ的样本加入队列Q
  21. \enspace\enspace\enspace\enspace T = T − Δ T=T-\Delta T=TΔ
  22. \enspace\enspace\enspace end if
  23. \enspace\enspace end while
  24. \enspace\enspace k=k+1
  25. \enspace\enspace 生成簇为访问过的样本 C k = T o l d − T C_k =T_{old}-T Ck=ToldT
  26. \enspace\enspace 删除访问过的核心对象 Ω = Ω − C k \Omega =\Omega -C_k Ω=ΩCk
  27. end while
  28. 输出:所有簇

算法瓶颈:DBSCAN的瓶颈在于求邻域 N ϵ ( x i ) N_\epsilon (x_i) Nϵ(xi),因为每次都要计算所有样本与 x i x_i xi的距离,当样本规模很大时求邻域相当费时。可以采用网格的方法,大量减少访问的样本数。

python_52">3.python实现

python">#确定x的领域
def neiborgh(data,x,epsilon):
    indexs = []
    for i in range(0,data.shape[0]):
        dist = math.sqrt((data[i]-x)@(data[i]-x).T)
        if dist <= epsilon:
            indexs.append(i)
    return data[indexs]

#求二维数组的交集
def intersect(A,B):
    lows=[]
    for i in range(0,A.shape[0]):
        if (B == A[i]).all(1).any():
            lows.append(i)
    return A[lows]
        

#求二维数组的差集
def difference(A,B):
    inter = intersect(A,B)
    lows = []
    for i in range(0,A.shape[0]):
        if (inter == A[i]).all(1).any():
            lows.append(i)
            
    return  np.delete(A,lows,axis = 0)
    
def dbscan(data,epsilon,minPts):
    m,n = data.shape
    #初始化核心对象集合
    centers = np.zeros((1,n))
    #寻找所有核心对象
    for i in range(0,m):
        N = neiborgh(data,data[i],epsilon)
        if N.shape[0] >= minPts: 
            centers = np.concatenate((centers,[data[i]]),axis=0)#行拼接
    #删除第一行
    centers = np.delete(centers,0,0)
    #簇的个数
    k = 0
    #未被访问的集合
    I = data
    #初始化簇的集合
    cls =[]
    #外层循环随机选择一个核心对象为出发点
    while centers.shape[0]!=0:
        Iold = I.copy()
        #随机选择一个核心对象,加入队列
        r = np.random.randint(0,centers.shape[0])
        Q = Queue(maxsize=0)
        Q.put(centers[r])
        I = difference(I,[centers[r]])
        #内层循环找到所有相连的核心对象,及其领域
        while Q.qsize() > 0:
            q = Q.get()
            N =neiborgh(data,q,epsilon)
            #如果是核心对象的话
            if N.shape[0] >= minPts:
                ins = intersect(I,N)
                #交集中所有样本加入队列
                for d in ins:
                    Q.put(d)
                I = difference(I,ins)
        k=k+1
        #访问过的核心对象及其领域,形成一簇
        ck = difference(Iold,I)
        cls.append(ck)
        #删去访问过的核心对象
        centers = difference(centers,ck)
        
    return cls

随机生成三簇数据检验DBSCAN聚类效果:

python">def showClusters(c1,c2,c3):
    plt.scatter(c1[:,0],c1[:,1],c='r',s=10)
    plt.scatter(c2[:,0],c2[:,1],c='b',s=10)
    plt.scatter(c3[:,0],c3[:,1],c='g',s=10)
    plt.show()

moids = [[2,2],[8,2],[0,8]]
X = datasets.make_blobs(n_samples=200, n_features=2, centers=moids,cluster_std=1)[0]
#X=datasets.make_classification(n_samples=200,n_features=2,n_informative=2,n_redundant=0,n_repeated=0,n_clusters_per_class=1)[0]
cls  = dbscan(X,0.88,4)
plt.scatter(X[:,0],X[:,1],s=10) 
plt.show()
showClusters(cls[0],cls[1],cls[2])

原始数据分布和聚类后的效果图如下:
请添加图片描述
可以看出DBSCAN聚类后簇的分布与原始数据分布基本一致,更重要地是,DBSCAN聚类过后一些离群点被抛弃了,因为离群点与任意核心对象都密度不可达,所以是访问不到的,这是DBSCAN的一个优点。


http://www.niftyadmin.cn/n/972607.html

相关文章

机器学习技法---(Week2)Dual Support Vector Machine

上节课把原始的优化问题改写成二次规划的形式&#xff0c;通过软件包来求解参数。这节课通过研究原问题的对偶问题&#xff0c;在一定条件下&#xff0c;对偶问题的最优解和解参数和原问题一致&#xff0c;继而得到原问题的解。 Motivation of Dual SVM 对于非线性SVM&#x…

分享一个我的JavaScript版GridView多功能表格

GridView是什么&#xff1f; GridView是由Mr.Co开发的一套开源的多功能表格插件&#xff0c;主要用于让页面开发者在开发中节省拼接Table表格和操作Table表格相关复杂操作的开发成本与时间。开发人员可以用GridView快速开发出带有集成编辑、组合表头、分页、表行列操作等一系列…

层次聚类AGNES与DIANA

1. AGNES AGNES是一种采用自底向上合并策略的聚类算法&#xff0c;其思想为&#xff1a;初始将所有样本看成一个簇&#xff0c;然后在每一轮过程中将距离最近的两个簇合并为一个簇&#xff0c;簇的个数不断减少到人为指定的聚类簇数K&#xff0c;终止算法。该算法关键在于如何…

linux之i2c子系统维护者源码仓库地址

仓库地址: git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux.git 转载于:https://www.cnblogs.com/dakewei/p/11224684.html

RPackage007---smbinning

title: “Learning R—smbinning” author: “刘栋” date: “2018年4月5日” output: word_document knitr::opts_chunk$set(echo TRUE)这个包主要是进行woe分组时候用的&#xff0c;有比较丰富的函数可以用。简单介绍其中两个函数&#xff0c;最优分箱和自定义分箱。业务希…

linux下的什么工具能将DVI文件转换成PostScript文件?

答: dvips,此工具能将由Latex或Tex生成的DVI文件转换成PostScript文件,官网在此 转载于:https://www.cnblogs.com/dakewei/p/11225010.html

Shiny07---用Shiny完成分箱调参工作

业务提需求&#xff0c;希望可以自动寻找阈值&#xff0c;完成分箱工作&#xff0c;继而找到合适的区间&#xff0c;区别好坏用户。采用R软件的smbinning包&#xff0c;提供自动最优分箱和手动切分两种方式&#xff0c;便于业务同事自动化的切分区间和观察结果。 提供ui和sever…

linux下的什么工具可以用来查看PostScript文件?

答: ghostview,官网在这里 转载于:https://www.cnblogs.com/dakewei/p/11225041.html