文章目录
在现实生活中收集到的好多数据是没有标签的,要在上面做一些数据挖掘和分析的工作,首先能用到的就是通过聚类的方式来得到数据的类别。然后再去发现一些相关的知识。因此,近期准备将各种聚类算法进行学习。
k-means算法大家都已经很熟悉了,它是基于距离计算的聚类方法,而且它旨在发现球状簇。若数据的分别刚好呈现的是其他的形状(如下图1),那么我们就要用到了基于密度的聚类方法,也就是今天我们学习的DBSCAN(Density-Based Spatial Clustering of Applications with Noise,具有噪声的基于密度的聚类方法)算法。该算法可以发现任意形状的簇。
一、基本思想
根据下面的图形象来说,我们可以认为这是系统在众多样本点中随机选中一个,围绕这个被选中的样本点画一个圆,规定这个圆的半径以及圆内最少包含的样本点,如果在指定半径内有足够多的样本点在内,那么这个圆圈的圆心就转移到这个内部样本点,继续去圈附近其它的样本点,类似传销一样,继续去发展下线。等到这个滚来滚去的圈发现所圈住的样本点数量少于预先指定的值,就停止了。那么我们称最开始那个点为核心点,如A,停下来的那个点为边界点,如B、C,没得滚的那个点为离群点,如N
1、原理
DBSCAN算法将数据点分为三类:
- 核心点:在半径Eps内含有超过MinPts数目的点
- 边界点:在半径Eps内点的数量小于MinPts,但是落在核心点的邻域内
- 噪音点:既不是核心点也不是边界点的点
2、参数选择
所以,首先我们需要指定半径Eps和MinPts的值。
- 半径:半径是最难指定的 ,大了,圈住的就多了,簇的个数就少了;反之,簇的个数就多了,这对我们最后的结果是有影响的。我们这个时候K距离可以帮助我们来设定半径r,也就是要找到突变点,比如:
以上虽然是一个可取的方式,但是有时候比较麻烦 ,大部分还是都试一试进行观察,用k距离需要做大量实验来观察,很难一次性把这些值都选准。
- MinPts:这个参数就是圈住的点的个数,也相当于是一个密度,一般这个值都是偏小一些,然后进行多次尝试
3、算法的伪代码
4、python代码实现
import numpy as np
import random
import matplotlib.pyplot as plt
import time
def findNeighbor(j,X,eps):
"查找点j的邻域"
N=[]
for p in range(X.shape[0]): #找到所有领域内对象
temp=np.sqrt(np.sum(np.square(X[j]-X[p]))) #欧氏距离
if(temp<=eps):
N.append(p)
return N
def dbscan(X,eps,min_Pts):
k=-1
NeighborPts=[] #array,某点领域内的对象
Ner_NeighborPts=[]
fil=[] #初始时已访问对象列表为空
gama=[x for x in range(len(X))] #初始时将所有点标记为未访问
cluster=[-1 for y in range(len(X))]
while len(gama)>0:
j=random.choice(gama) #随机选择一个点
gama.remove(j) #未访问列表中移除
fil.append(j) #添加入访问列表
NeighborPts=findNeighbor(j,X,eps)
if len(NeighborPts) < min_Pts:
cluster[j]=-1 #标记为噪声点
else:
k=k+1
cluster[j]=k
for i in NeighborPts:
if i not in fil:
gama.remove(i)
fil.append(i)
Ner_NeighborPts=findNeighbor(i,X,eps)
if len(Ner_NeighborPts) >= min_Pts:
for a in Ner_NeighborPts:
if a not in NeighborPts:
NeighborPts.append(a)
if (cluster[i]==-1):
cluster[i]=k
return cluster
二、DBSCAN算法可视化的迭代算法实现
国外有一个特别有意思的网站:https://www.naftaliharris.com/blog/visualizing-dbscan-clustering/它可以把我们DBSCAN的迭代过程动态图画出来。
如果minPoints参数设置再大一点,那么这个笑脸可能会更好看。没有颜色标注的就是圈不到的样本点,也就是离群点,DBSCAN聚类算法在检测离群点的任务上也有较好的效果。如果是传统的Kmeans聚类,我们也来看一下效果:
三、案例及使用
sklearn中DBSCAN算法使用
DBSCAN(eps=0.5, min_samples=5, metric='euclidean', algorithm='auto', leaf_size=30, p=None, n_jobs=1)
参数说明:
- eps:两个样本之间的最大距离,即扫描半径
- min_samples :作为核心点的话邻域(即以其为圆心,eps为半径的圆,含圆上的点)中的最小样本数(包括点本身)。
- metric :度量方式,默认为欧式距离,还有metric=‘precomputed’(稀疏半径邻域图)
- algorithm:近邻算法求解方式,有四种:‘auto’, ‘ball_tree’, ‘kd_tree’, ‘brute’
- leaf_size:叶的大小,在使用BallTree or cKDTree近邻算法时候会需要这个参数
- n_jobs :使用CPU格式,-1代表全开
1、采用datasets中的鸢尾花数据集
首先我们先看一下,原始的数据集根据第2和第3列的特征的类别分布
import numpy as np
import sklearn.cluster as skc
from sklearn import metrics
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
#导入数据集
X=load_iris().data
plt.scatter(X[:, 2], X[:, 3],c=load_iris().target)
plt.show()
如图所示:
使用DBSCAN算法进行聚类,代码如下:
import numpy as np
import sklearn.cluster as skc
from sklearn import metrics
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
#导入数据集
X=load_iris().data
# plt.scatter(X[:, 2], X[:, 3],c=load_iris().target)
# plt.show()
# DBSCAN聚类
db= skc.DBSCAN(eps=0.9, min_samples=10).fit(X[:,2:3])
label_pred = db.labels_
plt.scatter(X[:, 2], X[:, 3],c=label_pred)
plt.show()
参数eps=0.9, min_samples=10的运行结果如下
eps=0.2, min_samples=4
2、采用大学生校园网的日志数据
现有大学校园网的日志数据,290条大学生的校园网使用情况数据,数据包括用户ID,设备的MAC地址,IP地址,开始上网时间,停止上网时间,上网时长,校园网套餐等。利用已有数据,分析学生上网的模式。
数据集是如下图所示:
对学生上网时间进行聚类
import numpy as np
import sklearn.cluster as skc
from sklearn import metrics
import matplotlib.pyplot as plt
mac2id = dict()
onlinetimes = []
f = open('TestData.txt', encoding='utf-8')
for line in f:
mac = line.split(',')[2]
onlinetime = int(line.split(',')[6])
starttime = int(line.split(',')[4].split(' ')[1].split(':')[0])
if mac not in mac2id:
mac2id[mac] = len(onlinetimes)
onlinetimes.append((starttime, onlinetime))
else:
onlinetimes[mac2id[mac]] = [(starttime, onlinetime)]
real_X = np.array(onlinetimes).reshape((-1, 2))
X = real_X[:, 0:1]
db = skc.DBSCAN(eps=0.01, min_samples=20).fit(X) # dbscan聚类操作
labels = db.labels_ # 获取聚类标签
print('Labels:')
print(labels)
raito = len(labels[labels[:] == -1]) / len(labels) # 标签为-1的为噪声点
print('Noise raito:', format(raito, '.2%')) # 打印噪声点比例
n_clusters_ = len(set(labels)) - (1 if -1 in labels else 0)
print('Estimated number of clusters: %d' % n_clusters_)
print("Silhouette Coefficient: %0.3f" % metrics.silhouette_score(X, labels))
for i in range(n_clusters_):
print('Cluster ', i, ':')
print(list(X[labels == i].flatten()))
plt.hist(X, 24)
plt.show()
结果如下图所示:
从结果可以看出,学生的上网时间被分为了6个类别,分别为:22点/23点/21点/20点/7点/8点
四、常用的评估方法:轮廓系数
当没有数据的基准可用时,我们必须使用内在方法来评估聚类的质量。一般而言,内在方法通过考察簇的分离情况和簇的紧凑情况来评估聚类。许多内在方法都利用数据集的对象之间的相似性度量。
聚类算法中最常用的评估方法——轮廓系数(Silhouette Coefficient),它结合内聚度和分离度两种因素。可以用来在相同原始数据的基础上用来评价不同算法、或者算法不同运行方式对聚类结果所产生的影响。
-
计算样本 i i i到同簇其它样本到平均距离 a i a_{i} ai。 a i a_{i} ai越小,说明样本 i i i越应该被聚类到该簇(将 a i a_{i} ai称为样本 i i i到簇内不相似度)。
-
计算样本i到其它某簇 C j C_{j} Cj的所有样本的平均距离 b i j b_{ij} bij,称为样本 i i i与簇 C j C_{j} Cj的不相似度。定义为样本i的簇间不相似度: b i = m i n ( b i 1 , b i 2 , . . . , b i k 2 ) b_{i}=min(b_{i1},b_{i2},...,b_{ik_{2}}) bi=min(bi1,bi2,...,bik2)
-
s i s_{i} si接近1,则说明样本i聚类合理
-
s i s_{i} si接近-1,则说明样本i更应该分类到另外的簇
-
若 s i s_{i} si近似为0,则说明样本i在两个簇的边界上
总结
- DBSCAN不需要事先知道要形成的簇类的数量,通过参数自动得到簇数;
- DBSCAN能够识别出噪声点。对离群点有较好的鲁棒性,甚至可以检测离群点;
- 输入参数敏感,确定参数Eps , MinPts困难 ,若选取不当 ,将造成聚类质量下降;
- 由于经典的DBSCAN算法中参数Eps和MinPts(全局性表征密度的参数)在聚类过程中是不变的,因此当各个类的密度不均匀,或类间的距离相差很大时,聚类的质量较差
参考资料:
- DBSCAN聚类︱scikit-learn中一种基于密度的聚类方式https://blog.csdn.net/sinat_26917383/article/details/74932608#2_33
-
- https://blog.csdn.net/huacha__/article/details/81094891