天天看點

DBSCAN聚類原理及詳解

DBSCAN聚類

(1)DBSCAN簡介

DBSCAN是一個比較有代表性的基于密度的聚類算法。與劃分和層次聚類方法不同,它将簇定義為密度相連的點的最大集合,能夠把具有足夠高密度的區域劃分為簇,并可在噪聲的空間資料庫中發現任意形狀的聚類。

DBSCAN中的幾個定義:

Ε鄰域:給定對象半徑為Ε内的區域稱為該對象的Ε鄰域;

核心對象:如果給定對象Ε鄰域内的樣本點數大于等于min_samples,則稱該對象為核心對象;

直接密度可達:對于樣本集合D,如果樣本點q在p的Ε鄰域内,并且p為核心對象,那麼對象q從對象p直接密度可達。

密度可達:對于樣本集合D,給定一串樣本點p1,p2….pn,p= p1,q= pn,假如對象pi從pi-1直接密度可達,那麼對象q從對象p密度可達。

密度相連:存在樣本集合D中的一點o,如果對象o到對象p和對象q都是密度可達的,那麼p和q密度相聯。

DBSCAN中兩個重要參數eps ,min_samples

(1)eps:越大類别數越少——參數越大的話,多個簇和大部分對象會歸并到同一個簇中

(2)需要對eps和min_samples手動設定,eps預設值是0.5,表示的是半徑

如果 附近點的數量 ≥min_samples,則目前點與其附近點形成一個簇,并且出發點被标記為已通路(visited),如果 附近點的數量 <min_samples,則該點暫時被标記作為噪聲點。

(3)掃描半徑 (eps)和最小包含點數(min_samples), 給定點在鄰域内成為核心對象的最小鄰域點數:min_samples

(4)min_samples的選取有一個指導性的原則(a rule of thumb),min_samples ≥dim+1,其中dim表示待聚類資料的次元。min_samples設定為1是不合理的,因為設定為1,則每個獨立點都是一個簇,min_samples≤2時,與層次距離最近鄰域結果相同,是以,min_samples必須選擇大于等于3的值。若該值選取過小,則稀疏簇中結果由于密度小于min_samples,進而被認為是邊界點兒不被用于在類的進一步擴充;若該值過大,則密度較大的兩個鄰近簇可能被合并為同一簇。是以,該值是否設定适當會對聚類結果造成較大影響,如果min_samples不變,Eps取得值過大,會導緻大多數點都聚到同一個簇中,Eps過小,會導緻一個簇的分裂;如果Eps不變,min_samples的值取得過大,會導緻同一個簇中點被标記為離群點,min_samples過小,會導緻發現大量的核心點。

(2)實作步驟

導入使用的子產品和庫

DBSCAN聚類原理及詳解

将樣本點設為1000個, 參數設定為noise=0.1,用datasets.make_blobs函數生成樣本點1000個, 參數設定為n_features=2, centers=[[1.2,1.2]], cluster_std=0.1,random_state設為學号除以30取餘數

DBSCAN聚類原理及詳解

展示出原始資料如下所示

DBSCAN聚類原理及詳解

先用kmeans實作聚類,看看聚類效果

DBSCAN聚類原理及詳解

用kmeans進行聚類,聚類效果如下,聚成了3類

DBSCAN聚類原理及詳解

Silhouette Coefficient: 0.626(輪廓系數)

然後用DBSCAN聚類,先使用預設參數,觀察聚類效果(預設參數eps=0.5 min_samples=5)

DBSCAN聚類原理及詳解

預設參數聚類結果,因為eps過大,聚成了一類

DBSCAN聚類原理及詳解

然後将參數eps改為0.1,看一下聚類效果

DBSCAN聚類原理及詳解

将EPS=0.1改為0.1,領域減小,類别增多,聚成了3類,聚類結果如下

DBSCAN聚類原理及詳解

将參數eps改為0.1,min_samples改為15,聚類效果如下

DBSCAN聚類原理及詳解

(3)總結

優點

  1. 與K-means方法相比,DBSCAN不需要事先知道要形成的簇類的數量。
  2. 與K-means方法相比,DBSCAN可以發現任意形狀的簇類。
  3. 同時,DBSCAN能夠識别出噪聲點。
  4. DBSCAN對于資料庫中樣本的順序不敏感,即Pattern的輸入順序對結果的影響不大。但是,對于處于簇類之間邊界樣本,可能會根據哪個簇類優先被探測到而其歸屬有所擺動。

    缺點

    (1) DBSCAN不能很好反映高維資料。

    (2)DBSCAN不能很好反映資料集以變化的密度。

    (3)如果樣本集的密度不均勻、聚類間距差相差很大時,聚類品質較差。

    (4)源代碼展示

import numpy as np
import matplotlib.pyplot as plt
from sklearn import metrics
from sklearn import datasets
from sklearn.cluster import DBSCAN

#%% 生成、展示資料
X1,y1=datasets.make_moons(n_samples=1000,noise=0.1,random_state=16050416121%30)#生成兩組資料
X2, y2 = datasets.make_blobs(n_samples=1000, n_features=2, centers=[[1.2,1.2]],#y1y2是标簽
                             cluster_std=[[0.1]], random_state=16050416121%30)
X = np.concatenate((X1, X2))  #縱向拼接
plt.figure(figsize=(10, 7))
plt.title('original data')
plt.plot(X[:, 0], X[:, 1],  'o',markersize=6)
plt.show()

