具有噪声的基于密度的空间聚类算法--DBSCAN
Posted 数据挖掘与分析学习
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了具有噪声的基于密度的空间聚类算法--DBSCAN相关的知识,希望对你有一定的参考价值。
1.DBSCAN简介
DBSCAN(Density-Based Spatial Clustering of Applications with Noise)是一个比较有代表性的基于密度的聚类算法。与划分和层次聚类方法不同,它将簇定义为密度相连的点的最大集合,能够把具有足够高密度的区域划分为簇,并可在噪声的空间数据库中发现任意形状的聚类。
密度聚类算法一般假定类别可以通过样本分布的紧密程度决定。同一类别的样本,他们之间是紧密相连的,也就是说,在该类别任意样本周围不远处一定有同类别的样本存在。
DBSCAN算法利用基于密度的聚类的概念,即要求聚类空间中的一定区域内所包含对象(点或其他空间对象)的数目不小于某一给定阈值。DBSCAN算法的显著优点是聚类速度快且能够有效处理噪声点和发现任意形状的空间聚类。但是由于它直接对整个数据库进行操作且进行聚类时使用了一个全局性的表征密度的参数,因此也具有两个比较明显的弱点:
(1)当数据量增大时,要求较大的内存支持,I/O消耗也很大;
(2)当空间聚类的密度不均匀、聚类间距差相差很大时,聚类质量较差。
2.与传统聚类方法的对比
(1)不需要输入要划分的聚类个数,聚类结果没有偏倚,相对的,K-Means之类的聚类算法初始值对聚类结果有很大影响。
(2)可以对任意形状的稠密数据集进行聚类,相对的,K-Means之类的聚类算法一般只适用于凸数据集。
(3)可以在需要时输入过滤噪声的参数;
(4) 调参相对于传统的K-Means之类的聚类算法稍复杂,主要需要对距离阈值ε,邻域样本数阈值MinPts联合调参,不同的参数组合对最后的聚类效果有较大影响。
3.DBSCAN算法中的几个基本概念
(1)ε邻域:给定对象半径为ε内的区域称为该对象的ε邻域;
(2)核心对象:如果给定对象ε邻域内的样本点数大于等于MinPts,则称该对象为核心对象;
(3)直接密度可达:对于样本集合D,如果样本点q在p的ε邻域内,并且p为核心对象,那么对象q从对象p直接密度可达。
(4)密度可达:对于样本集合D,给定一串样本点p1,p2….pn,p= p1,q= pn,假如对象pi从pi-1直接密度可达,那么对象q从对象p密度可达。
(5)密度相连:存在样本集合D中的一点o,如果对象o到对象p和对象q都是密度可达的,那么p和q密度相联。
可以发现,密度可达是直接密度可达的传递闭包,并且这种关系是非对称的。密度相连是对称关系。DBSCAN目的是找到密度相连对象的最大集合。
例如:从下图可以很容易看出理解上述定义,图中MinPts=5,红色的点都是核心对象,因为其ε邻域至少有5个样本。黑色的样本是非核心对象。所有核心对象密度直达的样本在以红色核心对象为中心的超球体内,如果不在超球体内,则不能密度直达。图中用绿色箭头连起来的核心对象组成了密度可达的样本序列。在这些密度可达的样本序列的ε邻域内所有的样本相互都是密度相连的。
4.算法描述
DBSCAN算法描述:
输入: 包含n个对象的数据库,半径ε,最少数目MinPts;
输出:所有生成的簇,达到密度要求。
(1)Repeat
(2)从数据库中抽出一个未处理的点;
(3)IF抽出的点是核心点 THEN 找出所有从该点密度可达的对象,形成一个簇;
(4)ELSE 抽出的点是边缘点(非核心对象),跳出本次循环,寻找下一个点;
(5)UNTIL 所有的点都被处理。
DBSCAN对用户定义的参数很敏感,细微的不同都可能导致差别很大的结果,而参数的选择无规律可循,只能靠经验确定。
5.算法实现:java实现
(1)Point类
package algorithm.dbscan;
public class Point {
private int x;
private int y;
private boolean isKey;//是否是核心对象
private boolean isClassed;
public boolean isKey()
{
return isKey;
}
public void setKey(boolean isKey)
{
this.isKey=isKey;
this.isClassed=true;
}
public boolean isClassed()
{
return isClassed;
}
public void setClassed(boolean isClassed)
{
this.isClassed=isClassed;
}
public int getX()
{
return x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public void setX(int x) {
this.x = x;
}
public Point(){
x=0;
y=0;
}
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public Point(String str)
{
String[]p=str.split(",");
this.x=Integer.parseInt(p[0]);
this.y=Integer.parseInt(p[1]);
}
public String print()
{
return "<"+this.x+","+this.y+">";
}
}
(2)工具类:Utility
package algorithm.dbscan;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* 定义一个工具类,为算法的实现服务
*
* @author Administrator
*
*/
public class Utility {
// 计算两点之间的距离
public static double getDistance(Point p, Point q) {
int dx = p.getX() - q.getX();
int dy = p.getY() - q.getY();
double distance = Math.sqrt(dx * dx + dy * dy);
return distance;
}
// 检测p点是不是核心点,tmpLst存储核心点的直达点
public static List<Point> isKeyPoint(List<Point> lst, Point p, int e, int minp) {
int count = 0;
List<Point> tmpLst = new ArrayList<>();
for (Iterator<Point> it = lst.iterator(); it.hasNext();) {
Point q = it.next();
if (getDistance(p, q) <= e) {
++count;
if (!tmpLst.contains(q)) {
tmpLst.add(q);
}
}
}
if (count >= minp) {
p.setKey(true);
return tmpLst;
}
return null;
}
// 合并两个列表,前提是b中的核心点包含在a中
public static boolean mergeList(List<Point> a, List<Point> b) {
boolean merge = false;
if (a == null || b == null) {
return false;
}
for (int index = 0; index < b.size(); ++index) {
Point p = b.get(index);
if (p.isKey() && a.contains(p)) {
merge = true;
break;
}
}
if (merge) {
for (int index = 0; index < b.size(); ++index) {
if (!a.contains(b.get(index))) {
a.add(b.get(index));
}
}
}
return merge;
}
// 获取文本中的样本点集合
public static List<Point> getPointsList() throws IOException {
List<Point> lst = new ArrayList<>();
String txtPath = "points.txt";
BufferedReader br = new BufferedReader(new FileReader(txtPath));
String str = "";
while ((str = br.readLine()) != null && str != "") {
lst.add(new Point(str));
}
br.close();
return lst;
}
// 显示聚类的结果
public static void display(List<List<Point>> resultList) {
int index = 1;
for (Iterator<List<Point>> it = resultList.iterator(); it.hasNext();) {
List<Point> lst = it.next();
if (lst.isEmpty()) {
continue;
}
System.out.println("-----第" + index + "个聚类-----");
for (Iterator<Point> it1 = lst.iterator(); it1.hasNext();) {
Point p = it1.next();
System.out.println(p.print());
}
index++;
}
}
}
(3)DBScan类
package algorithm.dbscan;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class DBScan {
private final static int e = 3;// ε邻域半径
private final static int minp =4;// 密度阈值,最少点数
private static List<Point> pointsList = new ArrayList<Point>();// 存储原始样本点
private static List<List<Point>> resultList = new ArrayList<List<Point>>();// 存储最后的聚类结果
private static void applyDbscan() throws IOException {
pointsList = Utility.getPointsList();
for (int index = 0; index < pointsList.size(); ++index) {
List<Point> tmpLst = new ArrayList<Point>();
Point p = pointsList.get(index);
if (p.isClassed())
continue;
tmpLst = Utility.isKeyPoint(pointsList, p, e, minp);
if (tmpLst != null) {
resultList.add(tmpLst);
}
}
int length = resultList.size();
for (int i = 0; i < length; ++i) {
for (int j = 0; j < length; ++j) {
if (i != j) {
if (Utility.mergeList(resultList.get(i), resultList.get(j))) {
resultList.get(j).clear();
}
}
}
}
}
public static void main(String[] args) throws Exception {
applyDbscan();
Utility.display(resultList);
}
}
参考:(1)http://www.cnblogs.com/chaosimple/archive/2013/07/01/3164775.html
(2)https://baike.so.com/doc/3105468-3273248.html
以上是关于具有噪声的基于密度的空间聚类算法--DBSCAN的主要内容,如果未能解决你的问题,请参考以下文章