dbscan基于密度的空间聚类算法

参考文献:百度百科 http://baike.baidu.com

我的算法库:https://github.com/linyiqun/lyq-algorithms-lib 

算法介绍

说到聚类算法,大家如果有看过我写的一些关于机器学习的算法文章,一定都这类算法不会陌生,之前将的是划分算法(K均值算法)和层次聚类算法(BIRCH算法),各有优缺点和好坏。本文所述的算法是另外一类的聚类算法,他能够克服BIRCH算法对于形状的限制,因为BIRCH算法偏向于聚簇球形的聚类形成,而dbscan采用的是基于空间的密度的原理,所以可以适用于任何形状的数据聚类实现。

算法原理

在介绍算法原理之前,先介绍几个dbscan算法中的几个概念定义:

Ε领域:给定对象半径为Ε内的区域称为该对象的Ε领域;
核心对象:如果给定对象Ε领域内的样本点数大于等于MinPts,则称该对象为核心对象;
直接密度可达:对于样本集合D,如果样本点q在p的Ε领域内,并且p为核心对象,那么对象q从对象p直接密度可达。
密度可达:对于样本集合D,给定一串样本点p1,p2….pn,p= p1,q= pn,假如对象pi从pi-1直接密度可达,那么对象q从对象p密度可达。
密度相连:存在样本集合D中的一点o,如果对象o到对象p和对象q都是密度可达的,那么p和q密度相联。

下面是算法的过程(可能说的不是很清楚):

1、扫描原始数据,获取所有的数据点。

2、遍历数据点中的每个点,如果此点已经被访问(处理)过,则跳过,否则取出此点做聚类查找。

3、以步骤2中找到的点P为核心对象,找出在E领域内所有满足条件的点,如果个数大于等于MinPts,则此点为核心对象,加入到簇中。

4、再次P为核心对象的簇中的每个点,进行递归的扩增簇。如果P点的递归扩增结束,再次回到步骤2。

5、算法的终止条件为所有的点都被访问(处理过)。

算法可以理解为是一个DFS的深度优先扩展。

算法的实现

算法的输入Input(格式(x, y)):

[java]  view plain copy print ?
  1. 2 2  
  2. 3 1  
  3. 3 4  
  4. 3 14  
  5. 5 3  
  6. 8 3  
  7. 8 6  
  8. 9 8  
  9. 10 4  
  10. 10 7  
  11. 10 10  
  12. 10 14  
  13. 11 13  
  14. 12 8  
  15. 12 15  
  16. 14 7  
  17. 14 9  
  18. 14 15  
  19. 15 8  

坐标点类Point.java:

[java]  view plain copy print ?
  1. package DataMining_DBSCAN;  
  2.   
  3. /** 
  4.  * 坐标点类 
  5.  *  
  6.  * @author lyq 
  7.  *  
  8.  */  
  9. public class Point {  
  10.     // 坐标点横坐标  
  11.     int x;  
  12.     // 坐标点纵坐标  
  13.     int y;  
  14.     // 此节点是否已经被访问过  
  15.     boolean isVisited;  
  16.   
  17.     public Point(String x, String y) {  
  18.         this.x = (Integer.parseInt(x));  
  19.         this.y = (Integer.parseInt(y));  
  20.         this.isVisited = false;  
  21.     }  
  22.   
  23.     /** 
  24.      * 计算当前点与制定点之间的欧式距离 
  25.      *  
  26.      * @param p 
  27.      *            待计算聚类的p点 
  28.      * @return 
  29.      */  
  30.     public double ouDistance(Point p) {  
  31.         double distance = 0;  
  32.   
  33.         distance = (this.x - p.x) * (this.x - p.x) + (this.y - p.y)  
  34.                 * (this.y - p.y);  
  35.         distance = Math.sqrt(distance);  
  36.   
  37.         return distance;  
  38.     }  
  39.   
  40.     /** 
  41.      * 判断2个坐标点是否为用个坐标点 
  42.      *  
  43.      * @param p 
  44.      *            待比较坐标点 
  45.      * @return 
  46.      */  
  47.     public boolean isTheSame(Point p) {  
  48.         boolean isSamed = false;  
  49.   
  50.         if (this.x == p.x && this.y == p.y) {  
  51.             isSamed = true;  
  52.         }  
  53.   
  54.         return isSamed;  
  55.     }  
  56. }  

算法工具类DNSCANTool.java:

