DBSCAN算法学习小结

DBSCAN的算法实现和应用

在学习《Python机器学习实践指南》第3章《构建应用程序,发现低价的机票》一章,文中从google航班查询器爬取机票价格,使用DBSCAN算法实现聚类找出票价有问题的机票。文中使用DBSCAN算法主要还是调用了DBSCAN库。本文想学习DBSCAN的内部实现算法,并通过python实现,同时找一个类似的爬取项目完成。除了参考上面的书籍,本文对于DBSCAN的算法理解主要参考了http://www.cnblogs.com/wsine/p/5180778.htmlhttp://blog.csdn.net/itplus/article/details/10088625中的内容。

DBSCAN算法概述

DBSCAN算法被称为基于密度的空间聚类算法,适用于带有噪声数据的应用。除了给定的数据,还需要给定下面两个参数:
· epsilon(下面简写为eps):同一个聚类中两个点彼此之间的距离
· min_samples(最小点数):创建聚类所需要的最小数量(包括当前点)
注意:当某个点在eps范围内的点数小于min_samples,那么就把该点认为是噪声点。

关于DBSCAN算法,《Python机器学习实践指南》中是这样描述的:

从所有的点的集合中随机选择一个点。从这一个点出发,搜索所有方向上的和当前点相距epsilon距离的范围。如果在epsilon距离的范围内,存在等于或多余最小点数的点,那么这个范围内所有的点就隶属于一个聚类。针对每个新家兔该聚类的点,重复该过程。继续此操作,知道没有任何新的点可以添加到此聚类。此时,第一个聚类就完成了。现在,从已经完成的聚类之外,随机选择新的点再次开始。重复同样的过程,知道没有新的聚类可以形成。

DBSCAN算法流程图

网上给出了DBSCAN算法的伪代码,通常均包含两个函数:

def dbscan(data, eps, minPts)

输入所有数据data,距离eps和每个聚类中的最小点数minPts,对数据中的所有点遍历循环已获得其所对应的聚类。

def expand_cluster(data, clusterResult, pointId, clusterId, eps, minPts)

输入:所有数据data,聚类结果clusterResult,当前点序号pointId,当前聚类标识clusterId,以及距离eps和每个聚类中的最小点数minPts。
对于某一点找出于该点在同一聚类中的所有点。

下面给出流程图,便于理解:
expand_cluster:

Created with Raphaël 2.1.0 http://www.google.com Start http://www.google.com 根据pointID获取点p 获取与点p距离小于等于eps的点集 len(send)>=minPts? 将p的聚类族群标志设为ClusterID, 即将p划分到当前聚类 send个数为0? 取出send一点q,获取与点q距 离小于等于eps的点集querydata len(querydata)>=minPts? 遍历querydata中的点,如果该点为Noise,则设为clusterID,如果该点未标记过,则设为clusterID后插图send http://www.google.com End http://www.google.com 将p点设为噪声 yes no yes no yes no

调用该函数时,给出的点是新的一个聚类当中的点,聚类ID也是一个新的ID,目的就是获得当前点的聚类数,判断是否可以构成一个聚类。
1.获取与point距离小于或等于eps的点集,如果个数小于minPts,则认为该点是噪声点。
2.如果point的聚类点集send个数大于minPts,那么说明已经可以形成一个聚类,需要对聚类中的其他点依次判断,以获取该聚类中的所有点。注意该过程中找到的新点如果没有处理过,则需要加入send中,直到send中没有数据了,表明当前这个聚类的所有点都获取到了。

对于dbscan:当对一点p调用一次expand_cluster函数后,data中与p在同一聚类的点都设置了对应的clusterID,或者p设为了噪声。只需要再遍历没有处理过的点即可。

DBSCAN算法实现

