密度聚类:OPTICS算法简单易懂版

2023-05-16

前几天写了一篇详解版,感觉可能太详细了阅读量不高,所以修改精简成这篇。


很多人不理解OPTICS算法绘出的图该怎么理解。为什么波谷就算一类,有个波峰又算另一类了,本篇在第三部分的第2、3节详细讲这个是什么判别分类的。

本篇会添加一些个人思考过程,可能有不严谨的地方,希望在评论区讨论指正。

另外,学习本篇,需要有DBSCAN的基础。如果没基础可以先看看这篇《详解DBSCAN聚类》


一、OPTICS重要的新定义

在DBSCAN的基础上,给定 M i n P t s MinPts MinPts e p s eps eps 后,OPTICS又提出了下面几个新定义:

(1)核心距离coreDist:当前核心点 p p p 的邻域中,与核心点 p p p 距离升序排第 M i n P t s − 1 MinPts - 1 MinPts1位的样本点,与 p p p 的距离作为 p p p 的核心距离(-1是因为邻域包含了p本身)

在这里插入图片描述
这里 N ( p ) N(p) N(p)指的是p的邻域点数。

个人理解:因为OPTICS一开始会设定 e p s = i n f eps=inf eps=inf,所以所有点都是核心点,所以 c o r e D i s t ( p ) coreDist(p) coreDist(p)的计算也不需要区分条件 N ( p ) < M i n P t s N(p) < MinPts N(p)<MinPts,除非你数据集的总样本量 < M i n P t s MinPts MinPts

(2)可达距离reach_dist:当前核心点 p p p 下,其他样本点 i i i p p p 的可达距离为 m a x ( i 与 p 间的距离 , p 的核心距离 ) max(i与p间的距离, p的核心距离) max(ip间的距离,p的核心距离)

在这里插入图片描述
个人理解:同(1)

两种距离的举例如下图所示,图片来自知乎@马东什么,我觉得非常的直观
在这里插入图片描述


二、算法流程

先给出简洁的流程,如下。

1、中文描述(最精简)

计算样本点两两距离,填值到 d i s t M a t distMat distMat
全体样本点作为核心点集合 c o r e _ p o i n t s core\_points core_points
计算每个样本点的核心距离 c o r e _ d i s t core\_dist core_dist,方法是取 d i s t M a t distMat distMat每一列升序第 M i n P t s MinPts MinPts的值

数据集中随机选择一个点作为核心点 p p p
计算数据集中其他点 i i i到点 p p p的可达距离 r e a c h _ d i s t [ i ] reach\_dist[i] reach_dist[i],以键值对{ i i i : r e a c h _ d i s t [ i ] reach\_dist[i] reach_dist[i]}存在字典 S e e d s Seeds Seeds
OK,点 p p p已经用过,不再参与后续任何计算

S e e d s Seeds Seeds不空时,循环:

(1) S e e d s Seeds Seeds按值升序排列,取第0个即离上一个核心点最近的点 q q q,作为当前核心点
(2)计算数据集中其他未处理的点 i i i到点 q q q的可达距离 r e a c h _ d i s t [ i ] reach\_dist[i] reach_dist[i],取新旧中的较小值,更新到 S e e d s Seeds Seeds
(3)OK,点 q q q已经用过,从 S e e d s Seeds Seeds中删除,且不再参与后续任何计算

2、算法流程(精简)

为了更好理解一点,我将通过接近python的伪代码+中文描述来写流程

2.1 初始化:

给定邻域最小样本量MinPts, 数据集D
# OPTICS的半径初始化为inf
eps = inf
# 判断样本是否已处理的列表,初始化为0,表示未处理过,若处理过则为1
processed_list = np.full(shape=(len(D),), fill_value=0)
# 距离矩阵,distMat[i,j]表示样本i与样本j之间的距离
distMat = np.zeros((len(D), len(D)))
# 可达距离的列表,reach_dist[i]表示样本i到当前核心点的可达距离
# 	初始为inf而非上面定义所说的undefined是因为好用一个公式计算
# 	即 reach_dist[i] = \
#		min(max(distMat[i,p],core_dist[p]), 旧reach_dist[i])
reach_dist = np.full(shape=(len(D),), fill_value=inf)
# 结果序列,这里的order并不是指可达距离的升序(虽然有关系),是指处理的顺序
order_list = []

2.2 算法开始

计算样本点两两距离,填值到distMat
全体样本点作为核心点集合core_points
计算每个样本点的核心距离core_dist,方法是取distMat每一列升序第MinPts的值
# 随机选择一个当作当前核心点p
p = core_points[0]
# 获得p周围eps内的邻居点,因eps=inf,实际上就是数据集其他所有样本点
Neighbors_points = getNeighbors(p, eps)
# 标记当前核心点p已处理并放入结果队列
processed_list[p] = 1
ordered_list.append(p)

# 字典形式的有序队列,用于升序排列样本点的可达距离
# 	键值对是{样本索引i:i到当前核心点p的可达距离}
Seeds = {}
# 更新Seeds中各点到当前核心点的p的可达距离,并升序排序Seeds
# update(Neighbors_points, p, Seeds, core_dist)
##############   第一次update  ######################
# 为更直观,不另写一个函数update,而是将update过程直接写在下方
# 遍历核心点p的未处理的邻居
for neigbor in Neighbor_points:
	if processed_list[neighbor] == 1:
		continue
	# 计算p的邻居点neighbor与点p的新可达距离,并取新旧间的最小值
	reach_dist[neighbor] = \
		min(max(distMat[neighbor,p],core_dist[p]), reach_dist[neighbor])
	# 在有序队列中添加or更新键值对,此处的update是字典内置方法
	Seeds.update({neighbor:reach_dist[neighbor]})
##############   第一次update结束  ######################

# 再按Seeds顺序遍历
# 	提醒一下,Seeds现在是p的未处理过的邻居,也即数据集中未处理过的样本
while len(Seeds)!=0:
	# 取离p最近的那个点,即Seeds中按可达距离升序排列后的第0个样本
	q = sorted(Seeds.items(), key=operator.itemgetter(1))[0][0]    
	del Seeds[q]
	# 标记当前核心点q已处理并放入结果队列
	processed_list[q] = 1
	ordered_list.append(q)
	
	Neighbors_of_q = getNeighbors(q, eps)
	# 更新Seeds中各点到当前核心点q的可达距离,并升序排序Seeds
	# update(Neighbor_of_q, q, Seeds, core_dist)
	############   第二次update  #############
	# 为更直观,不另写一个函数update,而是将update过程直接写在下方
	# 遍历核心点q的未处理的邻居
	for neigbor in Neighbor_of_q:
		if processed_list[neighbor] == 1:
			continue
		# 计算q的邻居点neighbor与点q的新可达距离,并取新旧间的最小值
		reach_dist[neighbor] = \
			min(max(distMat[neighbor,q],core_dist[q]), reach_dist[neighbor])
		# 在有序队列中添加or更新键值对,此处的update是字典内置方法
		Seeds.update({neighbor:reach_dist[neighbor]})
	#############  第二次update结束  ##########

三、一个简单的例子,讲述算法流程

1、例子来源:《机器学习笔记(十一)聚类算法OPTICS原理和实践》

已知样本数据集为: D = [ 1 , 2 ] , [ 2 , 5 ] , [ 8 , 7 ] , [ 3 , 6 ] , [ 8 , 8 ] , [ 7 , 3 ] , [ 4 , 5 ] D = {[1, 2], [2, 5], [8, 7], [3, 6], [8, 8], [7, 3], [4,5]} D=[1,2],[2,5],[8,7],[3,6],[8,8],[7,3],[4,5],坐标轴上分布如下,我已经用黄色圆圈序号标明其在样本中的索引。

设定 e p s = i n f eps=inf eps=inf M i n P t s = 2 MinPts=2 MinPts=2
先计算了 d i s t M a t distMat distMat c o r e _ d i s t core\_dist core_dist
初始化 r e a c h _ d i s t reach\_dist reach_dist i n f inf inf