[java]  view plain copy print ?
  1. package DataMining_DBSCAN;  
  2.   
  3. import java.io.BufferedReader;  
  4. import java.io.File;  
  5. import java.io.FileReader;  
  6. import java.io.IOException;  
  7. import java.text.MessageFormat;  
  8. import java.util.ArrayList;  
  9.   
  10. /** 
  11.  * DBSCAN基于密度聚类算法工具类 
  12.  *  
  13.  * @author lyq 
  14.  *  
  15.  */  
  16. public class DBSCANTool {  
  17.     // 测试数据文件地址  
  18.     private String filePath;  
  19.     // 簇扫描半径  
  20.     private double eps;  
  21.     // 最小包含点数阈值  
  22.     private int minPts;  
  23.     // 所有的数据坐标点  
  24.     private ArrayList<Point> totalPoints;  
  25.     // 聚簇结果  
  26.     private ArrayList<ArrayList<Point>> resultClusters;  
  27.     //噪声数据  
  28.     private ArrayList<Point> noisePoint;  
  29.   
  30.     public DBSCANTool(String filePath, double eps, int minPts) {  
  31.         this.filePath = filePath;  
  32.         this.eps = eps;  
  33.         this.minPts = minPts;  
  34.         readDataFile();  
  35.     }  
  36.   
  37.     /** 
  38.      * 从文件中读取数据 
  39.      */  
  40.     public void readDataFile() {  
  41.         File file = new File(filePath);  
  42.         ArrayList<String[]> dataArray = new ArrayList<String[]>();  
  43.   
  44.         try {  
  45.             BufferedReader in = new BufferedReader(new FileReader(file));  
  46.             String str;  
  47.             String[] tempArray;  
  48.             while ((str = in.readLine()) != null) {  
  49.                 tempArray = str.split(" ");  
  50.                 dataArray.add(tempArray);  
  51.             }  
  52.             in.close();  
  53.         } catch (IOException e) {  
  54.             e.getStackTrace();  
  55.         }  
  56.   
  57.         Point p;  
  58.         totalPoints = new ArrayList<>();  
  59.         for (String[] array : dataArray) {  
  60.             p = new Point(array[0], array[1]);  
  61.             totalPoints.add(p);  
  62.         }  
  63.     }  
  64.   
  65.     /** 
  66.      * 递归的寻找聚簇 
  67.      *  
  68.      * @param pointList 
  69.      *            当前的点列表 
  70.      * @param parentCluster 
  71.      *            父聚簇 
  72.      */  
  73.     private void recursiveCluster(Point point, ArrayList<Point> parentCluster) {  
  74.         double distance = 0;  
  75.         ArrayList<Point> cluster;  
  76.   
  77.         // 如果已经访问过了,则跳过  
  78.         if (point.isVisited) {  
  79.             return;  
  80.         }  
  81.   
  82.         point.isVisited = true;  
  83.         cluster = new ArrayList<>();  
  84.         for (Point p2 : totalPoints) {  
  85.             // 过滤掉自身的坐标点  
  86.             if (point.isTheSame(p2)) {  
  87.                 continue;  
  88.             }  
  89.   
  90.             distance = point.ouDistance(p2);  
  91.             if (distance <= eps) {  
  92.                 // 如果聚类小于给定的半径,则加入簇中  
  93.                 cluster.add(p2);  
  94.             }  
  95.         }  
  96.   
  97.         if (cluster.size() >= minPts) {  
  98.             // 将自己也加入到聚簇中  
  99.             cluster.add(point);  
  100.             // 如果附近的节点个数超过最下值,则加入到父聚簇中,同时去除重复的点  
  101.             addCluster(parentCluster, cluster);  
  102.   
  103.             for (Point p : cluster) {  
  104.                 recursiveCluster(p, parentCluster);  
  105.             }  
  106.         }  
  107.     }  
  108.   
  109.     /** 
  110.      * 往父聚簇中添加局部簇坐标点 
  111.      *  
  112.      * @param parentCluster 
  113.      *            原始父聚簇坐标点 
  114.      * @param cluster 
  115.      *            待合并的聚簇 
  116.      */  
  117.     private void addCluster(ArrayList<Point> parentCluster,  
  118.             ArrayList<Point> cluster) {  
  119.         boolean isCotained = false;  
  120.         ArrayList<Point> addPoints = new ArrayList<>();  
  121.   
  122.         for (Point p : cluster) {  
  123.             isCotained = false;  
  124.             for (Point p2 : parentCluster) {  
  125.                 if (p.isTheSame(p2)) {  
  126.                     isCotained = true;  
  127.                     break;  
  128.                 }  
  129.             }  
  130.   
  131.             if (!isCotained) {  
  132.                 addPoints.add(p);  
  133.             }  
  134.         }  
  135.   
  136.         parentCluster.addAll(addPoints);  
  137.     }  
  138.   
  139.     /** 
  140.      * dbScan算法基于密度的聚类 
  141.      */  
  142.     public void dbScanCluster() {  
  143.         ArrayList<Point> cluster = null;  
  144.         resultClusters = new ArrayList<>();  
  145.         noisePoint = new ArrayList<>();  
  146.           
  147.         for (Point p : totalPoints) {  
  148.             if(p.isVisited){  
  149.                 continue;  
  150.             }  
  151.               
  152.             cluster = new ArrayList<>();  
  153.             recursiveCluster(p, cluster);  
  154.   
  155.             if (cluster.size() > 0) {  
  156.                 resultClusters.add(cluster);  
  157.             }else{  
  158.                 noisePoint.add(p);  
  159.             }  
  160.         }  
  161.         removeFalseNoise();  
  162.           
  163.         printClusters();  
  164.     }  
  165.       
  166.     /** 
  167.      * 移除被错误分类的噪声点数据 
  168.      */  
  169.     private void removeFalseNoise(){  
  170.         ArrayList<Point> totalCluster = new ArrayList<>();  
  171.         ArrayList<Point> deletePoints = new ArrayList<>();  
  172.           
  173.         //将聚簇合并  
  174.         for(ArrayList<Point> list: resultClusters){  
  175.             totalCluster.addAll(list);  
  176.         }   
  177.           
  178.         for(Point p: noisePoint){  
  179.             for(Point p2: totalCluster){  
  180.                 if(p2.isTheSame(p)){  
  181.                     deletePoints.add(p);  
  182.                 }  
  183.             }  
  184.         }  
  185.           
  186.         noisePoint.removeAll(deletePoints);  
  187.     }  
  188.   
  189.     /** 
  190.      * 输出聚类结果 
  191.      */  
  192.     private void printClusters() {  
  193.         int i = 1;  
  194.         for (ArrayList<Point> pList : resultClusters) {  
  195.             System.out.print("聚簇" + (i++) + ":");  
  196.             for (Point p : pList) {  
  197.                 System.out.print(MessageFormat.format("({0},{1}) ", p.x, p.y));  
  198.             }  
  199.             System.out.println();  
  200.         }  
  201.           
  202.         System.out.println();  
  203.         System.out.print("噪声数据:");  
  204.         for (Point p : noisePoint) {  
  205.             System.out.print(MessageFormat.format("({0},{1}) ", p.x, p.y));  
  206.         }  
  207.         System.out.println();  
  208.     }  
  209. }  
