编写基于dbscan的GPS数据热点区域分析(二)算法的实现

这一篇主要讲讲如何在实际运用中编写dbscan算法。dbscan算法主要的目的就是找到最大密度相连点的集合。那么它必然涉及到3个子算法:

1) dbscan主流程

2) 如何确定两个GPS数据点的距离

3) 如何合并簇

我们逐一解决。首先说说GPS数据点的距离该如何确定。这个问题感觉看似简单(居然有人用勾股定理和经纬度与距离关系来计算,我只能脑洞大,但没法用)。如果要精确计算两点之间的关系,单纯利用球心,半径,点三者关系利用三角和反三角函数来计算也不大对的。毕竟地球不是那种半径一致的球体,而是个两极稍扁、赤道略鼓的不规则球体。所以,还是用专业的知识去求解把。这里提供两种解决方法。

方法一: 既然我们地图选用的是百度地图。那自然就可以选用他的计算工具。(http://developer.baidu.com/map/library.htm)如图:编写基于dbscan的GPS数据热点区域分析(二)算法的实现_第1张图片


方法二:利用球心,半径(假设地球是规则球体),点关系来求解。这个计算结果可想而知,没法太精准的。当然方法一是否精准我也无法保证。基于方法二的算法大致相同。这里提供个参考资料链接。http://www.movable-type.co.uk/scripts/latlong.html 这个提供的资料很详细了。直接用吧。


  接下来,我们主要谈dbscan的具体实现。首先我们定义一个结构体用来存放数据类型。如下:

class DataObject:
    def __init__(self, longitude, latitude):
        self.latitude = latitude
        self.longitude = longitude
        self.isVisited = False
        self.clusterId = 0 #簇序号 0表示未分类 -1 表示噪声点 正数为序号
    def setVisited(self):
        self.isVisited = True
    def isVisited(self):
        return self.isVisited
    def getClusterId(self):
        return self.clusterId
    def getLongitude(self):
        return self.longitude
    def getLatitude(self):
        return self.latitude
    def setClusterId(self, id):
        self.clusterId = id
这样设置的好处是合并簇的时候我只要通过设置cluseterId即可实现,简单吧。

dbscan算法实现如下:

def dbscan(self):
        """
        @brief: dbscan核心函数 方法: 遍历每个点 判断是否访问,若未,则获取它的Eps邻域数据
            若Neps的大小>=MinPts 则 进行扩张簇
        :return:不返回数据
        """
        self.clusterId = 0 #簇序号
        for p in self.data:
            if p.isVisited == False:
                p.setVisited()
                neighbors = self.getNeighbors(p)
                if neighbors.__len__() <=0: #噪声点
                    p.setClusterId(-1)
                elif neighbors.__len__() >= self.MinPts:#核心点  (边界点不做处理)
                    if p.getClusterId() <=0:
                        self.clusterId += 1
                        self.expandCluster(p, neighbors, self.clusterId)
                    else :
                        id = p.getClusterId() #如果p核心点已经分类了,那么返回它原本属于的簇序号 进行扩张
                        self.expandCluster(p, neighbors, id)

其中,获取Eps邻域数据方法如下:

    def getNeighbors(self, pData):
        """
        @brief 找到点P的所有满足Eps邻域的点 注意这里采用浅复制 方便修改原数据
        :param pData: 待查找的点
        :return: 返回所有满足点
        """
        DataRet = []
        for tmp in self.data:
            if self.getDistance(tmp, pData) <= self.Eps:
                DataRet.append(tmp)
        return DataRet
扩张簇的方法如下:

  def expandCluster(self, pData, neighbors, clusterId):
        """
        @brief 扩张簇的数据
        :param pData: 核心点 需要设置簇ID
        :param neighbors:  核心点p的所有Eps邻域点
        :param clusterId:  簇的ID号
        :return: 不返回数据
        """
        pData.setClusterId(clusterId)
        for qData in neighbors:
            if qData.isVisited == False:
                qData.setVisited()
                #接下来查找 qData的所有Eps邻域的数据
                DataRst = self.getNeighbors(qData)
                if DataRst.__len__() >= self.MinPts:
                    for q in DataRst:
                        if q.getClusterId()<=0: #说明未分配 或者为噪声点
                            q.setClusterId(clusterId)
            if qData.getClusterId()<=0:
                qData.setClusterId(clusterId)

根据上一回说的( 聚类经典算法之DBSCAN算法)实现起来是不是不难呀。其实,我觉得学习一个算法,更重要的是学会他的思想,然后去思考如何去改进它,这样才有进步。至少我自己不喜欢学而不改。好了,装逼到此结束。



你可能感兴趣的:(数据挖掘)