用python实现DBSCAN

DBSCAN是比较著名的基于密度的聚类方法,它可以轻松地得到各种形状的簇。
主要有两个参数,邻域半径 ϵ \epsilon ϵ 以及邻域内最少数据点数 m i n p t s minpts minpts .
python代码如下:
github地址

def dist(a, b): 
    m = a.shape[0]
    n = b.shape[0]
    res = np.zeros((m, n))
    for i in range(m):
        res[i] = np.sqrt(np.sum((a[i] - b) ** 2, axis=1))
    return res
def dbscan(data, eps, minpts):
    dis = dist(data, data)  # 求两两之间距离
    n = data.shape[0]  # 样本数
    k = 0  # 类编号
    visit = np.zeros(n)  # 是否被访问过
    res = np.zeros(n).astype(np.int)  # 聚类结果
    random_id = np.random.permutation(n)  # 随机排列
    for s in random_id:
        if visit[s] == 0:
            visit[s] = 1
            neps = list(np.where(dis[s] <= eps)[0])  # 找到 eps 范围邻域内所有点(包括了自己)
            if len(neps)-1 < minpts:  # 数量不足 minpts 暂时设为噪声点
                res[s] = -1
            else:  
                k += 1
                res[s] = k  # 数量达到 minpts 归为 k 类
                while len(neps) > 0:
                    j = np.random.choice(neps)
                    neps.remove(j)
                    if res[j] == -1:  # 如果之前被标为噪声点,则归为该类, 也可以归为边界点
                        res[j] = k
                    if visit[j] == 0:  # 没有被访问过
                        visit[j] = 1
                        j_neps = list(np.where(dis[j] <= eps)[0])  # 找邻域
                        if len(j_neps)-1 < minpts:
                            res[j] = k  # 非核心点,可归为该类, 也可以归为边界点
                        else:
                            res[j] = k  # 核心点,加入集合
                            neps = list(set(neps + j_neps))
    return res, k

计算距离比较费时间,可以使用KD树进行优化:

def dbscan2(data, eps, minpts):
    kdtree = KDTree(data)
    n = data.shape[0]  # 样本数
    k = 0  # 类编号
    visit = np.zeros(n)  # 是否被访问过
    res = np.zeros(n)  # 聚类结果
    random_id = np.random.permutation(n)  # 随机排列
    for s in random_id:
        if visit[s] == 0:
            visit[s] = 1
            neps = list(kdtree.query_radius(data[s].reshape(1, -1), eps)[0])  # 找到 eps 范围邻域内所有点(包括了自己)
            if len(neps)-1 < minpts:  # 数量不足 minpts 暂时设为噪声点
                res[s] = -1
            else:  
                k += 1
                res[s] = k  # 数量达到 minpts 归为 k 类
                while len(neps) > 0:
                    j = random.choice(neps)
                    neps.remove(j)
                    if res[j] == -1:  # 如果之前被标为噪声点,则归为该类
                        res[j] = k
                    if visit[j] == 0:  # 没有被访问过
                        visit[j] = 1
                        j_neps = list(kdtree.query_radius(data[j].reshape(1, -1), eps)[0])  # 找邻域
                        if len(j_neps)-1 < minpts:
                            res[j] = k  # 非核心点,可归为该类, 也可以归为边界点
                        else:
                            res[j] = k  # 核心点,加入集合
                            neps = list(set(neps + j_neps))
    return res, k

生成数据:

from sklearn import datasets
x1, y1 = datasets.make_circles(n_samples=6000, factor=0.5, noise=0.03)
x2, y2 = datasets.make_blobs(n_samples=1000, centers=[[0, 0]], cluster_std=[0.085])
X = np.vstack((x1, x2))
plt.figure(figsize=(6, 6))
plt.plot(X[:,0],X[:,1],'.', markersize=5)

测试:

res, k = dbscan(X, 0.05, 8)

结果图:
用python实现DBSCAN_第1张图片
sklearn中DBSCAN

db = DBSCAN(eps=0.05, min_samples=8)
db.fit(X)
label = db.labels_

用python实现DBSCAN_第2张图片

你可能感兴趣的:(算法,python,机器学习,DBSCAN,聚类)