记录了每一步处理的核心对象(核心点),并及时在 p r o c e s s e d _ l i s t processed\_list processed_list标记为已处理

可以看到 S e e d s Seeds Seeds的更新、 S e e d s Seeds Seeds的排序、选新的核心对象计算可达距离 是交替进行的
在这里插入图片描述

第一次update只是针对第一个选中的核心点,所以只有一次。
第二次update是 S e e d s Seeds Seeds不空的循环,会一直到结束(当 S e e d s Seeds Seeds为空)
在这里插入图片描述

最终的 r e a c h _ d i s t reach\_dist reach_dist 是按照样本顺序给出的, o r d e r e d _ l i s t ordered\_list ordered_list 是按照核心点的处理顺序的(也与可达距离有关),为了绘图,我们需要将 r e a c h _ l i s t reach\_list reach_list 按照 o r d e r e d _ l i s t ordered\_list ordered_list 的顺序重排列。如上图。

然后,再用这个重排列的数据绘图如下。

2、重点来了!如何理解这张 r e a c h _ d i s t — p o i n t s reach\_dist—points reach_distpoints图并实现分类

在这里插入图片描述
假设 e p s = 3.8 eps=3.8 eps=3.8

(1)首先,样本点0,值为 i n f inf inf不用绘制出来,因为 r e a c h _ d i s t reach\_dist reach_dist 的原始定义其实应该是全 u n d e f i n e d undefined undefined 而不是 i n f inf inf,未定义的值当然不用绘制了
(2)然后,样本点1,显然离样本点0的可达距离 < eps,那么归到与样本点0一类是没有问题的
(3)然后,样本点3,显然离样本点1的可达距离 < eps,那么归到与样本点0一类是没有问题的。

如果你是这样想就错了!
只是就本例的数据集来说,确实可以看出3到1更近。
从更广义的样本来说,样本点3离样本0和1谁更近是不知道的,即 r e a c h _ d i s t ( 3 , 0 ) reach\_dist(3,0) reach_dist(3,0) r e a c h _ d i s t ( 3 , 1 ) reach\_dist(3,1) reach_dist(3,1)谁小不知道,可以举例为:
样本点1到核心点0的 r e a c h _ d i s t ( 1 , 0 ) = 3.16 reach\_dist(1,0) = 3.16 reach_dist(1,0)=3.16 <
样本点3到核心点0的 r e a c h _ d i s t ( 3 , 0 ) = 3.3 reach\_dist(3,0) = 3.3 reach_dist(3,0)=3.3 <
样本点3到核心点1的 r e a c h _ d i s t ( 3 , 1 ) = 3.5 reach\_dist(3,1) = 3.5 reach_dist(3,1)=3.5

这样的话就是样本点3到核心点0的距离更近,也满足这样的 o r d e r e d _ l i s t ordered\_list ordered_list 顺序0->1->3
所以:我们可以发现这样的一个规律:
1与前面的集合{0}中的某点可达距离最近为3.16
3与前面的集合{0,1}中的某点可达距离最近为1.41
6与前面的集合{0,1,3}中的某点可达距离最近为1.41
5与前面的集合{0,1,3,6}中的某点可达距离最近为3.61

(4)对于样本点2,为什么就要归入第二类呢?因为

2与前面的集合{0,1,3,6,5}中的某点可达距离最近为4.12 > eps,所以分在第一类已经不合适了,又因为核心距离 c o r e _ d i s t [ 2 ] = 1.0 < e p s core\_dist[2]=1.0<eps core_dist[2]=1.0<eps,所以归入第二类

(5)对于样本点4,可达距离小于eps,为什么也要归入第二类呢?因为

4与前面的集合{0,1,3,6,5,2}中的某点可达距离最近为1,
但是显然,样本点4不能与前面{0,1,3,6,5}的可达距离,必然比样本点2与前面的可达距离还要远(否则在 o r d e r _ l i s t order\_list order_list 中样本点4应该排在2前面)
4与前面的集合中的可达距离最近为1,还得是靠样本点2才能这么小。所以样本点4与归入2所在的第二类。

3、总结一下根据 r e a c h _ d i s t — p o i n t s reach\_dist—points reach_distpoints图如何定义簇

(可惜上面的例子没有噪声点样本。)
注意此时eps已不再是inf,而是依据图自定义的。

从结果队列 o r d e r _ l i s t order\_list order_list 按顺序取出样本点,直到结果队列为空:

若该点的可达距离 <= eps,则属于当前聚类簇
否则:

若该点的核心距离 > eps,为噪声点
若该点的核心距离 < eps,为新的聚类簇


四、numpy实现的代码

参考:武大博士的知乎——《聚类算法之OPTICS算法》


其他参考链接:

三、3、定义簇部分 ——《dbscan和optics(完结撒花~)》
二、1、算法流程(最精简)部分—— ——曾依灵, 许洪波, 白硕. 改进的OPTICS算法及其在文本聚类中的应用[C]// 全国信息检索与内容安全学术会议. 2007:51-55.
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

密度聚类:OPTICS算法简单易懂版 的相关文章

  • python使用serial模块,通过串口控制云台(基于PELCO-D协议)

    一 需求描述 通过python实现对云台的控制 xff0c 使用到的相关模块 xff1a 1 pyserial xff0c 串口模块 xff0c 用于连接串口 xff0c 并通过串口发送指令 2 bytes xff0c 内置模块 xff0c
  • TP-LINK路由器如何设置上网

    1 登陆 xff0c 网址192 168 1 1 xff0c 登陆 xff0c 一般在路由器背后 xff0c 没有或者忘记就重置 2 设置导向 3 输入拨号 xff08 以太网 xff09 的账号密码 4 设置wifi账号密码 xff0c
  • (1+x)^(1/x)的导数

  • 电脑待机久了没有声音,需要重启才行

    这种原因一般是唤醒电脑后 xff0c 声卡驱动没有跟着苏醒导致 xff1b 方法一 xff1a 重启电脑 xff0c 简单粗暴 xff1b 方法二 xff1a 在设备器里面重启声音设备 xff0c 先禁用 xff0c 再重新 xff1b 也
  • Pycharm 中安装pywin32报错

    1 在pycharm的寻找并安装插件pywin32时报错 xff1b 大致意思是安装失败 xff0c 建议的解决方案 xff1a 尝试从系统终端运行此命令 确保使用正确的 pip 版本 xff0c 该版本已为位于如下地址 xff1a F p
  • win10下关闭笔记本自带键盘以及解锁

    win10下关闭笔记本自带键盘 管理员运行cmd sc config i8042prt start 61 disabled 重启 解除自带键盘锁定 1 sc config i8042prt start 61 auto xff0c 重启 xf
  • chatra无法注册

    chatra用QQ邮箱注册显示邮箱无法访问 xff0c 不知道是不是邮箱设置了还是这个网站不支持 xff1b 用163邮箱注册就成功了
  • 打开回收站提示“回收站已损坏是否清空该驱动器上的回收站“解决方法

    我们一般需要删除的文件或者文件夹都是删除在电脑系统中的回收站中的 xff0c 但是最近有一个网友在打开Win10系统的回收站的时候 xff0c 忽然弹出了提示 D xff1a 上的回收站已损坏 是否清空该驱动上的回收站 xff0c 一般遇到
  • C++之迭代器(Iterator)篇

    迭代器 xff08 Iterator xff09 的介绍 背景 xff1a 指针可以用来遍历存储空间连续的数据结构 xff0c 但是对于存储空间费连续的 xff0c 就需要寻找一个行为类似指针的类 xff0c 来对非数组的数据结构进行遍历
  • Sublime Text运行C和C++程序

    Sublime Text 是一款当下非常流行的文本编辑器 xff0c 其功能强大 xff08 提供有众多的插件 xff09 界面简洁 还支持跨平台使用 xff08 包括 Mac OS X Linux 和 Windows xff09 在程序员
  • c++ string find(), rfind(), find_first_of(),find_last_of()

    find rfind 函数原型 xff1a span class token keyword int span span class token function find span span class token punctuation
  • 1233. 删除子文件夹

    难度 xff1a 中等 你是一位系统管理员 xff0c 手里有一份文件夹列表 folder xff0c 你的任务是要删除该列表中的所有 子文件夹 xff0c 并以 任意顺序 返回剩下的文件夹 如果文件夹 folder i 位于另一个文件夹
  • 【ROS入门篇·一】ROS文件系统 & catkin编译系统

    快速链接 xff1a ROS入门篇 ROS学习简介 一 ROS工作空间创建 amp 编译 1 xff09 创建工作空间 mkdir p catkin ws src cd catkin ws src 2 xff09 创建功能包 catkin
  • 【ROS入门篇·五】launch文件使用 & 向节点传递参数

    快速链接 xff1a ROS入门篇 ROS学习简介 一 launch文件使用 1 1 launch文件简介 launch文件能够同时启动一个ROS Master和多个Node launch文件的标签 lt launch gt lt 根标签
  • 【ROS小车仿真·一】rviz中显示urdf模型

    快速链接 xff1a ROS小车仿真 从零实现gezebo小车仿真 一 urdf简介 URDF Unified Robot Description Format 统一机器人描述格式 xff0c URDF是使用XML格式描述机器人的文件 参考
  • 2.为什么要内存对齐?

    一 什么是内存对齐 xff08 Memory alignment xff09 xff0c 也叫字节对齐 在计算机中 xff0c 内存是按 字节 byte 1byte 61 8bit 划分的 xff0c 而cpu在读取内存数据时 xff0c
  • 问题笔记:STM32串口数据位与校验位

    问题 xff1a STM32移植freemodbus 后测试时 xff0c 只能使用无校验 xff0c 设置奇偶校验时无法与上位机通讯解决方法 如果串口助手使用串口配置为 xff1a 数据位8 停止位1 有奇偶校验 STM32需设置为 xf
  • 虚拟机使用pc摄像头

    1 win 43 r打开运行 2 在运行中键入services msc回车 xff0c 打开服务 3 右边下拉找到VMware Authorization Service 4 双击打开属性 xff0c 启动类型 gt 自动 xff0c 点击
  • sudo: 无法执行 ./configure: 没有那个文件或目录

    先说下环境 xff0c centos7 xff0c 编译srt时遇到的 看看是否存在这个文件 xff0c 如果存在 ll 查看是否是可执行的 xff0c 如果不可执行 sudo chmod span class token operator
  • 【Unity】为什么要用栈?

    今天看到一个UI界面使用了栈 xff0c 养成了写的习惯 xff0c 但是没有明白后面的道理 xff0c 自己查了很多资料 xff0c 发现很多人都在说后进先出 xff0c 但是也没有比较好的例子和解释 xff0c 直到遇见了这样的一个说法

