1. 程式人生 > >聚類演算法之DBSCAN演算法之一:經典DBSCAN

聚類演算法之DBSCAN演算法之一:經典DBSCAN

DBSCAN是基於密度空間的聚類演算法,與KMeans演算法不同,它不需要確定聚類的數量,而是基於資料推測聚類的數目,它能夠針對任意形狀產生聚類。

1.epsilon-neighborhood

epsoiln-neighborhood(簡稱e-nbhd)可理解為密度空間,表示半徑為e且含有若干個點的nbhd,密度等於包含點的個數/空間大小。圖中中心點是(3,2),半徑epsilon是0.5
在這裡插入圖片描述
根據式子密度=點的個數/面積,可以計算得到上圖中密度=31/2pi(0.5)*(0.5)=62/pi,這個數字本身意義不大,但通過計算某一小區域的密度,橫向對比可以得知整個區域的密度分佈,由此相近的點可聚類到同一區域內。

2.DBSCAN

DBSCAN演算法需要首先確定兩個引數:
(1)epsilon:在一個點周圍鄰近區域的半徑
(2)minPts:鄰近區域內至少包含點的個數
根據以上兩個引數,結合epsilon-neighborhood的特徵,可以把樣本中的點分成三類:

  1. 核點(core point):滿足NBHD(p,epsilon)>=minPts,則為核樣本點
  2. 邊緣點(border point):NBHD(p,epsilon)<minPts,但是該點可由一些核點獲得(density-reachable或者directly-reachable
  3. 離群點(Outlier):既不是核點也不是邊緣點,則是不屬於這一類的點

注:邊緣點density-reachable是指存在當前類中其他點作為核點所在的類中。例如,朋友的朋友(可以是n多個)也是朋友。如下圖,黃圈右下角的點即為density-reachabledirectly-reachable的點即為NBHD中的點。
在這裡插入圖片描述

3.DBSCAN步驟

結合2中內容,DBSCAN的一般步驟是:(在已知epsilon和minPts的前提下)

  1. 任意選擇一個點(既沒有指定到一個類也沒有特定為外圍點),計算它的NBHD(p,epsilon)判斷是否為核點。如果是,在該點周圍建立一個類,否則,設定為外圍點。
  2. 遍歷其他點,直到建立一個類。把directly-reachable
    的點加入到類中,接著把density-reachable的點也加進來。如果標記為外圍的點被加進來,修改狀態為邊緣點。
  3. 重複步驟1和2,直到所有的點滿足在類中(核點或邊緣點)或者為外圍點

4.程式碼實現

思路:讀取資料,選擇Grocery和Milk兩列作為訓練樣本,對資料歸一化(特徵標準化),使用dbscan聚類,並作圖。
特徵標準化是歸一化中常用的手段,特點是零均值和單位方差,對於正態分佈的資料擬合很好。

# coding=utf-8
import numpy as np
from scipy.spatial.distance import cdist
import matplotlib.pyplot as plt
import seaborn as sns
sns.set()

from sklearn.cluster import DBSCAN
from sklearn.preprocessing import StandardScaler
import pandas as pd

data = pd.read_csv("data/wholesale.csv")
data.drop(["Channel", "Region"], axis=1, inplace=True)

data = data[["Grocery", "Milk"]]
data = data.as_matrix().astype("float32", copy=False)#convert to array

#資料預處理,特徵標準化,每一維是零均值和單位方差
stscaler = StandardScaler().fit(data)
data = stscaler.transform(data)

#畫出x和y的散點圖
plt.scatter(data[:, 0], data[:, 1])
plt.xlabel("Groceries")
plt.ylabel("Milk")
plt.title("Wholesale Data - Groceries and Milk")
plt.savefig("results/wholesale.png", format="PNG")

dbsc = DBSCAN(eps=0.5, min_samples=15).fit(data)

labels = dbsc.labels_ #聚類得到每個點的聚類標籤 -1表示噪點
#print(labels)
core_samples = np.zeros_like(labels, dtype=bool) #構造和labels一致的零矩陣,值是false
core_samples[dbsc.core_sample_indices_] = True
#print(core_samples)


unique_labels = np.unique(labels)
colors = plt.cm.Spectral(np.linspace(0, 1, len(unique_labels))) #linespace返回在【0,1】之間均勻分佈數字是len個,Sepectral生成len個顏色


#print(zip(unique_labels,colors))
for (label, color) in zip(unique_labels, colors):
    class_member_mask = (labels == label)
    print(class_member_mask&core_samples)
    xy = data[class_member_mask & core_samples]
    plt.plot(xy[:, 0], xy[:, 1], 'o', markerfacecolor=color, markersize=10)

    xy2 = data[class_member_mask & ~core_samples]
    plt.plot(xy2[:, 0], xy2[:, 1], 'o', markerfacecolor=color, markersize=5)
plt.title("DBSCAN on Wholsesale data")
plt.xlabel("Grocery (scaled)")
plt.ylabel("Milk (scaled)")
plt.savefig("results/(0.9,15)dbscan_wholesale.png", format="PNG")

5.分析及總結

(1)epsilon不變的情況下,調整minPts的大小,則minPts越大,NBHD越密集,產生離群點越多。以下四幅圖對應epsilon和minPts分別是(0.5,15),(0.5,20),(0.5,40),(0.5,60)

在這裡插入圖片描述
在這裡插入圖片描述
(2)在minPts不變的情況下,epsilon越小,聚類越密集,產生離群點越多。以下三幅圖分別是(0.5,15),(0.8,15),(0.9,15)
在這裡插入圖片描述
epsilon越小,minPts越多,則密度越高,產生聚類越密集。

DBSCAN最大的特點是事先不必確定聚類的種類,通過基於密度的方法,聚類並找出離群點。不僅需要對大部分在類中的點分析,也需要對離群點分析(例如統計分析被木馬程式入侵的電腦,本例中牛奶需求水平較高的顧客…)

6.參考連結

https://blog.dominodatalab.com/topology-and-density-based-clustering/?tdsourcetag=s_pcqq_aiomsg