(本部分算法实现摘自http://www.cnblogs.com/wsine/p/5180778.html)

import numpy as np
import matplotlib.pyplot as plt
import math
import time

UNCLASSIFIED = False
NOISE = 0

def loadDataSet(fileName, splitChar='\t'):
    """
    输入:文件名
    输出:数据集
    描述:从文件读入数据集
    """
    dataSet = []
    with open(fileName) as fr:
        for line in fr.readlines():
            curline = line.strip().split(splitChar)
            fltline = list(map(float, curline))
            dataSet.append(fltline)
    return dataSet

def dist(a, b):
    """
    输入:向量A, 向量B
    输出:两个向量的欧式距离
    """
    return math.sqrt(np.power(a - b, 2).sum())

def eps_neighbor(a, b, eps):
    """
    输入:向量A, 向量B
    输出:是否在eps范围内
    """
    return dist(a, b) < eps

def region_query(data, pointId, eps):
    """
    输入:数据集, 查询点id, 半径大小
    输出:在eps范围内的点的id
    """
    nPoints = data.shape[1]
    seeds = []
    for i in range(nPoints):
        if eps_neighbor(data[:, pointId], data[:, i], eps):
            seeds.append(i)
    return seeds

def expand_cluster(data, clusterResult, pointId, clusterId, eps, minPts):
    """
    输入:数据集, 分类结果, 待分类点id, 簇id, 半径大小, 最小点个数
    输出:能否成功分类
    """
    seeds = region_query(data, pointId, eps)
    if len(seeds) < minPts: # 不满足minPts条件的为噪声点
        clusterResult[pointId] = NOISE
        return False
    else:
        clusterResult[pointId] = clusterId # 划分到该簇
        for seedId in seeds:
            clusterResult[seedId] = clusterId

        while len(seeds) > 0: # 持续扩张
            currentPoint = seeds[0]
            queryResults = region_query(data, currentPoint, eps)
            if len(queryResults) >= minPts:
                for i in range(len(queryResults)):
                    resultPoint = queryResults[i]
                    if clusterResult[resultPoint] == UNCLASSIFIED:
                        seeds.append(resultPoint)
                        clusterResult[resultPoint] = clusterId
                    elif clusterResult[resultPoint] == NOISE:
                        clusterResult[resultPoint] = clusterId
            seeds = seeds[1:]
        return True

def dbscan(data, eps, minPts):
    """
    输入:数据集, 半径大小, 最小点个数
    输出:分类簇id
    """
    clusterId = 1
    nPoints = data.shape[1]
    clusterResult = [UNCLASSIFIED] * nPoints
    for pointId in range(nPoints):
        point = data[:, pointId]
        if clusterResult[pointId] == UNCLASSIFIED:
            if expand_cluster(data, clusterResult, pointId, clusterId, eps, minPts):
                clusterId = clusterId + 1
    return clusterResult, clusterId - 1

def plotFeature(data, clusters, clusterNum):
    nPoints = data.shape[1]
    matClusters = np.mat(clusters).transpose()
    fig = plt.figure()
    scatterColors = ['black', 'blue', 'green', 'yellow', 'red', 'purple', 'orange', 'brown']
    ax = fig.add_subplot(111)
    for i in range(clusterNum + 1):
        colorSytle = scatterColors[i % len(scatterColors)]
        subCluster = data[:, np.nonzero(matClusters[:, 0].A == i)]
        ax.scatter(subCluster[0, :].flatten().A[0], subCluster[1, :].flatten().A[0], c=colorSytle, s=50)

def main():
    dataSet = loadDataSet('788points.txt', splitChar=',')
    dataSet = np.mat(dataSet).transpose()
    # print(dataSet)
    clusters, clusterNum = dbscan(dataSet, 2, 15)
    print("cluster Numbers = ", clusterNum)
    # print(clusters)
    plotFeature(dataSet, clusters, clusterNum)

if __name__ == '__main__':
    start = time.clock()
    main()
    end = time.clock()
    print('finish all in %s' % str(end - start))
    plt.show()

对其中的一些内容和语法点作简要说明:
1.欧式距离:两个n维向量a(x11,x12,…,x1n)与 b(x21,x22,…,x2n)间的欧氏距离:
这里写图片描述
2.shape()获取维数。

DBSCAN算法应用

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值