DBSCAN(Density-Based Spatial Clustering of Applications with Noise,具有噪声的基于密度的聚类方法)是一种很典型的密度聚类算法,和只适用于凸样本集的K-Means聚类相比,DBSCAN既可以适用于凸样本集,也可以适用于非凸样本集。
DBSCAN一般假定类别可以通过样本分布的紧密程度决定。同一类别的样本,他们之间的紧密相连的,也就是说,在该类别任意样本周围不远处一定有同类别的样本存在。通过将紧密相连的样本划为一类,这样就得到了一个聚类类别。通过将所有各组紧密相连的样本划为各个不同的类别,则我们就得到了最终的所有聚类类别结果。
红色的点都是核心对象,黑色的样本是非核心对象。所有核心对象密度直达的样本在以红色核心对象为中心的超球体内,如果不在超球体内,则不能密度直达。图中用绿色箭头连起来的核心对象组成了密度可达的样本序列。
1、特点
和传统的K-Means算法相比,DBSCAN最大的不同就是不需要输入类别数k,当然它最大的优势是可以发现任意形状的聚类簇,而不是像K-Means,一般仅仅使用于凸的样本集聚类。同时它在聚类的同时还可以找出异常点,这点和BIRCH算法类似。
那么我们什么时候需要用DBSCAN来聚类呢?一般来说,如果数据集是稠密的,并且数据集不是凸的,那么用DBSCAN会比K-Means聚类效果好很多。如果数据集不是稠密的,则不推荐用DBSCAN来聚类。
2、Python实现
2.1 引入相关的模块
import numpy as np
from sklearn.cluster import DBSCAN
from sklearn import metrics
import seaborn as sns
import pandas as pd
from sklearn.datasets.samples_generator import make_blobs
from sklearn.preprocessing import StandardScaler
2.2查看样本的分布
我们对data的分布用seaborn进行查看。
data=[
[-2.68420713,1.469732895],[-2.71539062,-0.763005825],[-2.88981954,-0.618055245],[-2.7464372,-1.40005944],[-2.72859298,1.50266052],
[-2.27989736,3.365022195],[-2.82089068,-0.369470295],[-2.62648199,0.766824075],[-2.88795857,-2.568591135],[-2.67384469,-0.48011265],
[-2.50652679,2.933707545],[-2.61314272,0.096842835],[-2.78743398,-1.024830855],[-3.22520045,-2.264759595],[-2.64354322,5.33787705],
[-2.38386932,6.05139453],[-2.6225262,3.681403515],[-2.64832273,1.436115015],[-2.19907796,3.956598405],[-2.58734619,2.34213138],
[1.28479459,3.084476355],[0.93241075,1.436391405],[1.46406132,2.268854235],[0.18096721,-3.71521773],[1.08713449,0.339256755],
[0.64043675,-1.87795566],[1.09522371,1.277510445],[-0.75146714,-4.504983795],[1.04329778,1.030306095],[-0.01019007,-3.242586915],
[-0.5110862,-5.681213775],[0.51109806,-0.460278495],[0.26233576,-2.46551985],[0.98404455,-0.55962189],[-0.174864,-1.133170065],
[0.92757294,2.107062945],[0.65959279,-1.583893305],[0.23454059,-1.493648235],[0.94236171,-2.43820017],[0.0432464,-2.616702525],
[4.53172698,-0.05329008],[3.41407223,-2.58716277],[4.61648461,1.538708805],[3.97081495,-0.815065605],[4.34975798,-0.188471475],
[5.39687992,2.462256225],[2.51938325,-5.361082605],[4.9320051,1.585696545],[4.31967279,-1.104966765],[4.91813423,3.511712835],
[3.66193495,1.0891728],[3.80234045,-0.972695745],[4.16537886,0.96876126],[3.34459422,-3.493869435],[3.5852673,-2.426881725],
[3.90474358,0.534685455],[3.94924878,0.18328617],[5.48876538,5.27195043],[5.79468686,1.139695065],[3.29832982,-3.42456273]
]
data = pd.DataFrame(data)
data.columns=['x','y']
sns.relplot(x="x",y="y",data=data)
可以看出来,样本数据可以分为三类。
接下来我通过DBSCAN算法如何把这三个分类找出来。
2.3 建立一个简单的模型
db = DBSCAN(eps=1, min_samples=5).fit(data) #DBSCAN聚类方法 还有参数,matric = ""距离计算方法
data['labels'] = db.labels_ #和X同一个维度,labels对应索引序号的值 为她所在簇的序号。若簇编号为-1,表示为噪声,我们把标签放回到data数据集中方便画图
labels = db.labels_
raito = data.loc[data['labels']==-1].x.count()/data.x.count() #labels=-1的个数除以总数,计算噪声点个数占总数的比例
print('噪声比:', format(raito, '.2%'))
n_clusters_ = len(set(labels)) - (1 if -1 in labels else 0) # 获取分簇的数目
print('分簇的数目: %d' % n_clusters_)
print("轮廓系数: %0.3f" % metrics.silhouette_score(data, labels)) #轮廓系数评价聚类的好坏
sns.relplot(x="x",y="y", hue="labels",data=data)
噪声比: 28.33%
分簇的数目: 4
轮廓系数: 0.332
其中-1的点表示异常点,即噪声。可以看出来有17个点是噪声,所以噪声比就是17/60=28.33%,自动分为4个类别。
这个分类和我们的实际还是有一点的差距的。接下来我们调整参数,看看效果会不会好一点。
2.4 调参
我们尝试对eps和min_samples的各自参数组合进行拟合计算。
rs= []#存放各个参数的组合计算出来的模型评估得分和噪声比
eps = np.arange(0.2,4,0.2) #eps参数从0.2开始到4,每隔0.2进行一次
min_samples=np.arange(2,20,1)#min_samples参数从2开始到20
best_score=0
best_score_eps=0
best_score_min_samples=0
for i in eps:
for j in min_samples:
try:#因为不同的参数组合,有可能导致计算得分出错,所以用try
db = DBSCAN(eps=i, min_samples=j).fit(data)
labels= db.labels_#得到DBSCAN预测的分类便签
k=metrics.silhouette_score(data,labels) #轮廓系数评价聚类的好坏,值越大越好
raito = len(labels[labels[:] == -1]) / len(labels) #计算噪声点个数占总数的比例
n_clusters_ = len(set(labels)) - (1 if -1 in labels else 0) # 获取分簇的数目
rs.append([i,j,k,raito,n_clusters_])
if k>best_score:
best_score=k
best_score_eps=i
best_score_min_samples=j
except:
db='' #这里用try就是遍历i,j 计算轮廓系数会出错的,出错的就跳过
else:
db=''
rs= pd.DataFrame(rs)
rs.columns=['eps','min_samples','score','raito','n_clusters']
sns.relplot(x="eps",y="min_samples", size='score',data=rs)
sns.relplot(x="eps",y="min_samples", size='raito',data=rs)
不同的参数组合的得分情况,得分用的是轮廓系数,此系数评价聚类的好坏,值越大越好,值越大,图中的点就越大。
我们也可以看看噪声比,噪声比越小越好。
通过上图可以看出来,同时参考得分越大越好,噪声比越小越好,eps取值在1-2.5之间,min_samples取值在3-15之间,并且min_samples的影响不大。有很多的参数组合的结果的差不多,我们在合适的组合中随便选择一组。
修改参数eps=1.3 min_samples=3,运行下列代码:
db = DBSCAN(eps=1.5, min_samples=3).fit(data) #DBSCAN聚类方法 还有参数,matric = ""距离计算方法
data['labels'] = db.labels_ #和X同一个维度,labels对应索引序号的值 为她所在簇的序号。若簇编号为-1,表示为噪声,我们把标签放回到data数据集中方便画图
labels = db.labels_
raito = data.loc[data['labels']==-1].x.count()/data.x.count() #labels=-1的个数除以总数,计算噪声点个数占总数的比例
print('噪声比:', format(raito, '.2%'))
n_clusters_ = len(set(labels)) - (1 if -1 in labels else 0) # 获取分簇的数目
print('分簇的数目: %d' % n_clusters_)
print("轮廓系数: %0.3f" % metrics.silhouette_score(data, labels)) #轮廓系数评价聚类的好坏
sns.relplot(x="x",y="y", hue="labels",data=data)
噪声比: 8.33%
分簇的数目: 3
轮廓系数: 0.376
比较好的自动分为3类了。
3、DBSCAN算法的优缺点
3.1 DBSCAN的主要优点有:
- 可以对任意形状的稠密数据集进行聚类,相对的,K-Means之类的聚类算法一般只适用于凸数据集。
- 可以在聚类的同时发现异常点,对数据集中的异常点不敏感。
- 聚类结果没有偏倚,相对的,K-Means之类的聚类算法初始值对聚类结果有很大影响。
3.2 DBSCAN的主要缺点有:
- 如果样本集的密度不均匀、聚类间距差相差很大时,聚类质量较差,这时用DBSCAN聚类一般不适合。
- 如果样本集较大时,聚类收敛时间较长,此时可以对搜索最近邻时建立的KD树或者球树进行规模限制来改进。
- 调参相对于传统的K-Means之类的聚类算法稍复杂,主要需要对距离阈值ϵ,邻域样本数阈值MinPts联合调参,不同的参数组合对最后的聚类效果有较大影响。