dbscan算法python实现_挑子学习笔记:DBSCAN算法的python实现
DBSCAN(Density-Based Spatial Clustering of Applications with Noise)聚类算法,是一种基于高密度连通区域的、基于密度的聚类算法,能够将具有足够高密度的区域划分为簇(Cluster),并在具有噪声的数据中发现任意形状的簇。DBSCAN算法通过距离定义出一个密度函数,计算出每个样本附近的密度,从而根据每个样本附近的密度值来找出那些样本相对比较集中的区域,这些区域就是我们要找的簇。
1. DBSCAN算法的基本原理
其它聚类方法大都是基于对象之间的距离进行聚类,聚类结果是球状的簇。DBSCAN 算法利用簇的高密度连通性,寻找被低密度区域分离的高密度区域,可以发现任意形状的簇,其基本思想是:对于一个簇中的每个对象,在其给定半径的领域中包含的对象不能少于某一给定的最小数目。
DBSCAN算法中有两个重要参数:$\varepsilon$表示定义密度时的邻域半径,$M$ 表示定义核心点时的阈值。
考虑数据集合$X= \{x^{(1)}, x^{(2)},...,x^{(n)} \}$,引入以下概念与记号。
1. $\varepsilon$邻域
设$x \in X$,称
\begin{equation*}
N_{\varepsilon}(x) = \{ y \in X: d(y,x) \le \varepsilon \}
\end{equation*}
为$X$的$\varepsilon$邻域。显然,$x \in N_{\varepsilon}(x)$。
为了简单起见,节点$x^{(i)}$与其下标$i$一一对应,引入记号
\begin{equation*}
N_{\varepsilon}(i) = \{ j: d(y^{(j)},x^{(i)}) \le \varepsilon; \; y^{(j)},x^{(i)} \in X \}
\end{equation*}
2. 密度
设$x \in X$,称$\rho(x) = |N_{\varepsilon}(x)|$为$x$的密度。密度是一个整数,且依赖于半径$\varepsilon$。
3. 核心点
设$x \in X$,若$\rho(x) \ge M$,则称$x$为$X$的核心点。记由$X$中所有核心点构成的集合为$X_c$,并记$X_{nc}=X-X_c$表示$X$中所有非核心点构成的集合。
4. 边界点
若$x \in X_{nc}$,且$\exists y \in X$,满足$y \in N_{\varepsilon}(x) \bigcap X_c$,即$X$的非核心点$x$的$\varepsilon$邻域中存在核心点,则称$x$ 为$X$的边界点。记由$X$中所有边界点构成的集合为$X_{bd}$。
此外,边界点也可以这么定义,若$x \in X_{nc}$,且$x$落在某个核心点的$\varepsilon$邻域内,则称$x$为$X$的边界点。一个边界点可能同时落入一个或多个核心点的$\varepsilon$ 邻域内。
5. 噪声点
记$X_{noi} = X - (X_c \bigcup X_{bd})$,若$x \in X_{noi}$,则称$x$为噪音点。
至此,我们严格给出了核心点、边界点和噪音点的数学定义,且满足$X = X_c \bigcup X_{bd} \bigcup X_{noi}$.
图1:核心点、边界点和噪声点
直观地说,核心点对应稠密区域内部的点,边界点对应稠密区域边缘的点,而噪音点对应稀疏区域中的点。
数据集通过聚类形成的子集是簇。核心点位于簇的内部,它确定无误地属于某个特定的簇;噪音点是数据集中的干扰数据,它不属于任何一个簇;边界点是一类特殊的点,它位于一个或几个簇的边缘地带,可能属于一个簇,也可能属于另外一个簇,其归属并不明确。
6. 直接密度可达
设$x,y \in X$. 若满足$x \in X_c$,则称$y$是$x$从直接密度可达的。
7. 密度可达
设$p^{(1)}, p^{(2)},..., p^{(m)} \in X$,其中$m \ge 2$。若它们满足:$p^ {(i+1)}$ 是从$p^{(i)}$直接密度可达的,其中$i = 1,2,...,m-1$,则称$p^ {(m)}$ 是从$p^{(1)}$ 中密度可达的。
7.1. 当$m = 2$时,密度可达即为直接密度可达。实际上,密度可达是直接密度可达的传递闭包。
7.2. 密度可达关系不具有对称性。若$p^{(m)}$是从$p^{(1)}$密度可达的,那么$p^ {(1)}$ 不一定是从$p^{(m)}$密度可达的。根据上述定义可知,$p^{(1)}, p^{(2)}, ..., p^{(m-1)}$必须为核心点,而$p^{(m)}$可以是核心点,也可以是边界点。当$p^ {(m)}$是边界点时,$p^{(1)}$一定不是从$p^{(m)}$密度可达的。
8. 密度相连
设$x,y,z \in X$,若$y$和$z$均是从$x$密度可达的,则称$y$和$z$是密度相连的。显然,密度相连具有对称性。
9. 簇(cluster)
非空集合$C \subset X$,如果$C$满足:对于$x,y \in X$
若$x \in C$,且$y$是从$x$密度可达的,则$y \in C$,
若$x \in C$,$y \in C$,则$x,y$是密度相连的。
则称$C$是$X$的一个簇。
DBSCAN 算法基于以下一个基本事实:对于任一核心点$x$,数据集$X$中所有从$x$ 密度可达的数据点可以构成一个完整的簇$C$,且$x \in C$。其核心思想描述如下:从某个选定的核心点出发,不断向密度可达的区域扩张,从而得到一个包含核心点和边界点的最大化区域,区域中任意两点密度相连。
2. DBSCAN算法的实现
《数据挖掘概念与技术》给出的算法伪代码如下:
考虑数据集合$X= \{x^{(1)}, x^{(2)},..., x^{(n)} \}$。DBSCAN算法的目标是将数据集合$X$分成$K$个簇及噪声点集合,其中$K$也是由算法得到,为此,引入簇的标记数组
\begin{equation*}
m_i =
\begin{cases}
j, & \text{若}x^{(i)}\text{属于第}j\text{个簇}; \\
-1, & \text{若}x^{(i)}\text{为噪声点}
\end{cases}
\end{equation*}
DBSCAN算法的目标就是生成标记数组$m_i, \; i=1,...,n$.
为了保证可以更有效地实现算法1中第3句随机选择一个unvisited对象$p$,设计了一个数据结构visitlist,其中包含两个列表visitedlist和unvisitedlist,分别用于存储已访问的点和未访问的点,每次从unvisitedlist 中取点可以保证每次取到的点都是未访问过的点,实现代码如下:
代码1:visitlist数据结构
1 #visitlist类用于记录访问列表
2 #unvisitedlist记录未访问过的点
3 #visitedlist记录已访问过的点
4 #unvisitednum记录访问过的点数量
5 classvisitlist:6 def _init_(self, count=0):7 self.unvisitedlist=[i for i inrange(count)]8 self.visitedlist=list()9 self.unvisitednum=count10
11 defvisit(self, pointId):12 self.visitedlist.append(pointId)13 self.unvisitedlist.remove(pointId)14 self.unvisitednum -= 1
DBSCAN算法实现代码如下:
代码2:DBSCAN算法实现
1 importnumpy as np2 importmatplotlib.pyplot as plt3 importmath4 importrandom5
6 defdist(a, b):7 #计算a,b两个元组的欧几里得距离
8 return math.sqrt(np.power(a-b, 2).sum())9
10 defmy_dbscanl(dataSet, eps, minPts):11 #numpy.ndarray的 shape属性表示矩阵的行数与列数
12 nPoints =dataSet.shape[0]13 #(1)标记所有对象为unvisited
14 #在这里用一个类vPoints进行买现
15 vPoints = visitlist(count=nPoints)16 #初始化簇标记列表C,簇标记为 k
17 k = -1
18 C = [-1 for i inrange(nPoints)]19 while(vPoints.unvisitednum >0):20 #(3)随机上选择一个unvisited对象p
21 P =random.choice(vPoints.unvisitedlist)22 #(4)标记p为visited
23 vPoints.visit(p)24 #(5)if p的$\varepsilon$-邻域至少有MinPts个对象
25 #N是p的$\varepsilon$-邻域点列表
26 N = [i for i in range(nPoints) if dist(dataSet[i], dataSet[p])<=eps]27 if len(N) >=minPts:28 #(6)创建个新簇C,并把p添加到C
29 #这里的C是一个标记列表,直接对第p个结点进行赋植
30 k += 1
31 C[p]=k32 #(7)令N为p的ε-邻域中的对象的集合
33 #N是p的$\varepsilon$-邻域点集合
34 #(8) for N中的每个点p'
35 for p1 inN:36 #(9) if p'是unvisited
37 if p1 invPoints.unvisitedlist:38 #(10)标记p’为visited
39 vPoints.visit(p1)40 #(11) if p'的$\varepsilon$-邻域至少有MinPts个点,把这些点添加到N
41 #找出p'的$\varepsilon$-邻域点,并将这些点去重添加到N
42 M=[i for i in range(nPoints) ifdist(dataSet[i], \43 dataSet[p1]) <=eps]44 if len(M) >=minPts:45 for i inM:46 if i not inN:47 N.append(i)48 #(12) if p'还不是任何簇的成员,把P'添加到C
49 #C是标记列表,直接把p'分到对应的簇里即可
50 if C[p1] == -1:51 C[p1]=k52 #(15)else标记p为噪声
53 else:54 C[p]=-1
55
56 #(16)until没有标t己为unvisitedl内对象
57 return C
利用sklearn生成数据集,共2500条数据,并利用matplotlib画出散点图,代码如下:
代码3:生成数据集
1 importnumpy as np2 importmatplotlib.pyplot as plt3 from sklearn importdatasets4
5 X1, Y1 = datasets.make_circles(n_samples=2000, factor=0.6, noise=0.05,6 random_state=1)7 X2, Y2 = datasets.make_blobs(n_samples=500, n_features=2, centers=[[1.5,1.5]],8 cluster_std=[[0.1]], random_state=5)9
10 X =np.concatenate((X1, X2))11 plt.figure(figsize=(12, 9), dpi=80)12 plt.scatter(X[:,0], X[:,1], marker='.')13 plt.show()
图2:数据集散点图
设置参数Eps=0.1, MinPts=10,聚类结果如下图:
图3:聚类结果
3. 利用KD树进行优化
KD树(K-Dimensional Tree),是一种分割k维数据空间的数据结构,是二叉搜索树在多维条件下的推广。主要应用于多维空间关键数据的搜索。KD树的介绍见:https://www.jianshu.com/p/ffe52db3e12b,不赘述。
利用scipy实现KD树的构造和查询,对代码2的算法进行改进,代码如下:
代码4:DBSCAN算法的优化实现
1 importnumpy as np2 importmatplotlib.pyplot as plt3 importmath4 importrandom5 from scipy.spatial importKDTree6
7 def my-dbscan2(dataSet, eps, minPts):8 #numpy.ndarray的 shape属性表示矩阵的行数与列数
9 #行数即表小所有点的个数
10 nPoints =dataSet.shape[0]11 #(1) 标记所有对象为unvisited
12 #在这里用一个类vPoints进行实现
13 vPoints = visitlist(count=nPoints)14 #初始化簇标记列表C,簇标记为 k
15 k = -1
16 C = [-1 for i inrange(nPoints)]17 #构建KD-Tree,并生成所有距离<=eps的点集合
18 kd =KDTree(X)19 while(vPoints.unvisitednum>0):20 #(3) 随机选择一个unvisited对象p
21 p =random.choice(vPoints.unvisitedlist)22 #(4) 标t己p为visited
23 vPoints.visit(p)24 #(5) if p 的$\varepsilon$-邻域至少有MinPts个对象
25 #N是p的$\varepsilon$-邻域点列表
26 N =kd.query_ball_point(dataSet[p], eps)27 if len(N) >=minPts:28 #(6) 创建个一个新簇C,并把p添加到C
29 #这里的C是一个标记列表,直接对第p个结点进行赋值
30 k += 1
31 C[p] =k32 #(7) 令N为p的$\varepsilon$-邻域中的对象的集合
33 #N是p的$\varepsilon$-邻域点集合
34 #(8) for N中的每个点p'
35 for p1 inN:36 #(9) if p'是unvisited
37 if p1 invPoints.unvisitedlist:38 #(10) 标记p'为visited
39 vPoints.visit(p1)40 #(11) if p'的$\varepsilon$-邻域至少有MinPts个点,把这些点添加到N
41 #找出p'的$\varepsilon$-邻域点,并将这些点去重新添加到N
42 M =kd.query_ball_point(dataSet[p1], eps)43 if len(M) >=minPts:44 for i inM:45 if i not inN:46 N.append(i)47 #(12) if p'还不是任何簇的成员,把p'添加到c
48 #C是标记列表,直接把p'分到对应的簇里即可
49 if C[p1] == -1
50 C[p1] =k51 #(15) else标记p为噪声
52 else:53 C[p1] = -1
54
55 #(16) until没有标记为unvisited的对象
56 return C
以代码3中生成的2500条数据作为测试,比较优化前后的算法性能
1 importtime2 start =time.time()3 C1 = my_dbscanl(X, 0.1, 10)4 end =time.time()5 print "`运行时间`:", end -start6 plt.scatter(X[:, 0], X[:, 1], c=C1, marker='.')7 plt.show()8 >>> `运行时间:`29.1249849796
图4:优化前算法结果
1 importtime2 start =time.time()3 C2 = my_dbscan2(X, 0.1, 10)4 end =time.time()5 print "运行时间:", end -start6 plt.scatter(X[:, 0], X[:, 1], c=C2, marker='.')7 plt.show()8 >>> 运行时间:4.72340583801
图5:优化后算法结果
可以看到优化后的算法运行时间从29.12s降到了4.72s,优化的效果非常明显。
4. 后记
上文仅仅是对DBSCAN算法的思想与实现进行了简略摘要,是学习算法的一个过程。算法的学习还比较粗劣和浅层,在实践应用中上述代码并不实用。如果需要使用DBSCAN的算法求解聚类问题,建议使用sklearn自带的DBSCAN函数。以代码3中生成数据为例:
1 #DBSCAN eps = 0.1, MinPts = 10
2 importtime3 from sklearn.cluster importDBSCAN4 start =time.time()5 C = DBSCAN(eps=0.1, min_pts=10).6 end =time.time()7 print "运行时间:", end -start8 plt.scatter(X[:, 0], X[:, 1], c=C, marker='.')9 plt.show()10 >>> 运行时间:0.0240921974182
dbscan算法python实现_挑子学习笔记:DBSCAN算法的python实现相关推荐
- python 两阶段聚类_挑子学习笔记:两步聚类算法(TwoStep Cluster Algorithm)——改进的BIRCH算法...
转载请标明出处:http://www.cnblogs.com/tiaozistudy/p/twostep_cluster_algorithm.html 两步聚类算法是在SPSS Modeler中使用的 ...
- java基于聚类的离群点检测_挑子学习笔记:基于两步聚类的离群点检测
转载请标明出处:http://www.cnblogs.com/tiaozistudy/p/anomaly_detection.html 本文主要针对IBM SPSS Modeler 18.0中离群点检 ...
- Python 3.4.4 学习笔记(004)python manuals/the python tutorial -- 3. An Informal Introduction to Python...
可以看到python版本号变了,找到一本学习python的书,Mark Lutz所著的Python学习手册,建议我直接学习最新版本的Python,到网站上看了一下,是3.5.1,但是不支持XP,能支持 ...
- linux查看python环境_运维笔记linux环境提示python: command not found hello
场景描述: 新部署的容器环境,终端执行python命令,提示没有该命令. 从报错异常可以看出,可能是python环境未安装. 分析思路: 检查python路径: 方式一:type -a python ...
- smbus使用 树莓派_树莓派学习笔记——I2C使用 PCF8574 Python SMBUS
1.前言 树莓派的GPIO端口数量有限,可通过IO扩展芯片增加GPIO的数量,使得树莓派可以适应更多的应用.PCF8574为一款通过I2C总线扩展IO的芯片,单个PCF8574可扩展8个IO,一个I2 ...
- 动态规划算法实验报告_强化学习之动态规划算法
如今的强化学习研究大体分为了两个研究学派:一个是以Sutton,Sliver等人为代表的value-based学派,他们主要从值函数近似角度入手去研究强化学习,这也是强化学习早期最初发展起来时沿用的路 ...
- c语言算法有效性,BerForest—C语言学习笔记-《算法》
这是我学习C语言的笔记,也可以算是回忆录,反正有利于我的学习,也可以让C语言的新手借鉴. 许多人都在盲目的学习编程,其实学习编程无为就是学习一些编程语法.即使学会了,也不一定能够自己独立的编写出程序了 ...
- 数据结构和算法学多久_重新学习数据结构和算法
数据结构和算法学多久 为什么? 我记得在我第一次参加计算机科学算法课程时 伊丽莎白市州立大学(ECSU)认为:"我得到了什么 我自己变成了?!". 材料令人生畏,而且(大部分时间) ...
- 深度学习(DL)与卷积神经网络(CNN)学习笔记随笔-03-基于Python的LeNet之LR
原地址可以查看更多信息 本文主要参考于:Classifying MNIST digits using Logistic Regression python源代码(GitHub下载 CSDN免费下载) ...
最新文章
- 对“单子模式”的补充
- HDU 1426 Sudoku Killer【DFS 数独】
- hdu 6148 数位dp
- C++ primer 详解(第三章)
- 微信浏览器打开网页被拦截了?Mindjump快速解决微信屏蔽网址用户打不开的难题...
- 开源纯C#轻量级数据库引擎:SharpHSQL 1.0.3.0版本
- 一个简单字符型设备驱动及其测试
- sh mysql configure_【翻译自mos文章】使用config.sh/config.bat来configureorre-con
- add函数python怎么用_Python add()函数是如何使用呢?
- php 网关接口,[PHP] 通用网关接口CGI 的运行原理
- 2. COM编程——什么是接口
- 如何把门禁卡做成你用不起的样子?B站up主自制迷你卡片,公司小区通刷,还带墨水屏的那种...
- 武汉加油——传染病模型拟合
- 方志仅占古籍数量十分之一,在家谱编修中却举足轻重,方志凭什么
- 【超详细】初中高级软件测试工程师 都需要掌握哪些测试技能
- 更换ICCID码破解Apple运营商锁策略分析
- LaTeX中正负号写法
- Cesium雷达追踪圆锥体
- 阿里云视频点播的使用(SDK调用示例的运行)
- 鼓励参与计算机考试宣传标语,期末考试励志宣传标语
热门文章
- 免推北京大学计算机研究生,北大推免研究生个人陈述
- mysql文件说明_MySQL进阶之配置文件说明
- charles请求转发_用免费开源的frp实现内网穿透,使用nginx转发的方式去掉端口号...
- 百度步行导航加poi搜索android,【百度地图】带地图显示控件、导航控件、POI查找控件...
- java 添加图片背景_java添加背景图片
- cmake 找不到 macros_愿我们在彼此看不到的岁月里熠熠生辉
- 『操作系统』 进程的描述与控制 Part 1 前驱图与程序执行
- ACM算法--枚举方法(指数枚举,组合枚举)模板
- codeforces 1287A -Angry Students(模拟)
- Python+Opencv实现无参数、全自动的Canny算法