#%% 首先看看K-Means的聚類效果
from sklearn.cluster import KMeans
y_pred = KMeans(n_clusters=3, random_state=9).fit_predict(X)#對x預測
plt.figure(figsize=(10, 7))
plt.scatter(X[:, 0], X[:, 1],s=25, c=y_pred)#顔色
plt.title('k-means:k=3')
plt.show()
print("Silhouette Coefficient: %0.3f"
      % metrics.silhouette_score(X, y_pred))

#%% 那麼如果使用DBSCAN效果如何呢?我們先不調參,直接用預設參數,看看聚類效果,:
db = DBSCAN().fit(X) #用DBSCAN拟合      #0.5 5
core_samples_mask = np.zeros_like(db.labels_, dtype=bool)#核心樣本點 建立一個已知次元的數組,用0填充
core_samples_mask[db.core_sample_indices_] = True #将核心點指派為true
labels = db.labels_
n_clusters_ = len(set(labels)) - (1 if -1 in labels else 0) #
unique_labels = set(labels) #去掉重複的元素
colors = [plt.cm.Spectral(each)   
          for each in np.linspace(0, 1, len(unique_labels))]
#   1)np.linspace 傳回[0,1]之間的len(unique_labels) 個數
#   2)plt.cm 一個顔色映射子產品
#   3)生成的每個colors包含4個值,分别是RGBA:
#    RGBA是代表Red(紅色) Green(綠色) Blue(藍色)和 Alpha的色彩空間,
#    也就是透明度/不透明度
#   4)其實這行代碼的意思就是生成len(unique_labels)個可以和光譜對應的顔色值
plt.figure(figsize=(10, 7))
for k, col in zip(unique_labels, colors): #周遊
    class_member_mask = (labels == k)  #等于k是true
    if k == -1:   #被判定的噪聲點
        cls =  'noise'
        xy = X[class_member_mask] #
        plt.plot(xy[:, 0], xy[:, 1], 'o', markerfacecolor='k',
             markeredgecolor='k', markersize=6,label=cls) #
    else:
        xy = X[class_member_mask & core_samples_mask]   #核心點
        plt.plot(xy[:, 0], xy[:, 1], 'o', markerfacecolor=tuple(col),
             markeredgecolor='k', markersize=10,label= 'class '+ str(k)+' core')
        xy = X[class_member_mask & ~core_samples_mask]  #邊緣點
        plt.plot(xy[:, 0], xy[:, 1], 'o', markerfacecolor=tuple(col),
             markeredgecolor='k', markersize=6,label= 'class '+ str(k)+' border')
plt.legend(loc='best')
plt.title('Estimated number of clusters: %d' % n_clusters_)

plt.show()

#%% 對DBSCAN的兩個關鍵的參數eps和min_samples進行調參!發現,類别數太少,
#需要增加類别數,可以減少eps-鄰域的大小,預設是0.5,減到0.1看看效果,min_samples預設5
db = DBSCAN(eps=0.1).fit(X) #0.1  5
core_samples_mask = np.zeros_like(db.labels_, dtype=bool)
core_samples_mask[db.core_sample_indices_] = True
labels = db.labels_
n_clusters_ = len(set(labels)) - (1 if -1 in labels else 0)
unique_labels = set(labels)
colors = [plt.cm.Spectral(each)   
          for each in np.linspace(0, 1, len(unique_labels))]
plt.figure(figsize=(10, 7))
for k, col in zip(unique_labels, colors):
    class_member_mask = (labels == k)
    if k == -1:   #被判定的噪聲點
        cls =  'noise'  
        xy = X[class_member_mask]
        plt.plot(xy[:, 0], xy[:, 1], 'o', markerfacecolor='k',
             markeredgecolor='k', markersize=6,label=cls)
    else:
        xy = X[class_member_mask & core_samples_mask]   #核心點
        plt.plot(xy[:, 0], xy[:, 1], 'o', markerfacecolor=tuple(col),
             markeredgecolor='k', markersize=12,label='class '+ str(k)+' core')
        xy = X[class_member_mask & ~core_samples_mask]  #邊緣點
        plt.plot(xy[:, 0], xy[:, 1], 'o', markerfacecolor=tuple(col),
             markeredgecolor='k', markersize=6,label='class '+ str(k)+' border')
plt.legend(loc='best')
plt.title('Estimated number of clusters: %d' % n_clusters_)

plt.show()

#%% 對DBSCAN的兩個關鍵的參數eps和min_samples進行調參!發現,類别數太少,
#需要增加類别數,可以增大min_samples值,增大到15,看看效果 0.1 15
y_pred = DBSCAN(eps = 0.1, min_samples = 15).fit_predict(X)
plt.figure(figsize=(10, 7))
plt.scatter(X[:, 0], X[:, 1],s=25, c=y_pred)
plt.title('Estimated number of cluster:%d' )
plt.title('DBSCAN:eps=0.1, min_samples=15')
plt.show()
print("Silhouette Coefficient: %0.3f"
      % metrics.silhouette_score(X, y_pred))

#%% 對DBSCAN的兩個關鍵的參數eps和min_samples進行調參!發現,類别數太少,
#需要增加類别數,可以增大min_samples值,增大到10,看看效果  0.1 10
y_pred = DBSCAN(eps = 0.1, min_samples = 10).fit_predict(X)
plt.figure(figsize=(10, 7))
plt.scatter(X[:, 0], X[:, 1],s=25, c=y_pred)
plt.title('Estimated number of cluster:%d' )
plt.title('DBSCAN:eps=0.1, min_samples=10')
plt.show()
print("Silhouette Coefficient: %0.3f"
      % metrics.silhouette_score(X, y_pred))