当前位置:   article > 正文

DBSCAN 对点云障碍物聚类 找出数据集中的异常值_dbscan异常检测

dbscan异常检测

目录

使用DBSCAN找出数据集中的异常值

点云 DBSCAN 对点云障碍物聚类


使用DBSCAN找出数据集中的异常值

使用DBSCAN找出数据集中的异常值_赵卓不凡的博客-CSDN博客_dbscan 异常检测

1. 引言
找出数据中的异常值是数据预处理的必备工作之一,如果数据中存在异常值对于一些数据分析算法具有重大的影响。
本文主要探讨关于寻找异常值(离群值)的注意事项。

2. 离群值
离群值是指跟大部分数据差异很大的样本。比如,在一项统计国民收入的例子中,少数富豪的收入就很像离群值。离群值对于一些数据分析方法会有很大的影响。我们不妨来举个栗子。 


观察上图,为两个线性模型的分析结果,可以发现右侧只是增加一个离群值,我们的分析结果就会差很多。因此,如果没有处理好离群值,对于数据分析的结果可能会产生重大影响。

3.使用直方图确认离群值
既然我们知道数据集中存在离群值会对我们的分析结果产生影响,那么我们如何来确认数据中的离群值呢?
最常见的方法是使用直方图画出数据的分布。如下所示:

 

观察上图,我们很容易判断出这个数据集中确实存在离群值。但是,如果我们的数据集包含两个纬度的特征呢?我们来看个例子,如下:


观察上图,为我们将两个纬度的直方图绘制出来的结果,我们根据上图能否确认离群值呢?

嗯,看起来好像是没有离群值。。。很不幸这次通过看直方图我们的结论是错的。有时候,离群值是需要同时比对数个特征才有办法观察出来。我们不妨换种表示形式,不妨使用散点图(Scatter Plot)来呈现上述数据集,结果如下:


仔细观察上图右上角有两个样本,跟其他数据样本很不同,所以可以确定是离群值。我们需要同时观察2个特征,才有办法看出离群值。但是,如果数据集中有3个特征呢,难道要画出立体散点图来观察离群值吗?那如果数据集中有4个特征呢?这时候我们连图都画不出来了怎么办?

4.使用DBSACN来寻找离群值
DBSCAN是聚类算法的一种,这个方法是通过比较样本之间的距离,来判定那些样本是同一类。另一种常见的聚类算法是K-means,这是通过比较样本跟聚类中心的距离,来判定那些样本是同一类。由于我们的问题是要找是否有样本跟其他样本很不同,因此DBSCAN比较适合。

在下述代码中,我们使用sklearn中内置的SBSCAN方法来尝试找出上图右上角两个异常的离群值。

  1. colors = ['red','blue']
  2. data = np.array([x, y]).T
  3. model = DBSCAN(eps = 0.5
  4.                min_samples = 1
  5.                leaf_size = 1).fit(data)
  6. plt.figure()
  7. plt.scatter(x, 
  8.             y, 
  9.             c = model.labels_, 
  10.             cmap = matplotlib.colors.ListedColormap(colors))
  11. plt.xlabel("X")
  12. plt.ylabel("Y")
  13. plt.title("Scatter Plot of Feature X and Y")
  14. plt.show()



上述代码画出来的结果如下所示:

观察上图,可以看出跟其他样本不同,右上角的两个样本被分在不同的群组里。所以,通过使用DBSCAN算法,我们可以方便的找出具有多维特征的数据集里是否含有离群值。

5. 总结
我们生活的世界里数据每秒钟都在增长,在对数据进行分析之前,发现数据中的异常对于探索性数据分析至关重要。 本文就如何寻找数据集中的离群值的方法进行了简单的介绍,在具有多维特征的数据集中,大家不妨使用DBSCAN来寻找其中是否存在离群值。
 

点云 DBSCAN 对点云障碍物聚类

点云数据去除地面后,地面上的点很自然的都成了障碍物,但是要进行目标分类,还需要把每个目标的一堆障碍物的点聚集到一起,然后才好进行后续的分析,因为每个点都是空间上离的很近的点,那么很自然的,就想到了可以使用基于密度的聚类 DBSCAN ;

为了更好的适应我的需求,使用的是自己写的DBSCAN方法;

整个过程如下:

1 先做降采样,降低点云数量;

2 然后地面去除,得到障碍物的点;

3 然后把剩余的地表障碍物点送入 DBSCAN 进行聚类;

4 最后进行可视化;

聚类核心代码如下:

