DBSCAN算法的原理与实现
原理
DBSCAN(Density-Based Spatial Clustering of Applications with Noise)有噪声的基于密度的空间聚类算法,关于算法的原理,可以参照下面这两篇博客,重点关注第一篇博客,介绍的比较简单易懂:
ChaoSimple:常用聚类算法(一) DBSCAN算法
刘建平Pinard:DBSCAN密度聚类算法
代码逻辑
上面两篇博客虽然比较好的介绍了DBSCAN算法的基本原理,但都没有说清楚编程实现的基本思路,所以这里主要介绍DBSCAN算法的代码实现逻辑,请看下图:
代码及结果展示
源代码
import math
from numpy import random
import numpy as np
import matplotlib.pyplot as plt
class DBSCAN(object):
def __init__(self,dataSet:np.array,MinPts:int,r:float,dType) -> None:
self.dataSet=dataSet
self.MinPts=MinPts
self.r=r
# 距离计算方法:
# 0 欧式距离
# 1 曼哈顿距离
self.dType=dType
self.dMatrix()
def dMatrix(self):
# 初始化距离矩阵
nrow,ncol=self.dataSet.shape
dMatrix=np.zeros((nrow,nrow))
for i in range(nrow-1):
for j in range(i+1,nrow):
start=self.dataSet[i,:]
end=self.dataSet[j,:]
diff=np.abs(start-end)
if self.dType:
dist=np.sum(diff)
else:
dist=np.sqrt(np.sum(diff**2))
dMatrix[i,j]=dist
dMatrix[j,i]=dist
self.dMatrix=dMatrix
def isCore(self,i,nrow):
# 判断第i个样本点是不是核心样本点
dists=self.dMatrix[i,:]
indexs=list()
n=0
for j in range(nrow):
if j==i:
continue
if dists[j]<=self.r:
n+=1
indexs.append(j)
if n>=self.MinPts:
flag=True
else:
flag=False
return flag,indexs
def classify(self):
nrow,_=self.dataSet.shape
# 初始化分类结果
result=np.zeros(nrow)
k=0 #当前聚类数
# 遍历所有样本点
for i in range(nrow):
if result[i]:
continue #样本点已经分类
else:
flag,indexs=self.isCore(i,nrow)
if flag:
k+=1
result[i]=k
for j in indexs:
if result[j]:
continue
else:
result[j]=k
flag1,indexs1=self.isCore(j,nrow)
if flag1:
indexs.extend(indexs1)
self.result=result
if __name__=='__main__':
# 开始测试程序
dataSet=list()
# 产生大圆
for i in range(1000):
r=random.rand()+4.5
rad=random.rand()*np.pi*2
dataSet.append([r*math.cos(rad),r*math.sin(rad)])
# 产生小圆
for i in range(1000):
r=random.rand()+1.5
rad=random.rand()*np.pi*2
dataSet.append([r*math.cos(rad),r*math.sin(rad)])
# 产生随机噪声点
for i in range(100):
dataSet.append([random.rand()*10-5,random.rand()*10-5])
dataSet=np.array(dataSet)
DB=DBSCAN(dataSet,5,0.5,0)
DB.classify()
plt.figure(figsize=(10,10))
plt.scatter(dataSet[DB.result==2,0],dataSet[DB.result==2,1],color='blue',marker='o')
plt.scatter(dataSet[DB.result==1,0],dataSet[DB.result==1,1],color='green',marker='+')
plt.scatter(dataSet[DB.result==0,0],dataSet[DB.result==0,1],color='red',marker='<')
plt.show()
结果图
从图中可以看到,样本点被较好的分成了绿色和蓝色两类,聚类簇呈两个圆环,因此也验证了DBSCAN聚类算法对于聚类簇的形状没有偏移的特性,而类似于K均值聚类等基于空间距离的聚类算法就只能用于凸样本点聚类。
此外,向样本集中加入的噪声点也被较好的识别了出来,即结果图中红色三角形符号的样本点,验证了DBSCAN算法在聚类的同时也能识别样本集中的噪声点和离群值。