(數據科學學習手札15)DBSCAN密度聚類法原理簡介&Python與R的實現


DBSCAN算法是一種很典型的密度聚類法,它與K-means等只能對凸樣本集進行聚類的算法不同,它也可以處理非凸集。

關於DBSCAN算法的原理,筆者覺得下面這篇寫的甚是清楚練達,推薦大家閱讀:

https://www.cnblogs.com/pinard/p/6208966.html

DBSCAN的主要優點有:

    1) 可以對任意形狀的稠密數據集進行聚類,相對的,K-Means之類的聚類算法一般只適用於凸數據集。

    2) 可以在聚類的同時發現異常點,對數據集中的異常點不敏感。

    3) 聚類結果沒有偏倚,相對的,K-Means之類的聚類算法初始值對聚類結果有很大影響。

DBSCAN的主要缺點有:

    1)如果樣本集的密度不均勻、聚類間距差相差很大時,聚類質量較差,這時用DBSCAN聚類一般不適合。

    2) 如果樣本集較大時,聚類收斂時間較長,此時可以對搜索最近鄰時建立的KD樹或者球樹進行規模限制來改進。

    3) 調參相對於傳統的K-Means之類的聚類算法稍復雜,主要需要對距離閾值ϵ,鄰域樣本數閾值MinPts聯合調參,不同的參數組合對最后的聚類效果有較大影響。

 

R中的fpc包中封裝了dbscan(data,eps,MinPts),其中data為待聚類的數據集,eps為距離閾值ϵ,MinPts為樣本數閾值,這三個是必須設置的參數,無缺省項。

一、三種聚類算法在非凸樣本集上的性能表現

下面我們以正弦函數為材料構造非凸樣本集,分別使用DBSCAN、K-means、K-medoids算法進行聚類,並繪制最終的聚類效果圖:

library(fpc)
library(cluster)

#構造非凸樣本集
x1 <- seq(0,pi,0.01)
y1 <- sin(x1)+0.06*rnorm(length(x1))
y2 <- sin(x1)+0.06*rnorm(length(x1))+0.6
plot(x1,y1,ylim=c(0,2.0))
points(x1,y2)
c1 <- c(x1,x1)
c2 <- c(y1,y2)
data1 <- as.matrix(cbind(c1,c2))

構造的樣本集如下:

接着我們依次使用上述三種聚類算法:

#分別繪制三種聚類算法的聚類效果圖
par(mfrow=c(1,3))
#DBSCAN聚類法
db <- dbscan(data1,eps=0.2,MinPts = 5)
db$cluster
plot(data1,col=db$cluster)
title('DBSCAN Cluster')
#K-means聚類法
km <- kmeans(data1,centers=2)
km$cluster
plot(data1,col=km$cluster)
title('K-means Cluster')
#K-medoids聚類法
pm <- pam(data1,k=2)
pm$clustering
plot(data1,col=pm$clustering)
title('K-medoids Cluster')

具體的聚類效果如下:

可以看出,在對非凸樣本集的聚類上,DBSCAN效果非常好,而另外兩種專門處理凸集的聚類算法就遇到了麻煩。

 

二、DBSCAN算法在常規凸樣本集上的表現

 上面我們研究了DBSCAN算法在非凸樣本集上的表現,比K-means和K-medoids明顯優秀很多,下面我們構造一個10維的凸樣本集,具體的代碼和聚類結果如下:

> library(fpc)
> library(Rtsne)
> 
> #創建待聚類數據集
> data1 <- matrix(rnorm(10000,0,0.6),nrow=1000)
> data2 <- matrix(rnorm(10000,1,0.6),nrow=1000)
> 
> data <- rbind(data1,data2)
> 
> #對原高維數據集進行降維
> tsne <- Rtsne(data)
> 
> par(mfrow=c(4,4))
> for(i in 1:16){
+   #進行DBSCAN聚類
+   db <- dbscan(data,eps=1.1+i*0.025,MinPts = 25)
+   #繪制聚類效果圖
+   plot(tsne$Y[,1],tsne$Y[,2],col=db$cluster)
+   title(paste('eps=',as.character(1.1+i*0.025),sep=''))
+   print(paste('eps=',as.character(1.1+i*0.025)))
+   print(table(db$cluster))
+ }
[1] "eps= 1.125"

   0 
