聚类算法之CHAMELEON(Java实现)

来源:互联网 发布:改图软件 编辑:程序博客网 时间:2024/06/14 06:20

CHAMELEON是一种两阶段聚类法。第一阶段把点分成很多小的簇;第二阶段根据相近程度合并这些小的簇。第一阶段采用K最邻近法,即把一个点和它最邻近的K个点连接起来。第二阶段计算任意两个簇的互连性RI和紧密性RC,当两个指标都比较大时才合并这两个簇。

相对互连度

相对紧密度

|Ci|表示簇i内数据点的个数;EC(Ci)表示簇i内所有边的权重和;EC(Ci,Cj)表示跨越两个簇的所有边的权重和。

下图是第一阶段后形成的几个小的子簇:

 

把子簇合并后形成的最终簇划分:

 

 CHAMELEON具有两个特点:(1)适合于高维数据的聚类,在文本分类中,每个文本都被表示为一个数千维的向量。(2)采用k-邻近图可以动态地捕捉邻域概念,在稠密区域邻域比较窄,在稀疏区域邻域比较宽,这相比于DBSCAN中的全局邻域密度来说容易获得更自然的邻域。

下面给出CHAMELEON算法的核心代码:

/** * Author: Orisun * Date: Sep 13, 2011 * FileName: chameleon.java * Function:  */package orisun;import java.io.DataInputStream;import java.io.DataOutputStream;import java.io.File;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.FileInputStream;import java.io.IOException;import java.util.zip.DeflaterOutputStream;import java.util.zip.InflaterInputStream;import java.util.ArrayList;import java.util.Comparator;import java.util.HashMap;import java.util.Iterator;import java.util.LinkedList;import java.util.Map;import java.util.PriorityQueue;import java.util.Map.Entry;import java.util.Queue;import java.util.Vector;public class Chameleon {float[][] W; // weight矩阵(方阵)byte[][] Conn; // 连接矩阵(方阵)Vector<Vector<Integer>> clusters;double MI; // 综合指数// 构造函数,初始化变量public Chameleon(int datanum, double mi) {W = new float[datanum][];for (int i = 0; i < datanum; i++) {W[i] = new float[datanum];}Conn = new byte[datanum][];for (int i = 0; i < datanum; i++) {// 由于是无向图,连接矩阵是对称的,所以我们只用矩阵的下三角以节约空间Conn[i] = new byte[i + 1];}clusters = new Vector<Vector<Integer>>();MI = mi;}// 构造weight矩阵。根据两点间距离的倒数计算两点的相似度,作为连接权重public void buildWeightMatrix(ArrayList<DataObject> objects) {for (int i = 0; i < W.length; i++) {W[i][i] = 1.0f;for (int j = i + 1; j < W[i].length; j++) {float dist = (float) Global.calEuraDist(objects.get(i).getVector(), objects.get(j).getVector(), objects.get(j).getVector().length);W[i][j] = W[j][i] = 1 / (1 + dist);}}}// 把weight矩阵写入文件,下次计算相同实例时免得重新计算public void writeWeightToFile(File file) {DataOutputStream fout;try {fout = new DataOutputStream(new DeflaterOutputStream(new FileOutputStream(file)));fout.writeInt(W.length);//先把方阵的边长写入文件for(int i=0;i<W.length;i++)for(int j=0;j<W.length;j++)fout.writeFloat(W[i][j]);fout.close();} catch (FileNotFoundException e) {System.err.println("File Not Found!");}catch(IOException e){e.printStackTrace();}}// 从文件读入weight矩阵public void readWeightFromFile(File file){DataInputStream fin;try {fin = new DataInputStream(new InflaterInputStream(new FileInputStream(file)));fin.readInt();//第一个数字是方阵的边长,略过for(int i=0;i<W.length;i++)for(int j=0;j<W.length;j++)W[i][j]=fin.readFloat();fin.close();} catch (FileNotFoundException e) {System.err.println("File Not Found!");}catch(IOException e){e.printStackTrace();}}// CHAMELEON第一阶段,按照K最邻近建立较小的子簇public void buildSmallCluster() {PriorityQueue<Entry<Integer, Float>> pq = new PriorityQueue<Entry<Integer, Float>>(Global.k, new Comparator<Map.Entry<Integer, Float>>() {public int compare(Entry<Integer, Float> arg0,Entry<Integer, Float> arg1) {return arg0.getValue().compareTo(arg1.getValue());}});for (int i = 0; i < W.length; i++) {pq.clear();int j = 0;HashMap<Integer, Float> map = new HashMap<Integer, Float>();// 找到与object距离最小(亦即相似度最大)的K个点for (; j < Global.k; j++) {map.clear();map.put(j, W[i][j]);pq.add(map.entrySet().iterator().next());}for (; j < W[i].length; j++) {if (W[i][j] > pq.peek().getValue()) {pq.poll();map.clear();map.put(j, W[i][j]);pq.add(map.entrySet().iterator().next());}}for (j = 0; j < Global.k; j++) {Entry<Integer, Float> entry = pq.poll();if (i > entry.getKey())Conn[i][entry.getKey()] = 1;else if (i < entry.getKey())Conn[entry.getKey()][i] = 1;// 对角线上的设为0}}// 根据连接矩阵构造连通子图boolean[] visited = new boolean[W.length];boolean allvisited = false;while (!allvisited) {allvisited = true;L1: for (int i = 0; i < W.length; i++) {if (visited[i])continue;allvisited = false;// 搜寻第i列,以图发现新子图的第一个点for (int j = i + 1; j < W.length; j++) {// 发现了新子图的第一个点if (Conn[j][i] == 1) {Vector<Integer> cluster = new Vector<Integer>();Queue<Integer> queue = new LinkedList<Integer>();queue.add(i);queue.add(j);while (!queue.isEmpty()) {int ele = queue.poll();cluster.add(ele);visited[ele] = true;// 遍历第ele列for (int k = ele + 1; k < W.length; k++) {if (visited[k])continue;if (Conn[k][ele] == 1 && !queue.contains(k)) {queue.add(k);}}// 遍历第ele行for (int k = 0; k < ele; k++) {if (visited[k])continue;if (Conn[ele][k] == 1 && !queue.contains(k))queue.add(k);}}clusters.add(cluster);break L1;}}}}}// 打印子簇public void printClusters() {for (int i = 0; i < clusters.size(); i++) {System.out.print("以下数据点属于第" + i + "簇:");Iterator<Integer> iter = clusters.get(i).iterator();while (iter.hasNext()) {System.out.print(iter.next() + ",");}System.out.println();}}// CHAMELEON第二阶段,合并相对互联度RI和相对紧密度RC都较高的簇public void cluster() {int len = clusters.size();float[] EC1 = new float[len];for (int i = 0; i < len; i++) {Vector<Integer> vec = clusters.get(i);for (int j = 0; j < vec.size(); j++) {for (int k = 0; k < vec.size(); k++) {EC1[i] += W[vec.get(j)][vec.get(k)];}}}boolean end = true;for (int i = 0; i < clusters.size(); i++) {for (int j = i + 1; j < clusters.size(); j++) {Vector<Integer> vec1 = clusters.get(i);Vector<Integer> vec2 = clusters.get(j);float EC = 0.0f;float RI = 0.0f;float SEC = 0.0f;float RC = 0.0f;for (int k = 0; k < vec1.size(); k++) {for (int m = 0; m < vec2.size(); m++) {EC += W[vec1.get(k)][vec2.get(m)];}}RI = 2 * EC / (EC1[i] + EC1[j]);RC = (vec1.size() + vec2.size()) * EC/ (vec2.size() * EC1[i] + vec1.size() * EC1[j]);// 以RI*RC作为综合指数if (RI * RC > MI) {mergeClusters(i, j);end = false;break;}}}// 递归合并子簇if (!end)cluster();}// 把簇b合并到簇a里面去public void mergeClusters(int a, int b) {Iterator<Integer> iter = clusters.get(b).iterator();while (iter.hasNext()) {clusters.get(a).add(iter.next());}clusters.remove(b);}public static void main(String[] args) {Global.setK(2); // 2最邻近,这里面包括它自己DataSource datasource = new DataSource();datasource.readMatrix(new File("/home/orisun/test/dot.mat"));datasource.readRLabel(new File("/home/orisun/test/dot.rlabel"));//datasource.readMatrix(new File("/home/orisun/test/text.normalized.mat"));//datasource.readRLabel(new File("/home/orisun/test/text.rlabel"));//File wfile=new File("/home/orisun/test/test_weight");//if(!wfile.exists()){//try {//wfile.createNewFile();//} catch (IOException e) {//e.printStackTrace();//}//}//综合指数0.1Chameleon cham = new Chameleon(datasource.row, 0.1);//cham.readWeightFromFile(wfile);cham.buildWeightMatrix(datasource.objects);//cham.writeWeightToFile(wfile);cham.buildSmallCluster();System.out.println("==============第一阶段后的分类结果==============");cham.printClusters();cham.cluster();System.out.println("==============第二阶段后的分类结果==============");cham.printClusters();}}