随机推荐

  • printf打印long long类型数据

    用微妙做单位的话 xff0c 时间戳是16位数字 xff0c 应该用64位整形存储 xff0c long long 如果用printf打印的话 xff0c d默认是int类型 xff0c 打印long long int格式是 lld 打印l
  • h264和h265编码所需要的处理器性能

    h265压缩比为1 200 xff0c h264压缩比为1 100 xff0c 压缩一帧h265理论上比压缩一帧h264多10ms的时间 以下数据均来自实测 在Intel Core i7 6700 CPU 64 3 40GHz 4核8线程中
  • webrtc(native C++) + srs 拉流客户端

    webrtc编译h264使用openh264 解码使用ffmpeg解码 对于vp8 vp9解码也是使用ffmpeg 其实openh264库可以支持解码功能 这点不同于x264 但webrtc选择的是ffmpeg 可能是为了统一吧 首先在编译
  • openssl生成server.crt和server.key

    你可以使用 openssl 命令行工具来生成自签名的 SSL 证书和密钥 以下是步骤 xff1a 打开终端 输入以下命令 xff0c 生成私钥 xff08 server key xff09 xff1a openssl genrsa span
  • srs one2one,one2many通话环境搭建

    一 简介 二 go环境配置 三 srs编译配置 四 信令服务器编译 4 1 signaling8 4 2 web服务器 五 测试 六 附录 官 档参考地址 xff1a https github com ossrs srs wiki v4 C
  • srs 直播连麦环境搭建

    一 简介 二 修改conf rtc conf 三 两个客户端加入房间 四 合流 4 1分别拉流尝试 4 2合流推流 4 3拉取合流 一 简介 直播连麦是指在one2one或one2many进行音视频通话 xff0c 此时把他们的音视频流合在
  • DM-VIO安装与运行各类数据集

    1 论文地址 xff1a https vision in tum de media research vslam dm vio dm vio pdf https vision in tum de media research vslam d
  • sklearn风格的keras接口KerasClassifier、KerasRegressor

    span class token keyword from span tensorflow span class token punctuation span keras span class token punctuation span
  • python与java通信——使用socket模块

    前几天遇到个问题需要用python和java通信 xff0c 网上这种帖子很多 xff0c 比如runtime方法 xff0c py4j方法等 但是runtime方法似乎只能向python传参 xff0c 不能接受python传回 xff1
  • 简单移动平均SMA和指数移动平均EMA

    一 简单移动平均SMA 最近有一个平滑的需求 xff1a 设置平滑期数h xff08 奇数 xff09 xff0c 每期点平滑方法是 xff1a 取该期前后共m期 xff08 含本期 xff09 点的平均值 如果前或后没有足够的点则不用平滑
  • postman基础教程

    目录 一 postman安装说明 1 下载与安装 2 界面导航说明 3 发送第一个请求 二 postman基础功能 1 常见类型的接口请求 1 1 查询参数的接口请求 1 2 表单类型的接口请求 1 3 上传文件的表单请求 1 4 json
  • pandas使用分位数筛选满足条件的行

    分位数计算原理参见 python pandas 分位数 下面直接使用pandas的quantile方法 1 给个例子 span class token keyword import span pandas span class token
  • pandas str.endswith筛选结尾字符串为一个范围内的行

    span class token keyword import span numpy span class token keyword as span np span class token keyword import span pand
  • 聚类集成方法python实现(基于相似度、基于重标记法)

    一 写此篇的背景 有个同学给我两篇论文 基于聚类集成的特征选择方法研究 李玥 基于基聚类器对齐的聚类集成方法研究 杨康 xff0c 基于相似度的方法跟他讲了一遍他自己复现好了 xff0c 但是他觉得他的数据集有几万条 xff0c 时间和空间
  • pandas 如何获得每类A的月末数据B

    一 描述问题 如何取到下面这个dataframe中 xff0c 每一类Code对应的月末数据 df span class token operator 61 span pd span class token punctuation span
  • pandas groupby分组后对每个组进行fillna填值

    一 初始数据如下 xff0c 希望分组后 xff0c 组间数据互不干扰的填充 span class token keyword import span pandas span class token keyword as span pd s
  • VS2022配置Games101作业环境

    一 首先配置opencv4 43 contrib 1 opencv源码下载 访问github上的opencv主页 首先点进第一个opencv 我这里默认就是4 x xff0c 点开可以知道分支为4 x 还需要点Tags 我这里使用的是4 5
  • Games101 VS2022 C++ auto推断不出变量类型

    在写Games101 Homework2的时候 xff0c 下面这句的 auto推断不出3个变量的类型 span class token comment If so use the following code to get the int
  • 密度聚类:OPTICS算法详解

    很多人不理解OPTICS算法绘出的图该怎么理解 为什么波谷就算一类 xff0c 有个波峰又算另一类了 xff0c 本篇在第三部分的第2 3节详细讲这个是什么判别分类的 本篇会添加一些个人思考过程 xff0c 可能有不严谨的地方 xff0c
  • 密度聚类:OPTICS算法简单易懂版

    前几天写了一篇详解版 xff0c 感觉可能太详细了阅读量不高 xff0c 所以修改精简成这篇 很多人不理解OPTICS算法绘出的图该怎么理解 为什么波谷就算一类 xff0c 有个波峰又算另一类了 xff0c 本篇在第三部分的第2 3节详细讲