欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

密度聚类(DBSCAN)

程序员文章站 2022-07-14 11:41:28
...

算法基本概念

基于密度的聚类算法从样本密度的角度考察样本之间的可连接性,并基于可连接样本不断扩展聚类簇得到最终结果。
几个必要概念:
ε-邻域:对于样本集中的xj, 它的ε-邻域为样本集中与它距离小于ε的样本所构成的集合。
核心对象:若xj的ε-邻域中至少包含MinPts个样本,则xj为一个核心对象。
密度直达:若xj位于xi的ε-邻域中,且xi为核心对象,则xj由xi密度直达。
密度可达:若样本序列p1, p2, ……, pn。pi+1由pi密度直达,则p1由pn密度可达。

实验步骤

解析样本数据文件,计算每个点与其他所有点之间的欧几里德距离。根据给定MinPts=4,以及半径Eps的值,计算所有核心点,并建立核心点与到核心点距离小于半径Eps的点的映射。根据得到的核心点集合,以及半径Eps的值,计算能够连通的核心点,得到噪声点。将能够连通的每一组核心点,以及到核心点距离小于半径Eps的点,都放到一起,形成一个簇。选择不同的半径Eps,使用DBSCAN算法聚类得到的一组簇及其噪声点,使用散点图对比聚类效果。然后,再详细描述聚类过程的具体实现。

编程思路

初始化核心对象集合T为空,遍历一遍样本集D中所有的样本,计算每个样本点的ε-邻域中包含样本的个数,如果个数大于等于MinPts,则将该样本点加入到核心对象集合中。初始化聚类簇数k = 0, 初始化未访问样本集和为P = D。
当T集合中存在样本时执行如下步骤:
2.1记录当前未访问集合P_old = P
2.2从T中随机选一个核心对象o,初始化一个队列Q = [o]
2.3P = P-o(从T中删除o)
2.4当Q中存在样本时执行:
2.4.1取出队列中的首个样本q
2.4.2计算q的ε-邻域中包含样本的个数,如果大于等于MinPts,则令S为q的ε-邻域与P的交集,
Q = Q+S, P = P-S
2.5 k = k + 1,生成聚类簇为Ck = P_old - P
2.6 T = T - Ck
划分为C= {C1, C2, ……, Ck}

参数变化特点

如果MinPts不变,Eps取得值过大,会导致大多数点都聚到同一个簇中,Eps过小,会导致已一个簇的分裂;如果Eps不变,MinPts的值取得过大,会导致同一个簇中点被标记为噪声点,MinPts过小,会导致发现大量的核心点。

python源代码

 #-*- coding:utf-8 -*-

import math
import numpy as np
import pylab as pl

import scipy.io as sio

data_train = sio.loadmat('square4.mat')  
data=data_train['b']
data_1=data[:,:2]
dataset = [(float(data_1[i,0]), float(data_1[i,1])) for i in range(0,1000)]#转化为列表

def dist(x1, x2):#计算欧几里得距离,x1,x2分别为两个元组
    return math.sqrt(math.pow(x1[0]-x2[0], 2)+math.pow(x1[1]-x2[1], 2))

#算法模型
def DBSCAN(Date, R, Minpts):
    #初始化核心对象集合T,聚类个数k,聚类集合C, 未访问集合P,
    T = set(); k = 0; C = []; P = set(Date)
    for d in Date:
        if len([ i for i in Date if dist(d, i) <= R]) >= Minpts:#遍历数据集,找出核心点
            T.add(d)
    #开始聚类
    while len(T):
        P_all = P
        o = list(T)[np.random.randint(0, len(T))]#取出其中一个核心点
        P = P - set(o)
        Q = []; Q.append(o)

        while len(Q):
            q = Q[0]
            Nq = [i for i in Date if dist(q, i) <= R]#找出与核心点同一簇的种子点
            if len(Nq) >= Minpts:
                S = P & set(Nq)
                Q += (list(S))                       #与种子点同一簇的种子点
                P = P - S                            #未分类
            Q.remove(q)
        k += 1                                       #簇个数
        Ck = list(P_all - P)                         #最终同一簇
        T = T - set(Ck)
        C.append(Ck)
    return C


def draw(C):#画图
    C.sort(key=lambda x: len(x))                    #对列表中的列表长度排序
    colValue = ['r', 'g', 'b', 'y', 'c', 'k', 'm']
    for i in range(len(C)):
        coo_X = []    #x坐标列表
        coo_Y = []    #y坐标列表

        if i>len(C)-5:
            for j in range(len(C[i])):
                coo_X.append(C[i][j][0])
                coo_Y.append(C[i][j][1])
            pl.scatter(coo_X, coo_Y, marker='x', color=colValue[i%len(colValue)],label=i)

        else:
            for j in range(len(C[i])):
                coo_X.append(C[i][j][0])
                coo_Y.append(C[i][j][1])
            pl.scatter(coo_X, coo_Y, marker='o', color=colValue[3 % len(colValue)])
    pl.legend(loc='upper right')
    pl.show()

C = DBSCAN(dataset, 0.5, 5)
draw(C)

实验结果

密度聚类(DBSCAN)