Chameleon两阶段聚类算法

参考文献:http://www.cnblogs.com/zhangchaoyang/articles/2182752.html(用了很多的图和思想)
博客园(华夏35度) 作者:Orisun
数据挖掘算法-Chameleon算法.百度文库

我的算法库:https://github.com/linyiqun/lyq-algorithms-lib(里面可能有你正想要的算法)

算法介绍

本篇文章讲述的还是聚类算法,也是属于层次聚类算法领域的,不过与上篇文章讲述的分裂实现聚类的方式不同,这次所讲的Chameleon算法是合并形成最终的聚类,恰巧相反。Chamelon的英文单词的意思是变色龙,所以这个算法又称之为变色龙算法,变色龙算法的过程如标题所描绘的那样,是分为2个主要阶段的,不过他可不是像BIRCH算法那样,是树的形式。继续看下面的原理介绍。

算法原理

先来张图来大致了解整个算法的过程。

Chameleon两阶段聚类算法_第1张图片

上面图的显示过程虽然说有3个阶段,但是这其中概况起来就是两个阶段,第一个是形成小簇集的过程就是从Data Set 到k最近邻图到分裂成小聚餐,第二个阶段是合并这些小聚簇形成最终的结果聚簇。理解了算法的大致过程,下面看看里面定义的一些概念,还不少的样子。

为了引出变色龙算法的一些定义,这里先说一下以往的一些聚类算法的不足之处。

1、忽略簇与簇之间的互连性。就会导致最终的结果形成如下:

Chameleon两阶段聚类算法_第2张图片

2、忽略簇与簇之间的近似性。就会导致最终的聚类结果变成这样“:

Chameleon两阶段聚类算法_第3张图片

为什么提这些呢,因为Chameleon算法正好弥补了这2点要求,兼具互连性和近似性。在Chameleon算法中定义了相对互连性,RI表示和相对近似性,RC表示,最后通过一个度量函数:

function value = RI( Ci, Cj)× RC( Ci, Cj)α,α在这里表示的多少次方的意思,不是乘法。

来作为2个簇是否能够合并的标准,其实这些都是第二阶段做的事情了。

在第一阶段,所做的一件关键的事情就是形成小簇集,由零星的几个数据点连成小簇,官方的作法是用hMetic算法根据最小化截断的边的权重和来分割k-最近邻图,然后我网上找了一些资料,没有确切的hMetic算法,借鉴了网上其他人的一些办法,于是用了一个很简单的思路,就是给定一个点,把他离他最近的k个点连接起来,就算是最小簇了。事实证明,效果也不会太差,最近的点的换一个意思就是与其最大权重的边,采用距离的倒数最为权重的大小。因为后面的计算,用到的会是权重而不是距离。

我们再回过头来细说第二阶段所做的事情,首先是2个略复杂的公式(直接采用截图的方式):

                                                                              相对互连性RI=

相对近似性RC=

Ci,Cj表示的是i,j聚簇内的数据点的个数,EC(Ci)表示的Ci聚簇内的边的权重和,EC(Ci,Cj)表示的是连接2个聚簇的边的权重和。

后来我在查阅书籍和一些文库的时候发现,这个公式还不是那么的标准,因为他对分母,分子进行了部分的改变,但是大意上还是一致的,标准公式上用到的是平均权重,而这里用的是和的形式,差别不大,所以就用这个公式了。

那么合并的过程如下:

1、给定度量函数如下minMetric,

2、访问每个簇,计算他与邻近的每个簇的RC和RI,通过度量函数公式计算出值tempMetric。

3、找到最大的tempMetric,如果最大的tempMetric超过阈值minMetric,将簇与此值对应的簇合并

4、如果找到的最大的tempMetric没有超过阈值,则表明此聚簇已合并完成,移除聚簇列表,加入到结果聚簇中。

4、递归步骤2,直到待合并聚簇列表最终大小为空。

算法的实现

算法的输入依旧采用的是坐标点的形式graphData.txt:

[java]  view plain copy print ?
  1. 0 2 2  
  2. 1 3 1  
  3. 2 3 4  
  4. 3 3 14  
  5. 4 5 3  
  6. 5 8 3  
  7. 6 8 6  
  8. 7 9 8  
  9. 8 10 4  
  10. 9 10 7  
  11. 10 10 10  
  12. 11 10 14  
  13. 12 11 13  
  14. 13 12 8  
  15. 14 12 15  
  16. 15 14 7  
  17. 16 14 9  
  18. 17 14 15  
  19. 18 15 8  
算法坐标点数据Point.java:

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

簇类Cluster.java:

[java]  view plain copy print ?
  1. package DataMining_Chameleon;  
  2.   
  3. import java.util.ArrayList;  
  4.   
  5. /** 
  6.  * 聚簇类 
  7.  *  
  8.  * @author lyq 
  9.  *  
  10.  */  
  11. public class Cluster implements Cloneable{  
  12.     //簇唯一id标识号  
  13.     int id;  
  14.     // 聚簇内的坐标点集合  
  15.     ArrayList<Point> points;  
  16.     // 聚簇内的所有边的权重和  
  17.     double weightSum = 0;  
  18.   
  19.     public Cluster(int id, ArrayList<Point> points) {  
  20.         this.id = id;  
  21.         this.points = points;  
  22.     }  
  23.   
  24.     /** 
  25.      * 计算聚簇的内部的边权重和 
  26.      *  
  27.      * @return 
  28.      */  
  29.     public double calEC() {  
  30.         int id1 = 0;  
  31.         int id2 = 0;  
  32.         weightSum = 0;  
  33.           
  34.         for (Point p1 : points) {  
  35.             for (Point p2 : points) {  
  36.                 id1 = p1.id;  
  37.                 id2 = p2.id;  
  38.   
  39.                 // 为了避免重复计算,取id1小的对应大的  
  40.                 if (id1 < id2 && ChameleonTool.edges[id1][id2] == 1) {  
  41.                     weightSum += ChameleonTool.weights[id1][id2];  
  42.                 }  
  43.             }  
  44.         }  
  45.   
  46.         return weightSum;  
  47.     }  
  48.   
  49.     /** 
  50.      * 计算2个簇之间最近的n条边 
  51.      *  
  52.      * @param otherCluster 
  53.      *            待比较的簇 
  54.      * @param n 
  55.      *            最近的边的数目 
  56.      * @return 
  57.      */  
  58.     public ArrayList<int[]> calNearestEdge(Cluster otherCluster, int n){  
  59.         int count = 0;  
  60.         double distance = 0;  
  61.         double minDistance = Integer.MAX_VALUE;  
  62.         Point point1 = null;  
  63.         Point point2 = null;  
  64.         ArrayList<int[]> edgeList = new ArrayList<>();  
  65.         ArrayList<Point> pointList1 = (ArrayList<Point>) points.clone();  
  66.         ArrayList<Point> pointList2 = null;  
  67.         Cluster c2 = null;  
  68.           
  69.         try {  
  70.             c2 = (Cluster) otherCluster.clone();  
  71.             pointList2 = c2.points;  
  72.         } catch (CloneNotSupportedException e) {  
  73.             // TODO Auto-generated catch block  
  74.             e.printStackTrace();  
  75.         }  
  76.   
  77.         int[] tempEdge;  
  78.         // 循环计算出每次的最近距离  
  79.         while (count < n) {  
  80.             tempEdge = new int[2];  
  81.             minDistance = Integer.MAX_VALUE;  
  82.               
  83.             for (Point p1 : pointList1) {  
  84.                 for (Point p2 :  pointList2) {  
  85.                     distance = p1.ouDistance(p2);  
  86.                     if (distance < minDistance) {  
  87.                         point1 = p1;  
  88.                         point2 = p2;  
  89.                         tempEdge[0] = p1.id;  
  90.                         tempEdge[1] = p2.id;  
  91.   
  92.                         minDistance = distance;  
  93.                     }  
  94.                 }  
  95.             }  
  96.   
  97.             pointList1.remove(point1);  
  98.             pointList2.remove(point2);  
  99.             edgeList.add(tempEdge);  
  100.             count++;  
  101.         }  
  102.   
  103.         return edgeList;  
  104.     }  
  105.   
  106.     @Override  
  107.     protected Object clone() throws CloneNotSupportedException {  
  108.         // TODO Auto-generated method stub  
  109.           
  110.         //引用需要再次复制,实现深拷贝  
  111.         ArrayList<Point> pointList = (ArrayList<Point>) this.points.clone();  
  112.         Cluster cluster = new Cluster(id, pointList);  
  113.           
  114.         return cluster;  
  115.     }  
  116.       
  117.       
  118.   
  119. }  

算法工具类Chameleon.java:

[java]  view plain copy print ?
  1. package DataMining_Chameleon;  
  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.  * Chameleon 两阶段聚类算法工具类 
  12.  *  
  13.  * @author lyq 
  14.  *  
  15.  */  
  16. public class ChameleonTool {  
  17.     // 测试数据点文件地址  
  18.     private String filePath;  
  19.     // 第一阶段的k近邻的k大小  
  20.     private int k;  
  21.     // 簇度量函数阈值  
  22.     private double minMetric;  
  23.     // 总的坐标点的个数  
  24.     private int pointNum;  
  25.     // 总的连接矩阵的情况,括号表示的是坐标点的id号  
  26.     public static int[][] edges;  
  27.     // 点与点之间的边的权重  
  28.     public static double[][] weights;  
  29.     // 原始坐标点数据  
  30.     private ArrayList<Point> totalPoints;  
  31.     // 第一阶段产生的所有的连通子图作为最初始的聚类  
  32.     private ArrayList<Cluster> initClusters;  
  33.     // 结果簇结合  
  34.     private ArrayList<Cluster> resultClusters;  
  35.   
  36.     public ChameleonTool(String filePath, int k, double minMetric) {  
  37.         this.filePath = filePath;  
  38.         this.k = k;  
  39.         this.minMetric = minMetric;  
  40.   
  41.         readDataFile();  
  42.     }  
  43.   
  44.     /** 
  45.      * 从文件中读取数据 
  46.      */  
  47.     private void readDataFile() {  
  48.         File file = new File(filePath);  
  49.         ArrayList<String[]> dataArray = new ArrayList<String[]>();  
  50.   
  51.         try {  
  52.             BufferedReader in = new BufferedReader(new FileReader(file));  
  53.             String str;  
  54.             String[] tempArray;  
  55.             while ((str = in.readLine()) != null) {  
  56.                 tempArray = str.split(" ");  
  57.                 dataArray.add(tempArray);  
  58.             }  
  59.             in.close();  
  60.         } catch (IOException e) {  
  61.             e.getStackTrace();  
  62.         }  
  63.   
  64.         Point p;  
  65.         totalPoints = new ArrayList<>();  
  66.         for (String[] array : dataArray) {  
  67.             p = new Point(array[0], array[1], array[2]);  
  68.             totalPoints.add(p);  
  69.         }  
  70.         pointNum = totalPoints.size();  
  71.     }  
  72.   
  73.     /** 
  74.      * 递归的合并小聚簇 
  75.      */  
  76.     private void combineSubClusters() {  
  77.         Cluster cluster = null;  
  78.   
  79.         resultClusters = new ArrayList<>();  
  80.   
  81.         // 当最后的聚簇只剩下一个的时候,则退出循环  
  82.         while (initClusters.size() > 1) {  
  83.             cluster = initClusters.get(0);  
  84.             combineAndRemove(cluster, initClusters);  
  85.         }  
  86.     }  
  87.   
  88.     /** 
  89.      * 递归的合并聚簇和移除聚簇 
  90.      *  
  91.      * @param clusterList 
  92.      */  
  93.     private ArrayList<Cluster> combineAndRemove(Cluster cluster,  
  94.             ArrayList<Cluster> clusterList) {  
  95.         ArrayList<Cluster> remainClusters;  
  96.         double metric = 0;  
  97.         double maxMetric = -Integer.MAX_VALUE;  
  98.         Cluster cluster1 = null;  
  99.         Cluster cluster2 = null;  
  100.   
  101.         for (Cluster c2 : clusterList) {  
  102.             if (cluster.id == c2.id) {  
  103.                 continue;  
  104.             }  
  105.   
  106.             metric = calMetricfunction(cluster, c2, 1);  
  107.   
  108.             if (metric > maxMetric) {  
  109.                 maxMetric = metric;  
  110.                 cluster1 = cluster;  
  111.                 cluster2 = c2;  
  112.             }  
  113.         }  
  114.   
  115.         // 如果度量函数值超过阈值,则进行合并,继续搜寻可以合并的簇  
  116.         if (maxMetric > minMetric) {  
  117.             clusterList.remove(cluster2);  
  118.             // 将边进行连接  
  119.             connectClusterToCluster(cluster1, cluster2);  
  120.             // 将簇1和簇2合并  
  121.             cluster1.points.addAll(cluster2.points);  
  122.             remainClusters = combineAndRemove(cluster1, clusterList);  
  123.         } else {  
  124.             clusterList.remove(cluster);  
  125.             remainClusters = clusterList;  
  126.             resultClusters.add(cluster);  
  127.         }  
  128.   
  129.         return remainClusters;  
  130.     }  
  131.   
  132.     /** 
  133.      * 将2个簇进行边的连接 
  134.      *  
  135.      * @param c1 
  136.      *            聚簇1 
  137.      * @param c2 
  138.      *            聚簇2 
  139.      */  
  140.     private void connectClusterToCluster(Cluster c1, Cluster c2) {  
  141.         ArrayList<int[]> connectedEdges;  
  142.   
  143.         connectedEdges = c1.calNearestEdge(c2, 2);  
  144.   
  145.         for (int[] array : connectedEdges) {  
  146.             edges[array[0]][array[1]] = 1;  
  147.             edges[array[1]][array[0]] = 1;  
  148.         }  
  149.     }  
  150.   
  151.     /** 
  152.      * 算法第一阶段形成局部的连通图 
  153.      */  
  154.     private void connectedGraph() {  
  155.         double distance = 0;  
  156.         Point p1;  
  157.         Point p2;  
  158.   
  159.         // 初始化权重矩阵和连接矩阵  
  160.         weights = new double[pointNum][pointNum];  
  161.         edges = new int[pointNum][pointNum];  
  162.         for (int i = 0; i < pointNum; i++) {  
  163.             for (int j = 0; j < pointNum; j++) {  
  164.                 p1 = totalPoints.get(i);  
  165.                 p2 = totalPoints.get(j);  
  166.   
  167.                 distance = p1.ouDistance(p2);  
  168.                 if (distance == 0) {  
  169.                     // 如果点为自身的话,则权重设置为0  
  170.                     weights[i][j] = 0;  
  171.                 } else {  
  172.                     // 边的权重采用的值为距离的倒数,距离越近,权重越大  
  173.                     weights[i][j] = 1.0 / distance;  
  174.                 }  
  175.             }  
  176.         }  
  177.   
  178.         double[] tempWeight;  
  179.         int[] ids;  
  180.         int id1 = 0;  
  181.         int id2 = 0;  
  182.         // 对每个id坐标点,取其权重前k个最大的点进行相连  
  183.         for (int i = 0; i < pointNum; i++) {  
  184.             tempWeight = weights[i];  
  185.             // 进行排序  
  186.             ids = sortWeightArray(tempWeight);  
  187.   
  188.             // 取出前k个权重最大的边进行连接  
  189.             for (int j = 0; j < ids.length; j++) {  
  190.                 if (j < k) {  
  191.                     id1 = i;  
  192.                     id2 = ids[j];  
  193.   
  194.                     edges[id1][id2] = 1;  
  195.                     edges[id2][id1] = 1;  
  196.                 }  
  197.             }  
  198.         }  
  199.     }  
  200.   
  201.     /** 
  202.      * 权重的冒泡算法排序 
  203.      *  
  204.      * @param array 
  205.      *            待排序数组 
  206.      */  
  207.     private int[] sortWeightArray(double[] array) {  
  208.         double[] copyArray = array.clone();  
  209.         int[] ids = null;  
  210.         int k = 0;  
  211.         double maxWeight = -1;  
  212.   
  213.         ids = new int[pointNum];  
  214.         for (int i = 0; i < pointNum; i++) {  
  215.             maxWeight = -1;  
  216.   
  217.             for (int j = 0; j < copyArray.length; j++) {  
  218.                 if (copyArray[j] > maxWeight) {  
  219.                     maxWeight = copyArray[j];  
  220.                     k = j;  
  221.                 }  
  222.             }  
  223.   
  224.             ids[i] = k;  
  225.             // 将当前找到的最大的值重置为-1代表已经找到过了  
  226.             copyArray[k] = -1;  
  227.         }  
  228.   
  229.         return ids;  
  230.     }  
  231.   
  232.     /** 
  233.      * 根据边的连通性去深度优先搜索所有的小聚簇 
  234.      */  
  235.     private void searchSmallCluster() {  
  236.         int currentId = 0;  
  237.         Point p;  
  238.         Cluster cluster;  
  239.         initClusters = new ArrayList<>();  
  240.         ArrayList<Point> pointList = null;  
  241.   
  242.         // 以id的方式逐个去dfs搜索  
  243.         for (int i = 0; i < pointNum; i++) {  
  244.             p = totalPoints.get(i);  
  245.   
  246.             if (p.isVisited) {  
  247.                 continue;  
  248.             }  
  249.   
  250.             pointList = new ArrayList<>();  
  251.             pointList.add(p);  
  252.             recusiveDfsSearch(p, -1, pointList);  
  253.   
  254.             cluster = new Cluster(currentId, pointList);  
  255.             initClusters.add(cluster);  
  256.   
  257.             currentId++;  
  258.         }  
  259.     }  
  260.   
  261.     /** 
  262.      * 深度优先的方式找到边所连接着的所有坐标点 
  263.      *  
  264.      * @param p 
  265.      *            当前搜索的起点 
  266.      * @param lastId 
  267.      *            此点的父坐标点 
  268.      * @param pList 
  269.      *            坐标点列表 
  270.      */  
  271.     private void recusiveDfsSearch(Point p, int parentId, ArrayList<Point> pList) {  
  272.         int id1 = 0;  
  273.         int id2 = 0;  
  274.         Point newPoint;  
  275.   
  276.         if (p.isVisited) {  
  277.             return;  
  278.         }  
  279.   
  280.         p.isVisited = true;  
  281.         for (int j = 0; j < pointNum; j++) {  
  282.             id1 = p.id;  
  283.             id2 = j;  
  284.   
  285.             if (edges[id1][id2] == 1 && id2 != parentId) {  
  286.                 newPoint = totalPoints.get(j);  
  287.                 pList.add(newPoint);  
  288.                 // 以此点为起点,继续递归搜索  
  289.                 recusiveDfsSearch(newPoint, id1, pList);  
  290.             }  
  291.         }  
  292.     }  
  293.   
  294.     /** 
  295.      * 计算连接2个簇的边的权重 
  296.      *  
  297.      * @param c1 
  298.      *            聚簇1 
  299.      * @param c2 
  300.      *            聚簇2 
  301.      * @return 
  302.      */  
  303.     private double calEC(Cluster c1, Cluster c2) {  
  304.         double resultEC = 0;  
  305.         ArrayList<int[]> connectedEdges = null;  
  306.   
  307.         connectedEdges = c1.calNearestEdge(c2, 2);  
  308.   
  309.         // 计算连接2部分的边的权重和  
  310.         for (int[] array : connectedEdges) {  
  311.             resultEC += weights[array[0]][array[1]];  
  312.         }  
  313.   
  314.         return resultEC;  
  315.     }  
  316.   
  317.     /** 
  318.      * 计算2个簇的相对互连性 
  319.      *  
  320.      * @param c1 
  321.      * @param c2 
  322.      * @return 
  323.      */  
  324.     private double calRI(Cluster c1, Cluster c2) {  
  325.         double RI = 0;  
  326.         double EC1 = 0;  
  327.         double EC2 = 0;  
  328.         double EC1To2 = 0;  
  329.   
  330.         EC1 = c1.calEC();  
  331.         EC2 = c2.calEC();  
  332.         EC1To2 = calEC(c1, c2);  
  333.   
  334.         RI = 2 * EC1To2 / (EC1 + EC2);  
  335.   
  336.         return RI;  
  337.     }  
  338.   
  339.     /** 
  340.      * 计算簇的相对近似度 
  341.      *  
  342.      * @param c1 
  343.      *            簇1 
  344.      * @param c2 
  345.      *            簇2 
  346.      * @return 
  347.      */  
  348.     private double calRC(Cluster c1, Cluster c2) {  
  349.         double RC = 0;  
  350.         double EC1 = 0;  
  351.         double EC2 = 0;  
  352.         double EC1To2 = 0;  
  353.         int pNum1 = c1.points.size();  
  354.         int pNum2 = c2.points.size();  
  355.   
  356.         EC1 = c1.calEC();  
  357.         EC2 = c2.calEC();  
  358.         EC1To2 = calEC(c1, c2);  
  359.   
  360.         RC = EC1To2 * (pNum1 + pNum2) / (pNum2 * EC1 + pNum1 * EC2);  
  361.   
  362.         return RC;  
  363.     }  
  364.   
  365.     /** 
  366.      * 计算度量函数的值 
  367.      *  
  368.      * @param c1 
  369.      *            簇1 
  370.      * @param c2 
  371.      *            簇2 
  372.      * @param alpha 
  373.      *            幂的参数值 
  374.      * @return 
  375.      */  
  376.     private double calMetricfunction(Cluster c1, Cluster c2, int alpha) {  
  377.         // 度量函数值  
  378.         double metricValue = 0;  
  379.         double RI = 0;  
  380.         double RC = 0;  
  381.   
  382.         RI = calRI(c1, c2);  
  383.         RC = calRC(c1, c2);  
  384.         // 如果alpha大于1,则更重视相对近似性,如果alpha逍遥于1,注重相对互连性  
  385.         metricValue = RI * Math.pow(RC, alpha);  
  386.   
  387.         return metricValue;  
  388.     }  
  389.   
  390.     /** 
  391.      * 输出聚簇列 
  392.      *  
  393.      * @param clusterList 
  394.      *            输出聚簇列 
  395.      */  
  396.     private void printClusters(ArrayList<Cluster> clusterList) {  
  397.         int i = 1;  
  398.   
  399.         for (Cluster cluster : clusterList) {  
  400.             System.out.print("聚簇" + i + ":");  
  401.             for (Point p : cluster.points) {  
  402.                 System.out.print(MessageFormat.format("({0}, {1}) ", p.x, p.y));  
  403.             }  
  404.             System.out.println();  
  405.             i++;  
  406.         }  
  407.   
  408.     }  
  409.   
  410.     /** 
  411.      * 创建聚簇 
  412.      */  
  413.     public void buildCluster() {  
  414.         // 第一阶段形成小聚簇  
  415.         connectedGraph();  
  416.         searchSmallCluster();  
  417.         System.out.println("第一阶段形成的小簇集合:");  
  418.         printClusters(initClusters);  
  419.   
  420.         // 第二阶段根据RI和RC的值合并小聚簇形成最终结果聚簇  
  421.         combineSubClusters();  
  422.         System.out.println("最终的聚簇集合:");  
  423.         printClusters(resultClusters);  
  424.     }  
  425. }  

