目录
- 理论部分
- 1.1 提出背景
- 1.2 常见算法
- 1.3 DBSCAN算法
- 1.3.1 基本概念
- 1.3.2 算法流程
- 1.3.3 参数设置
- 1.3.3 优点
- 1.3.4 缺点
- 1.3.5 可视化结果展示
- 1.4 评估指标
- 代码部分
- 2.1 不使用sklearn实现
- 2.2 使用sklearn实现
理论部分
1.1 提出背景
与K-means算法基于距离聚类不同,DBSCAN算法是基于样本点密度进行聚类。基于距离的聚类方法只适用于凸型数据尤其是球状分布的数据,而难以处理非凸数据,而密度聚类法可以很好地解决这个问题,密度聚类法的基本思想是只要一个区域中的点的密度大过某个阀值,就把它加到与之相近的聚类中去。
1.2 常见算法
基于密度的聚类算法有很多,例如DBSCAN算法、OPTICS算法、DENCLUE算法、基于密度峰值的聚类方法等。本篇我们着重讲解DBSCAN。
1.3 DBSCAN算法
1.3.1 基本概念
设为样本集,
表示领域半径,
表示密度阈值,则有:
·领域
即表示以为中心,
为半径的圆内样本点的集合(包括自身)。
·密度
的密度定义为
的
领域内的样本点个数。
·核心点
令,若
,则称
为
的核心点,记
中所有核心点构成的集合为
,所有非核心点构成的集合为
PS:简单来说,核心点即为满足在领域内至少有密度阈值个样本点的点。
·边界点
若(即
为非核心点),且
,满足:
则称为边界点,令样本点中所有边界点构成的集合为
PS:边界点可以理解为指那些落入核心点领域内的但不是核心点的样本点。
·噪声点
若样本点同时有:
则称为噪声点。
PS:噪声点可以理解为既不是核心点也不是边界点的样本点。即若某个样本点在其领域内不具有至少密度阈值个样本点,同时该样本点也不落在任何核心点的
领域内,则称该样本点为噪声点。
如上图,蓝色样本点即为核心点,黑色样本为边界点,白色样本为噪声点。
·密度直达
对于样本点,若有:
则称是由
关于参数
密度直达。
PS:是由
密度直达的等效为以下说法:
是核心点且
处在
的
领域内。
·密度可达
对于样本点,如果存在一系列样本点
,若
能够由
密度直达
,则称
可由
密度可达。
·密度相连
如果存在一个样本点,使得
均可由
密度可达,则称
是密度相连的。
1.3.2 算法流程
该算法流程可大致描述如下:首先确定所有的核心点从而构成核心点集合(并将这些核心点标注为已访问)。而后每次选取一个核心点,如果其密度直达范围中存在另一个核心点,则将该核心点和可到达的那个核心点以及两者的密度直达范围中的样本点全部归属为一类。否则,第一类完成,开始第二类聚类,以此类推
1.3.3 参数设置
若或者
设置得非常小,则意味着没有点是核心样本,可能会导致所有点被标记为噪声
若或者
设置得非常大,可能会导致所有点形成单个簇。
1.3.3 优点
1.不必提前设置聚类数。
2.对非凸型数据处理能力较强,可以对任意类内稠密,类间系数的样本集进行聚类。
3.对异常点敏感性较低,可以较为有效地排除异常点的干扰。
4.可优化性强,部分步骤可由数据结构中的相关算法进行优化。
1.3.4 缺点
1.需要设置和
,而这往往是依靠人的直觉和经验的。
2.在大规模数据集上效率较低。
属于簇的点是实心,噪声点则显示为空心,核心样本点显示为较大的标记,而边界点则显示为较小的标记,上图即验证了和
取值大小对结果的影响。
1.3.5 可视化结果展示
上图为DBSCAN算法对于非球形分布数据的聚类过程。
上图为DBSCAN聚类结果(左图)和K-means聚类结果(右图)的对比
不同的和
设置也会导致不同的结果:
1.4 评估指标
这里仍然可以采用轮廓系数进行评估,关于轮廓系数的介绍可以见
代码部分
2.1 不使用sklearn实现
import matplotlib.pyplot as plt
import random
import numpy as np
import math
from sklearn import datasets
list_1 = []
list_2 = []
def loadDataSet(fileName, splitChar='\t'):
dataSet = []
with open(fileName) as fr:
for line in fr.readlines():
curline = line.strip().split(splitChar)
fltline = list(map(float, curline))
dataSet.append(fltline)
return dataSet
# 计算两个点之间的欧式距离,参数为两个元组
def dist(t1, t2):
dis = math.sqrt((np.power((t1[0]-t2[0]),2) + np.power((t1[1]-t2[1]),2)))
# print("两点之间的距离为:"+str(dis))
return dis
# DBSCAN算法,参数为数据集,Eps为指定半径参数,MinPts为制定邻域密度阈值
def dbscan(Data, Eps, MinPts):
num = len(Data) # 点的个数
# print("点的个数:"+str(num))
unvisited = [i for i in range(num)] # 没有访问到的点的列表
# print(unvisited)
visited = [] # 已经访问的点的列表
C = [-1 for i in range(num)]
# C为输出结果,默认是一个长度为num的值全为-1的列表
# 用k来标记不同的簇,k = -1表示噪声点
k = -1
# 如果还有没访问的点
while len(unvisited) > 0:
# 随机选择一个unvisited对象
p = random.choice(unvisited)
unvisited.remove(p)
visited.append(p)
# N为p的epsilon邻域中的对象的集合
N = []
for i in range(num):
if (dist(Data[i], Data[p]) <= Eps):# and (i!=p):
N.append(i)
# 如果p的epsilon邻域中的对象数大于指定阈值,说明p是一个核心对象
if len(N) >= MinPts:
k = k+1
# print(k)
C[p] = k
# 对于p的epsilon邻域中的每个对象pi
for pi in N:
if pi in unvisited:
unvisited.remove(pi)
visited.append(pi)
# 找到pi的邻域中的核心对象,将这些对象放入N中
# M是位于pi的邻域中的点的列表
M = []
for j in range(num):
if (dist(Data[j], Data[pi])<=Eps): #and (j!=pi):
M.append(j)
if len(M)>=MinPts:
for t in M:
if t not in N:
N.append(t)
# 若pi不属于任何簇,C[pi] == -1说明C中第pi个值没有改动
if C[pi] == -1:
C[pi] = k
# 剩余点既不是核心点也不是边界点
else:
C[p] = -1
return C
dataSet = loadDataSet(r'D:\Deeplearning\788points.txt', splitChar=',')
C = dbscan(dataSet, 2, 14)
print(C)
x = []
y = []
for data in dataSet:
x.append(data[0])
y.append(data[1])
plt.figure(figsize=(8, 6), dpi=80)
plt.scatter(x,y, c=C, marker='o')
plt.show()
结果如下:
2.2 使用sklearn实现
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
%matplotlib inline
X1, y1=datasets.make_circles(n_samples=5000, factor=.6,
noise=.05)
X2, y2 = datasets.make_blobs(n_samples=1000, n_features=2, centers=[[1.2,1.2]], cluster_std=[[.1]],
random_state=9)
X = np.concatenate((X1, X2))
plt.scatter(X[:, 0], X[:, 1], marker='o')
plt.show()
y_pred = DBSCAN(eps = 0.1, min_samples = 10).fit_predict(X)
plt.scatter(X[:, 0], X[:, 1], c=y_pred)
plt.show()
结果如下: