聚類演算法之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的特徵,可以把樣本中的點分成三類:
- 核點(core point):滿足NBHD(p,epsilon)>=minPts,則為核樣本點
- 邊緣點(border point):NBHD(p,epsilon)<minPts,但是該點可由一些核點獲得(density-reachable或者directly-reachable)
- 離群點(Outlier):既不是核點也不是邊緣點,則是不屬於這一類的點
注:邊緣點density-reachable是指存在當前類中其他點作為核點所在的類中。例如,朋友的朋友(可以是n多個)也是朋友。如下圖,黃圈右下角的點即為density-reachable,directly-reachable的點即為NBHD中的點。
3.DBSCAN步驟
結合2中內容,DBSCAN的一般步驟是:(在已知epsilon和minPts的前提下)
- 任意選擇一個點(既沒有指定到一個類也沒有特定為外圍點),計算它的NBHD(p,epsilon)判斷是否為核點。如果是,在該點周圍建立一個類,否則,設定為外圍點。
- 遍歷其他點,直到建立一個類。把directly-reachable
- 重複步驟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