dbscan算法是一种基于密度的聚类算法。
该算法的目的在于过滤低密度区域,发现稠密度样本点,跟传统的基于层次聚类和划分聚类的凸形聚类簇不同,该算法可以发现任意形状的聚类簇,与传统的算法相比它有如下优点:
与K-means比较起来,你不必输入你要划分的聚类个数;
聚类簇的形状没有bias;
可以在需要时输入过滤噪声的参数;
DBSCAN中的的几个定义:
Ε领域:给定对象半径为Ε内的区域称为该对象的Ε领域
核心对象:如果给定对象Ε领域内的样本点数大于等于MinPts,则称该对象为核心对象。
直接密度可达:对于样本集合D,如果样本点q在p的Ε领域内,并且p为核心对象,那么对象q从对象p直接密度可达。
密度可达:对于样本集合D,给定一串样本点p1,p2….pn,p= p1,q= pn,假如对象pi从pi-1直接密度可达,那么对象q从对象p密度可达。
密度相连:对于样本集合D中的任意一点O,如果存在对象p到对象o密度可达,并且对象q到对象o密度可达,那么对象q到对象p密度相连。
可以发现,密度可达是直接密度可达的传递闭包,并且这种关系是非对称的。密度相连是对称关系。DBSCAN目的是找到密度相连对象的最大集合。
Eg: 假设半径Ε=3,MinPts=3,点p的E领域中有点{m,p,p1,p2,o}, 点m的E领域中有点{m,q,p,m1,m2},点q的E领域中有点{q,m},点o的E领域中有点{o,p,s},点s的E领域中有点{o,s,s1}.
那么核心对象有p,m,o,s(q不是核心对象,因为它对应的E领域中点数量等于2,小于MinPts=3);
点m从点p直接密度可达,因为m在p的E领域内,并且p为核心对象;
点q从点p密度可达,因为点q从点m直接密度可达,并且点m从点p直接密度可达;
点q到点s密度相连,因为点q从点p密度可达,并且s从点p密度可达。
算法描述:
算法:DBSCAN
输入:E — 半径
输出:目标类簇集合
方法:repeat
1)
2)
优缺点:
DBSCAN算法的显著优点是聚类速度快且能够有效处理噪声点和发现任意形状的空间聚类。但是由于它直接对整个数据库进行操作且进行聚类时使用了一个全局性的表征密度的参数,因此也具有两个比较明显的弱点:
(1)当数据量增大时,要求较大的内存支持I/O消耗也很大;
(2)当空间聚类的密度不均匀、聚类间距差相差很大时,聚类质量较差。
2、DBSCAN和传统聚类算法对比
DBSCAN算法的目的在于过滤低密度区域,发现稠密度样本点。跟传统的基于层次的聚类和划分聚类的凸形聚类簇不同,该算法可以发现任意形状的聚类簇,与传统的算法相比它有如下优点:
(1)与K-MEANS比较起来,不需要输入要划分的聚类个数;
(2)聚类簇的形状没有偏倚;
(3)可以在需要时输入过滤噪声的参数;
R语言实现,来自fpc包中的例子:
<span style="font-size:18px;background-color: rgb(255, 255, 255);">install.packages("fpc")
library(fpc)
set.seed(665544)
n <- 600
x <- cbind(runif(10, 0, 10)+rnorm(n, sd=0.2), runif(10, 0, 10)+rnorm(n,sd=0.2))
ds <- dbscan(x, 0.2)
# run with showplot=1 to see how dbscan works.
ds
plot(ds, x)
x2 <- matrix(0,nrow=4,ncol=2)
x2[1,] <- c(5,2)
x2[2,] <- c(8,3)
x2[3,] <- c(5,8)
x2[4,] <- c(9,4)
predict(ds, x, x2)</span>
DBScan算法的核心代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
|
package orisun;
import java.io.File;
import java.util.ArrayList;
import java.util.Vector;
import java.util.Iterator;
public class DBScan {
double Eps= 3 ; //区域半径
int MinPts= 4 ; //密度
//由于自己到自己的距离是0,所以自己也是自己的neighbor
public Vector<DataObject> getNeighbors(DataObject p,ArrayList<DataObject> objects){
Vector<DataObject> neighbors= new Vector<DataObject>();
Iterator<DataObject> iter=objects.iterator();
while (iter.hasNext()){
DataObject q=iter.next();
double [] arr1=p.getVector();
double [] arr2=q.getVector();
int len=arr1.length;
if (Global.calEditDist(arr1,arr2,len)<=Eps){ //使用编辑距离
// if(Global.calEuraDist(arr1, arr2, len)<=Eps){ //使用欧氏距离
// if(Global.calCityBlockDist(arr1, arr2, len)<=Eps){ //使用街区距离
// if(Global.calSinDist(arr1, arr2, len)<=Eps){ //使用向量夹角的正弦
neighbors.add(q);
}
}
return neighbors;
}
public int dbscan(ArrayList<DataObject> objects){
int clusterID= 0 ;
boolean AllVisited= false ;
while (!AllVisited){
Iterator<DataObject> iter=objects.iterator();
while (iter.hasNext()){
DataObject p=iter.next();
if (p.isVisited())
continue ;
AllVisited= false ;
p.setVisited( true ); //设为visited后就已经确定了它是核心点还是边界点
Vector<DataObject> neighbors=getNeighbors(p,objects);
if (neighbors.size()<MinPts){
if (p.getCid()<= 0 )
p.setCid(- 1 ); //cid初始为0,表示未分类;分类后设置为一个正数;设置为-1表示噪声。
} else {
if (p.getCid()<= 0 ){
clusterID++;
expandCluster(p,neighbors,clusterID,objects);
} else {
int iid=p.getCid();
expandCluster(p,neighbors,iid,objects);
}
}
AllVisited= true ;
}
}
return clusterID;
}
private void expandCluster(DataObject p, Vector<DataObject> neighbors,
int clusterID,ArrayList<DataObject> objects) {
p.setCid(clusterID);
Iterator<DataObject> iter=neighbors.iterator();
while (iter.hasNext()){
DataObject q=iter.next();
if (!q.isVisited()){
q.setVisited( true );
Vector<DataObject> qneighbors=getNeighbors(q,objects);
if (qneighbors.size()>=MinPts){
Iterator<DataObject> it=qneighbors.iterator();
while (it.hasNext()){
DataObject no=it.next();
if (no.getCid()<= 0 )
no.setCid(clusterID);
}
}
}
if (q.getCid()<= 0 ){ //q不是任何簇的成员
q.setCid(clusterID);
}
}
}
public static void main(String[] args){
DataSource datasource= new DataSource();
//Eps=3,MinPts=4
datasource.readMatrix( new File( "/home/orisun/test/dot.mat" ));
datasource.readRLabel( new File( "/home/orisun/test/dot.rlabel" ));
//Eps=2.5,MinPts=4
// datasource.readMatrix(new File("/home/orisun/text.normalized.mat"));
// datasource.readRLabel(new File("/home/orisun/text.rlabel"));
DBScan ds= new DBScan();
int clunum=ds.dbscan(datasource.objects);
datasource.printResult(datasource.objects,clunum);
}
}
|