测试类Client.java:

[java]  view plain copy print ?
  1. package DataMining_DBSCAN;  
  2.   
  3. /** 
  4.  * Dbscan基于密度的聚类算法测试类 
  5.  * @author lyq 
  6.  * 
  7.  */  
  8. public class Client {  
  9.     public static void main(String[] args){  
  10.         String filePath = "C:\\Users\\lyq\\Desktop\\icon\\input.txt";  
  11.         //簇扫描半径  
  12.         double eps = 3;  
  13.         //最小包含点数阈值  
  14.         int minPts = 3;  
  15.           
  16.         DBSCANTool tool = new DBSCANTool(filePath, eps, minPts);  
  17.         tool.dbScanCluster();  
  18.     }  
  19. }  
算法的输出:

[java]  view plain copy print ?
  1. 聚簇1:(2,2) (3,4) (5,3) (3,1) (8,3) (8,6) (10,4) (9,8) (10,7) (10,10) (12,8) (14,7) (14,9) (15,8)   
  2. 聚簇2:(10,14) (11,13) (14,15) (12,15)   
  3.   
  4. 噪声数据:(3,14)   
图示结果如下:

dbscan基于密度的空间聚类算法_第1张图片

算法的缺点

dbscan虽说可以用于任何形状的聚类发现,但是对于密度分布不均衡的数据,变化比较大,分类的性能就不会特别好,还有1点是不能反映高尺寸数据。

你可能感兴趣的:(dbscan基于密度的空间聚类算法)