调用类Client.java:

[java]  view plain copy print ?
  1. package DataMining_Chameleon;  
  2.   
  3. /** 
  4.  * Chameleon(变色龙)两阶段聚类算法 
  5.  * @author lyq 
  6.  * 
  7.  */  
  8. public class Client {  
  9.     public static void main(String[] args){  
  10.         String filePath = "C:\\Users\\lyq\\Desktop\\icon\\graphData.txt";  
  11.         //k-近邻的k设置  
  12.         int k = 1;  
  13.         //度量函数阈值  
  14.         double minMetric = 0.1;  
  15.           
  16.         ChameleonTool tool = new ChameleonTool(filePath, k, minMetric);  
  17.         tool.buildCluster();  
  18.     }  
  19. }  

算法输出如下:

[java]  view plain copy print ?
  1. 第一阶段形成的小簇集合:  
  2. 聚簇1:(22) (31) (34) (53)   
  3. 聚簇2:(314) (1014) (1113)   
  4. 聚簇3:(83) (104)   
  5. 聚簇4:(86) (98) (107) (128) (1010)   
  6. 聚簇5:(1215) (1415)   
  7. 聚簇6:(147) (158) (149)   
  8. 最终的聚簇集合:  
  9. 聚簇1:(22) (31) (34) (53) (83) (104)   
  10. 聚簇2:(314) (1014) (1113) (1215) (1415)   
  11. 聚簇3:(86) (98) (107) (128) (1010) (147) (158) (149)   

图形展示情况如下:

首先是第一阶段形成小簇集的结果:

Chameleon两阶段聚类算法_第4张图片

然后是第二阶段合并的结果:

Chameleon两阶段聚类算法_第5张图片

与结果相对应,请读者细细比较。

算法总结

在算法的实现过程中遇到一个比较大的困惑点在于2个簇近和并的时候,合并边的选取,我是直接采用的是最近的2对顶点进行连接,显然这是不合理的,当簇与簇规模比较大的时候,这个连接边需要变多,我有想过做一个计算函数,帮我计算估计要连接几条边。这里再提几点变色龙算法的优缺点,首先是这个算法将互连性和近似性都考虑了进来,其次他能发现高质量的任意形状的簇,问题有,第一与KNN算法一样,这个k的取值永远是一个痛,时间复杂度高,有可能会达到O(n*n)的程度,细心的博友一定能观察到我好多地方用到了双次循环的操作了。

你可能感兴趣的:(Chameleon两阶段聚类算法)