算法原理
- DBSCAN(Density-Based Spatial Clustering of Applications with Noise)是一个比较有代表性的基于密度的聚类算法,能够将具有高密度的区域划分为簇,并且能够在具有噪声的样本中发现任意形状的簇。
- DBSCAN算法需要选择一种距离度量,计算两点之间的距离,反映点之间的密度,一般选择欧氏距离。
- 该算法需要输入两个参数:一个参数是半径(eps),表示以给定点P为中心的圆形邻域半径;另一个参数是以P为中心的圆形领域内最少包含点的数量(MinPts)。如果以点P为圆心、eps为半径的圆形领域内点的数量不少于MinPts,则成点P为核心点。
- 参数选取方法
首先介绍k-距离概念。k-距离是指:计算给定数据点P(i)到数据集D(除去P(i))中各点的距离,按照升序排列,取第k个值作为该点的k-距离。对于聚类集合中的每个点P(i)都计算k-距离,得到集合E={e(1),e(2),…,e(n)}。
(1)根据经验计算eps大小:绘制出集合E的变化曲线,将急剧发生变化的位置所对应k-距离的值作为eps的大小
(2)根据经验确定MinPts的大小
如果MinPts不变,eps值过大,会导致大多数点聚到一个簇,eps过小会导致一个簇的分裂;如果eps值不变,MinPts过小,会导致出现大量核心点,MinPts过大会导致同一个簇中的某些点变为噪声点。 - 算法流程
输入:数据集D,参数(eps,MinPts)
方法:(1)计算出数据集中的核心点集;(2)遍历所有核心点,对于该核心点和由该核心点密度可达的点划分为一个簇,贴上标签,对于已经贴上标签的核心点不再执行上述操作;最后数据集中几乎所有点均由标签,未有标签的点为噪声点。
输出:含有类别划分标签的数据集
sklearn实现
模型:
sklearn.cluster.DBSCAN(eps=0.5, min_samples=5, metric=‘euclidean’, metric_params=None, algorithm=‘auto’, leaf_size=30, p=None, n_jobs=None)
- 主要参数为eps和min_sample(MinPts)
- 主要属性
core_sample_indices_: ndarray of shape (n_core_samples,)
components_: ndarray of shape (n_core_samples, n_features),Copy of each core sample found by training.
labels_: ndarray of shape (n_samples),Cluster labels for each point in the dataset given to fit(). Noisy samples are given the label -1. - 主要方法
fit(X, y=None, sample_weight=None)
首先看下数据集分布是什么样的
#可视化数据
import numpy as np
import matplotlib.pyplot as plt
import scipy.io as sio
from sklearn.cluster import DBSCAN
%matplotlib notebook
path='D:\code\python\database\ex7data2'
data=sio.loadmat(path)
X=data['X']
plt.scatter(X[:,0],X[:,1])
<IPython.core.display.Javascript object>
<matplotlib.collections.PathCollection at 0x25398236ac8>
利用sklearn模型聚类
model = DBSCAN(0.5,4) #取eps=0.5,MinPts=4
model.fit(X)
print(set(model.labels_)) #输出聚类簇数
plt.scatter(X[:,0],X[:,1],c=model.labels_,cmap='rainbow')
{0, 1, 2, -1}
<IPython.core.display.Javascript object>
<matplotlib.collections.PathCollection at 0x25398414e10>
结果显示聚类簇数为3,蓝色点为噪声点
python代码实现(聚类效果同sklearn一样)
class DBSCAN_my:
def __init__(self,eps,min_samples):
self.eps = eps
self.min_samples = min_samples
def calCoreSamples(self,X):
m = X.shape[0]
core_samples = {} #用字典保存核心对象集合
for i in range(m):
samples = [] #用列表保存核心对象密度直达的点
count = 0
for j in range(m):
dist = np.sqrt(np.sum((X[i]-X[j])**2))
if dist < self.eps:
samples.append(j)
count += 1
if count > self.min_samples:
samples.remove(i)
core_samples[i] = samples
return core_samples
def cluster(self,value,core_samples,k):
for i in value:
if self.labels[i]==-1:
self.labels[i] = k
if i in core_samples:
self.cluster(core_samples[i],core_samples,k)
def fit(self,X):
#计算样本集的核心对象
core_samples = self.calCoreSamples(X)
#初始化样本集的标签,设置为-1
self.labels = -np.ones(X.shape[0])
k = 0 #用k表示类别
#开始聚类
for key,value in core_samples.items():
if self.labels[key] == -1:
#对该核心点设置标签
self.labels[key] = k
#对该核心点密度可达的样本点设置标签
self.cluster(value,core_samples,k)
k += 1
model = DBSCAN_my(0.5,4)
model.fit(X)
print(set(model.labels))
plt.scatter(X[:,0],X[:,1],c=model.labels,cmap='rainbow')
{0.0, 1.0, 2.0, -1.0}
<IPython.core.display.Javascript object>
<matplotlib.collections.PathCollection at 0x253985be940>