2000 
[1] "eps= 1.15"

   0    1    2 
1950   26   24 
[1] "eps= 1.175"

   0    1    2 
1920   59   21 
[1] "eps= 1.2"

   0    1    2 
1834  120   46 
[1] "eps= 1.225"

   0    1    2 
1682  177  141 
[1] "eps= 1.25"

   0    1    2 
1515  250  235 
[1] "eps= 1.275"

   0    1    2 
1305  344  351 
[1] "eps= 1.3"

   0    1    2 
1163  425  412 
[1] "eps= 1.325"

  0   1   2 
989 521 490 
[1] "eps= 1.35"

  0   1   2 
854 596 550 
[1] "eps= 1.375"

  0   1   2 
707 670 623 
[1] "eps= 1.4"

  0   1   2 
572 732 696 
[1] "eps= 1.425"

  0   1   2 
500 766 734 
[1] "eps= 1.45"

   0    1 
 420 1580 
[1] "eps= 1.475"

   0    1 
 355 1645 
[1] "eps= 1.5"

   0    1 
 285 1715 

 

可以看出,DBSCAN雖然性能優越,但是涉及到有些麻煩的調參數的過程,需要進行很多次的試探,沒有K-means和K-medoids來的方便快捷。

 

Python

在Python中,DBSCAN算法集成在sklearn.cluster中,我們利用datasets構造兩個非凸集和一個凸集,效果如下:

 

from sklearn import datasets
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.pyplot import style
from sklearn.cluster import KMeans,DBSCAN

style.use('ggplot')
'''構造樣本集'''
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='*')
plt.title('Samples')

 

 

分別使用K-means和DBSCAN對上述樣本集進行聚類,效果如下:

'''利用K-means'''
km = KMeans(n_clusters=3).fit_predict(X)
col = [(['red','green','blue','yellow','grey','purple'])[i] for i in km]

plt.figure(figsize=(16,8))
plt.subplot(121)
plt.scatter(X[:, 0], X[:, 1], marker='*',c=col)
plt.title('K-means')

'''利用DBSCAN'''
db = DBSCAN(eps = 0.12, min_samples = 19).fit_predict(X)
col = [(['red','green','blue','yellow'])[i] for i in db]
plt.subplot(122)
plt.scatter(X[:, 0], X[:, 1], marker='*',c=col)
plt.title('DBSCAN')

 對DBSCAN中的參數eps(超球體半徑)進行試探:

'''對eps進行試探性調整'''
plt.figure(figsize=(15,15))
for i in range(9):
    db = DBSCAN(eps = 0.05+i*0.04, min_samples = 19).fit_predict(X)
    col = [(['red','green','blue','yellow','purple','aliceblue','antiquewhite','black','blueviolet','cyan','darkgray'])[i] for i in db]
    plt.subplot(331+i)
    plt.scatter(X[:, 0], X[:, 1], marker='*',c=col)
    plt.title('eps={}'.format(str(round(0.05+i*0.04,2))))

對DBSCAN中的參數MinPts(核心點內最少樣本個數)進行試探:

'''對MinPts進行試探性調整'''
plt.figure(figsize=(15,15))
for i in range(9):
    db = DBSCAN(eps = 0.12, min_samples = 10+i*4).fit_predict(X)
    col = [(['red','green','blue','yellow','purple','aliceblue','antiquewhite','black','blueviolet','cyan','darkgray'])[i] for i in db]
    plt.subplot(331+i)
    plt.scatter(X[:, 0], X[:, 1], marker='*',c=col)
    plt.title('MinPts={}'.format(str(round(10+i*4))))

可見參數的設置對聚類效果的影響非常顯著。

以上就是DBSCAN的簡單介紹,若發現錯誤望指出。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM