DBSCAN算法,概念+示例,超详细!!

DBSCAN

(Density-Based Spatial Clustering of Applications with Noise)

与划分和层次聚类方法不同,它将簇定义为密度相连的点的最大集合,能够把具有足够高密度的区域划分为簇,并可在噪声的空间数据库中发现任意形状的聚类

算法流程

  1. 选取一个点,以eps为半径,画一个圈,看圈有几个临近点

    若临近点个数大于某个阈值(min_points) ,则认为该点为某一簇的点;

    若小于min_points,则被标记为噪声点,然后处理下一个点

  2. 将临近点作为种子点 seeds = [4,7,9,10 ]

    遍历所有种子点

    ​ 若该点被标记为噪声点 ,则重标为聚类点

    ​ 若该点没有被标记过,则标记为聚类点

    ​ 并且以该点为圆心,以eps为半径画圈,若圈内点大于min_points,则将圈内点添加到种子点中

    ​ seeds = [4,7,9,10,1,7,9,16]

    在这里插入图片描述

  3. 重复步骤2,直到遍历完所有的种子点

    seeds = [4,7,9,10, 1,7,9,16] #第一次步骤2,标记4

    ​ 7的周围有12,4,小于min_points,seeds不扩展

    在这里插入图片描述

    seeds = [4,7,9,10, 1,7,9,16] #第二次步骤2,标记7

    ​ 9的周围有1,4,3,大于min_points,seeds扩展

    在这里插入图片描述

    seeds = [4,7,9,10, 1,7,9,16,1,4,3] #第三次步骤2,标记9

    ​ 10的周围有1,6,7,大于min_points,seeds扩展

    在这里插入图片描述

    seeds = [4,7,9,10, 1,7,9,16,1,4,3,1,6,7] #第四次步骤2,标记10

    ​ 1已经标记过,继续下一个点

    seeds = [4,7,9,10, 1,7,9,16,1,4,3,1,6,7]

    ​ 7已经标记过,继续下一个点

    seeds = [4,7,9,10, 1,7,9,16,1,4,3,1,6,7]

    ​ 9已经标记过,继续下一个点

    seeds = [4,7,9,10, 1,7,9,16,1,4,3,1,6,7]

    ​ 16的周围有12,4,小于min_points,seeds不扩展

    seeds = [4,7,9,10, 1,7,9,16,1,4,3,1,6,7]

    .

    .

    .

    重复进行画圈、标记

  4. 标记完一簇后

寻找一个未被标记的点,开始新一轮的聚类

​ 如果找到点5,周围点少,标记为NOISE

​ 如果找到点15,周围点少,标记为NOISE

​ 如果找到点19,开始新一轮聚类

在这里插入图片描述

.

.

.

最终效果

在这里插入图片描述

举例

import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import DBSCAN
from sklearn import metrics

UNCLASSIFIED = 0        #点未被标记
NOISE = -1              #噪声点标记

# 计算数据点两两之间的距离
def getDistanceMatrix(datas):       #datas 是聚类数据
    N,D = np.shape(datas)    #读取datas的维度,维度是N x D(N指数据个数,D指特征维度)   ,shape函数用于获取矩阵的形状
    dists = np.zeros([N,N])     #zeros 函数:返回一个给定形状和类型的用0填充的数组,

    for i in range(N):      
        for j in range(N):
            vi = datas[i,:]     #切片 [开始,结束]
            vj = datas[j,:]
            dists[i,j]= np.sqrt(np.dot((vi-vj),(vi-vj)))    #欧式距离函数,返回点与点之间距离的数组
    return dists


#  寻找以点cluster_id 为中心,eps 为半径的圆内的所有点的id
def find_points_in_eps(point_id, eps, dists):
    index = dists[point_id] <= eps          #dists[point_id] 即:point_id 与 所有点的距离
    return np.where(index)[0].tolist()      #返回所有符合的点的集合


# 聚类扩展
# dists : 任意数据两两之间的距离  N x N
# labs :   所有数据的标签 labs N,
# cluster_id : 一个簇的标号
# eps : 密度评估半径
# seeds: 用来进行簇扩展的点
# min_points: 半径内最少的点数
def expand_cluster(dists, labs, cluster_id, seeds, eps, min_points):
    i = 0
    while i < len(seeds):   
        Pn = seeds[i]       #获取一个点
        if labs[Pn] == NOISE:   #如果是噪声点,则重新标记
            labs[Pn] = cluster_id
        elif labs[Pn] == UNCLASSIFIED:  #如果未被标记过,则进行标记
            labs[Pn] = cluster_id           
            new_seeds = find_points_in_eps(Pn, eps, dists) # 以新点为圆心再画圈,进行扩展
            if len(new_seeds) >= min_points:   #如果扩张的圈中数够多,则加入到seeds队列中 
                seeds = seeds + new_seeds   
        i += 1
                #通过挨个标记和扩展seeds里的数字,实现聚类过程
        
def dbscan(datas, eps, min_points):
    dists = getDistanceMatrix(datas)        #获取点与点之间的距离,且以二维数组的形式
    # 将所有点的标签初始化为0
    n_points = datas.shape[0]           #shape[0]指读取读取矩阵第一维的长度      
    labs = [UNCLASSIFIED] * n_points       

    cluster_id = 0
    # 遍历所有点
    for point_id in range(n_points):
        if labs[point_id] != UNCLASSIFIED:  #如果被标记,则结束此次循环,表示该点已处理过
            continue                        #没有处理过,则计算寻找临近点
        seeds = find_points_in_eps(point_id, eps, dists)   #符合条件的点存到seeds中

        if len(seeds) < min_points: # 如果临近点过少,则标记为噪声点
            labs[point_id] = NOISE  
        else:                               #否则开启新一轮扩张
            cluster_id = cluster_id + 1     
            labs[point_id] = cluster_id     #标记当前点
            expand_cluster(dists, labs, cluster_id, seeds, eps, min_points)
    return labs, cluster_id
   
   
# 绘图         
                # 数据  聚类结果  聚类个数
def draw_cluster(datas,labs, n_cluster):     
    plt.cla()       
                #设计颜色                   
    colors = [plt.cm.Spectral(each)         
          for each in np.linspace(0, 1,n_cluster)]   #(起点,终点,几个元素)   
    
        #遍历所有点的坐标
    for i,lab in enumerate(labs):
        if lab ==NOISE:     #如果是噪声点 则为黑色
            plt.scatter(datas[i,0],datas[i,1],s=16.,color=(0,0,0))
        else:       #否则,根据类别的编号,来标记颜色
            plt.scatter(datas[i,0],datas[i,1],s=16.,color=colors[lab-1])
    plt.show()
    

if __name__== "__main__":
    
    ## 数据1
    centers = [[1, 1], [-1, -1], [1, -1]]       #三个中心点的坐标
    #datas为样本数据集,labels_true为样本数据集的标签
    datas, labels_true = make_blobs(n_samples=750, centers=centers, cluster_std=0.4,
                             random_state=0) 
                              #产生一组随机数datas,中心点是centers,方差是0.4,产生750个点
    
    ## 数据2
    #file_name = "spiral"
    #with open(file_name+".txt","r",encoding="utf-8") as f:
    #    lines = f.read().splitlines()
    #lines = [line.split("\t")[:-1] for line in lines]
    #datas = np.array(lines).astype(np.float32)
    ###
    
    # 数据正则化,让参与的数据减去均值出方差,是临均值,标准差成了1
    datas = StandardScaler().fit_transform(datas) #计算训练数据的均值和方差,并基于计算出来的均值和方差来转换训练数据,从而把数据转换成标准的正态分布
    eps = 0.3
    min_points = 10
    	#手动实现DBSCAN
    	#dbscan算法,labs是最终结果,cluster_id是分成了多少类
    labs, cluster_id = dbscan(datas, eps=eps, min_points=min_points)
    print("labs of my dbscan")
    print(labs)
    
    #sklearn里的DBSCAN 算法
    #分类器     # 半径      min_points           对datas进行聚类
    db = DBSCAN(eps=eps, min_samples=min_points).fit(datas)
    skl_labels = db.labels_
    print("labs of sk-DBSCAN")
    print(skl_labels)
    #画出 
    draw_cluster(datas,labs, cluster_id)

    # dbscan 输出,123表示聚类点,-1表示噪声点
    # sklearn 输出  012表示聚类点,-1表示噪声点

  • 3
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值