def vector_distance_v2(v):
    """
    把单个向量内部的每个元素两两相减,得到一个差值矩阵,矩阵是上三角和下三角刚好相反的结果
    :param v: 可以是一个一维数组,或者一个一维的列表
    :return:
    """
    if type(v) is list:
        v = np.array(v)
    # result = []
    # for i in range(len(v)):
    #     result.append(v[i] - v)  # 可以改为列表推导式
    result = [v[i] - v  for i in range(len(v))]
    return np.vstack(result)   
 
 

  1. def point_distance(points):
  2.     """
  3.     计算所有 points 两两之间的距离
  4.     :param points:  地面分割之后检测出来的点  n * 4
  5.     :return:  n * n 的距离矩阵
  6.     """
  7.     d2 = vector_distance_v2(points[:,0])**2
  8.          vector_distance_v2(points[:,1])**2
  9.          vector_distance_v2(points[:,2])**2
  10.  
  11.  
  12.     return np.sqrt(d2)
  13.  
  1. # @profile
  2. def DBSCAN_points(points, eps=2., Minpts=15):
  3.     """
  4.     基于密度的点云聚类
  5.     :param d_bbox: 点与点之间的距离矩阵
  6.     :param eps:  最大搜索直径阈值
  7.     :param Minpts:  最小包含其他对象数量阈值
  8.     :return: 返回聚类结果,是一个嵌套列表,每个子列表就是这个区域的对象的序号
  9.     """
  10.     # 先求距离
  11.     print('DBSCAN clustering:',points.shape)
  12.     d_bbox = point_distance(points)
  13.  
  14.     #初始化核心对象集合T,聚类个数k,聚类集合C, 未访问集合P,
  15.     T = set()
  16.     k = 0
  17.     C = []
  18.     P = set(range(d_bbox.shape[0]))
  19.     # print('P',P)
  20.     for d in range(d_bbox.shape[0]):
  21.         # print(np.sum( d_bbox[d,:] <= eps))
  22.         if np.sum( d_bbox[d,:] <= eps) >= Minpts:
  23.             T.add(d)  # 最初的核心对象
  24.     print('Len T: ',len(T))
  25.     #开始聚类
  26.     while len(T):
  27.         P_old = P  #
  28.         o = list(T)[np.random.randint(0, len(T))]  # 从T中随机选取一个核心元素
  29.         # o = list(T)[random.randint(0, len(T)-1)]  # 从T中随机选取一个核心元素
  30.         # print('o: ',o)
  31.         P = P - set([o])
  32.         Q = []
  33.         Q.append(o)
  34.         # print('Q: ',Q)
  35.  
  36.         while len(Q):
  37.             q = Q[0]
  38.             # print('q: ', q)
  39.             # Nq = [i for i in range(d_bbox.shape[0]) if d_bbox[q,i] <= eps] #q的领域密度
  40.             Nq = np.where(d_bbox[q,:] <= eps)[0]
  41.             if len(Nq) >= Minpts:
  42.                 S = P & set(Nq)   # 这个核心对象的密度可达对象与未访问对象的交集
  43.                 Q += (list(S))   # 把这个核心对象以及它的密度可达对象都包含进来,对所有的对象再做多次密度可达检测
  44.                 P = P - S  # 未访问集合P 减去 这个核心对象的密度可达对象
  45.             # print('S: ', S)
  46.             # print('Nq: ', Nq)
  47.             # print('P: ', P)
  48.  
  49.             Q.remove(q)  # q 已经做过密度可达检测了,去掉它
  50.         # print('------')
  51.         k += 1
  52.         Ck = P_old - P # 原有的P和去掉了该核心对象的密度可达对象的P就是该类的所有对象
  53.         T = T - Ck  # 去掉该类对象里面包含的核心对象
  54.         C.append(Ck)     # 把该类的对象加入列表
  55.     # print('noise points:', P)   # 最后没有被归类的数据点就是噪音点
  56.     return C


存在的问题:

1 耗时久,计算量大;主要是由于 DBSCAN 需要计算每两个点两两之间的距离,超过5万个点就是25万的距离矩阵,就直接报内存错误了,查看任务管理器发现内存占用达到了5-9G;尽管后续极力压缩,还是需要5-6秒的时间,这显然是不可接受的;

2 它对同一个物体聚类的效果比较好,不会存在同一个物体聚类分割成了2部分的情况;但是! 它会把多个离的比较近的物体聚类为一个;

效果如下图:


因为经过了降采样,图片中的点可能不是那么亮,但是其实效果还可以;并且这个里面的地面还存在一些,因为没有使用最新的地面分割方法;

原文链接:https://blog.csdn.net/weixin_31971181/article/details/113038548

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/855495
推荐阅读
相关标签
  

闽ICP备14008679号