DBSCAN(Density-Based Spatial Clustering of Applications with Noise)
是一基于密度的聚类算法,DBSCAN
将簇定义为密度相连的点的最大集合,能够把具有足够高密度的区域划分为簇,并可在噪声的空间数据库中发现任意形状的聚类。
DBSCAN
相关定义 DBSCAN
是基于一组邻域来描述样本集的紧密程度的,参数 ( ϵ , M i n P t s ) (ϵ, MinPts) (ϵ,MinPts)用来描述邻域的样本分布紧密程度。其中, ϵ ϵ ϵ描述了某一数据点的邻域距离阈值(半径), M i n P t s MinPts MinPts描述了数据点半径为 ϵ ϵ ϵ的邻域中数据点个数的最小个数。下面是与密度聚类相关的定义(假设我的样本集是 D = ( x 1 , x 2 , . . . , x m ) D=(x_1,x_2,...,x_m) D=(x1,x2,...,xm)):
从下图可以很容易看出理解上述定义,图中 M i n P t s = 5 MinPts=5 MinPts=5,红色的点都是核心对象,因为其 ϵ ϵ ϵ-邻域至少有 5 5 5个样本。黑色的样本是非核心对象。所有核心对象密度直达的样本在以红色核心对象为中心的圆内,如果不在圆内,则不能密度直达。图中用绿色箭头连起来的核心对象组成了密度可达的样本序列,此序列是一个簇集。在这些密度可达的样本序列的ϵ-邻域内所有的样本相互都是密度相连的 (注意,此图中有两个簇集)。
DBSCAN
密度聚类思想DBSCAN
算法定义 DBSCAN
的聚类定义很简单: 由密度可达关系导出的最大密度相连的样本集合,即为我们最终聚类的一个类别,或者说一个簇。(注意是密度相连的集合),簇里面可以有一个或者多个核心对象。如果只有一个核心对象,则簇里其他的非核心对象样本都在这个核心对象的 ϵ ϵ ϵ-邻域里;如果有多个核心对象,则簇里的任意一个核心对象的 ϵ ϵ ϵ-邻域中一定有一个其他的核心对象,否则这两个核心对象无法密度可达。这些核心对象的 ϵ ϵ ϵ-邻域里所有的样本的集合组成的一个DBSCAN
聚类簇。
那么怎么才能找到这样的簇样本集合呢?DBSCAN
使用的方法很简单,它任意选择一个没有类别的核心对象作为种子,然后找到所有这个核心对象能够密度可达的样本集合,即为一个聚类簇。接着继续选择另一个没有类别的核心对象去寻找密度可达的样本集合,这样就得到另一个聚类簇 (这样的得到都肯定是密度相连的)。一直运行到所有核心对象都有类别为止。
基本上这就是DBSCAN算法的主要内容了,是不是很简单?但是我们还是有三个问题没有考虑。
DBSCAN
中,我们一般将这些样本点标记为噪音点。DBSCAN
中,一般采用最近邻思想,采用某一种距离度量来衡量样本距离,比如欧式距离、曼哈顿距离等。DBSCAN
采用先来后到,先进行聚类的类别簇会标记这个样本为它的类别。也就是说BDSCAN
的算法不是完全稳定的算法。输入:样本集 D = ( x 1 , x 2 , . . . , x m ) D=(x_1,x_2,...,x_m) D=(x1,x2,...,xm),邻域参数 ( ϵ , M i n P t s ) (ϵ,MinPts) (ϵ,MinPts)
DBSCAN
算法实现numpy
实现DBSCAN
算法import numpy as np
import matplotlib.pyplot as plt
from scipy.spatial.distance import pdist
from scipy.spatial.distance import squareform
import time
# 计算距离矩阵
def compute_squared_EDM(X):
return squareform(pdist(X,metric='euclidean'))
# DBSCAN算法核心过程
def DBSCAN(data,eps,minPts):
# 获得距离矩阵
disMat = compute_squared_EDM(data)
# 获得数据的行和列(一共有n条数据)
n, m = data.shape
# 将矩阵的中小于minPts的数赋予1,大于minPts的数赋予零,然后1代表对每一行求和,然后求核心点坐标的索引
core_points_index = np.where(np.sum(np.where(disMat <= eps, 1, 0), axis=1) >= minPts)[0]
# 初始化类别,-1代表未分类。
labels = np.full((n,), -1)
clusterId = 0
# 遍历所有的核心点
for pointId in core_points_index:
# 如果核心点未被分类,将其作为的种子点,开始寻找相应簇集
if (labels[pointId] == -1):
# 首先将点pointId标记为当前类别(即标识为已操作)
labels[pointId] = clusterId
# 然后寻找种子点的eps邻域且没有被分类的点,将其放入种子集合
neighbour=np.where((disMat[:, pointId] <= eps) & (labels==-1))[0]
seeds = set(neighbour)
# 通过种子点,开始生长,寻找密度可达的数据点,一直到种子集合为空,一个簇集寻找完毕
while len(seeds) > 0:
# 弹出一个新种子点
newPoint = seeds.pop()
# 将newPoint标记为当前类
labels[newPoint] = clusterId
# 寻找newPoint种子点eps邻域(包含自己)
queryResults = np.where(disMat[:,newPoint]<=eps)[0]
# 如果newPoint属于核心点,那么newPoint是可以扩展的,即密度是可以通过newPoint继续密度可达的
if len(queryResults) >= minPts:
# 将邻域内且没有被分类的点压入种子集合
for resultPoint in queryResults:
if labels[resultPoint] == -1:
seeds.add(resultPoint)
# 簇集生长完毕,寻找到一个类别
clusterId = clusterId + 1
return labels
# 将分类后的数据可视化显示
def plotFeature(data, labels_):
clusterNum=len(set(labels_))
fig = plt.figure()
scatterColors = ['black', 'blue', 'green', 'yellow', 'red', 'purple', 'orange', 'brown']
ax = fig.add_subplot(111)
for i in range(-1,clusterNum):
colorSytle = scatterColors[i % len(scatterColors)]
subCluster = data[np.where(labels_==i)]
ax.scatter(subCluster[:,0], subCluster[:,1], c=colorSytle, s=12)
plt.show()
# 加载数据
data = np.loadtxt("./cluster2.csv", delimiter=",")
start = time.clock()
# DBSCAN聚类并返回标识;ϵ=2,且MinPts=15
labels=DBSCAN(data,3,30)
end = time.clock()
print('finish all in %s' % str(end - start))
plotFeature(data, labels)
注意:棕色是离群点
scikit-learn
调用DBSCAN
算法# -*- coding: utf-8 -*-
import numpy as np
from sklearn.cluster import DBSCAN
# 加载数据
data=np.loadtxt("788points.txt",delimiter=",")
# 构造一个ϵ=2,MinPts=15的聚类器,距离使用欧式距离
estimator=DBSCAN(eps=2,min_samples=15,metric='euclidean')
# 聚类数据
estimator.fit(data)
# 输出聚类都类别(-1代表异常点)
print(estimator.labels_)
DBSCAN
优缺点DBSCAN的主要优点有:
K-Means
之类的聚类算法一般只适用于凸数据集。DBSCAN的主要缺点有:
K-Means
之类的聚类算法稍复杂,主要需要对距离阈值ϵ
,邻域样本数阈值MinPts
联合调参,不同的参数组合对最后的聚类效果有较大影响。Demo
一个Star
,您的支持是我最大的动力)