聚类算法之层次聚类(Java实现)
资源出处: http://www.cnblogs.com/emanlee/archive/2012/02/28/2371273.html
http://blog.csdn.net/jwh_bupt/article/details/7685809
一、知识点:层次聚类算法是非常常用的聚类算法,同时也是被广泛研究的聚类算法。层次聚类本身分为合并和分裂两种实现。下面介绍合并聚类。在合并算法中,又分基于矩阵理论的合并和基于图论的合并。本文只是初学聚类的一点体会,因此只实现了基于矩阵理论的算法。同时,用于大数据集合的层次算法如CURE,ROCK和Chameleon算法都没有涉及。
二、算法步骤:
1、(初始化)把每个样本归为一类,计算每两个类之间的距离,也就是样本与样本之间的相似度;
2、寻找各个类之间最近的两个类,把他们归为一类(这样类的总数就少了一个);
3、重新计算新生成的这个类与各个旧类之间的相似度;
4、重复2和3直到所有样本点都归为一类,结束。
整个聚类过程其实是建立了一棵树,在建立的过程中,可以通过在第二步上设置一个阈值,当最近的两个类的距离大于这个阈值,则认为迭代可以终止。另外关键的一步就是第三步,如何判断两个类之间的相似度有不少种方法。这里介绍一下三:
SingleLinkage:又叫做 nearest-neighbor ,就是取两个类中距离最近的两个样本的距离作为这两个集合的距离,也就是说,最近两个样本之间的距离越小,这两个类之间的相似度就越大。容易造成一种叫做Chaining 的效果,两个 cluster 明明从“大局”上离得比较远,但是由于其中个别的点距离比较近就被合并了,并且这样合并之后Chaining 效应会进一步扩大,最后会得到比较松散的 cluster.
CompleteLinkage:这个则完全是 Single Linkage 的反面极端,取两个集合中距离最远的两个点的距离作为两个集合的距离。其效果也是刚好相反的,限制非常大,两个cluster 即使已经很接近了,但是只要有不配合的点存在,就顽固到底,老死不相合并,也是不太好的办法。这两种相似度的定义方法的共同问题就是指考虑了某个有特点的数据,而没有考虑类内数据的整体特点。
Average-linkage:这种方法就是把两个集合中的点两两的距离全部放在一起求一个平均值,相对也能得到合适一点的结果。
SL会产生连锁反应,CL效果较好啊,AL的一个变种就是取两两距离的中值,与取均值相比更加能够解除个别偏离样本对结果的干扰。
三、总结:它将簇定义为密度相连的点的最大集合,能够把具有足够高密度的区域划分为簇,并可在噪声的空间数据库中发现任意形状的聚类。DBSCAN对用户定义的参数很敏感,细微的不同都可能导致差别很大的结果,而参数的选择无规律可循,只能靠经验确定
下面给出DBScan算法的核心代码:
package util;
import java.util.*;
public class Clusterer {
private List[] clusterList;
DisjointSets ds;
private static final int MAX = Integer.MAX_VALUE;
private int n;
private int cc;
// private double ori[] = {1,2,5,7,9,10};
public Clusterer(int num) {
ds = new DisjointSets(num);
n = num;
cc = n;
clusterList = new ArrayList[num];
for (int i = 0; i < n; i++)
clusterList[i] = new ArrayList();
}
public List[] getClusterList() {
return clusterList;
}
public void setClusterList(List[] clusterList) {
this.clusterList = clusterList;
}
public void output() {
int ind = 1;
for (int i = 0; i < n; i++) {
clusterList[ds.find(i)].add(i);
}
for (int i = 0; i < n; i++) {
if (clusterList[i].size() != 0) {
System.out.print("cluster " + ind + " :");
for (int j = 0; j < clusterList[i].size(); j++) {
System.out.print(clusterList[i].get(j) + " ");
}
System.out.println();
ind++;
}
}
}
/** *//**
* this method provides a hierachical way for clustering data.
*
* @param r
* denote the distance matrix
* @param n
* denote the sample num(distance matrix's row number)
* @param dis
* denote the threshold to stop clustering
*/
public void cluster(double[][] r, int n, double dis) {
int mx = 0, my = 0;
double vmin = MAX;
for (int i = 0; i < n; i++) { // 寻找最小距离所在的行列
for (int j = 0; j < n; j++) {
if (j > i) {
if (vmin > r[i][j]) {
vmin = r[i][j];
mx = i;
my = j;
}
}
}
}
if (vmin > dis) {
return;
}
ds.union(ds.find(mx), ds.find(my)); // 将最小距离所在的行列实例聚类合并
double o1[] = r[mx];
double o2[] = r[my];
double v[] = new double[n];
double vv[] = new double[n];
for (int i = 0; i < n; i++) {
double tm = Math.min(o1[i], o2[i]);
if (tm != 0)
v[i] = tm;
else
v[i] = MAX;
vv[i] = MAX;
}
r[mx] = v;
r[my] = vv;
for (int i = 0; i < n; i++) { // 更新距离矩阵
r[i][mx] = v[i];
r[i][my] = vv[i];
}
cluster(r, n, dis); // 继续聚类,递归直至所有簇之间距离小于dis值
}
/** *//**
*
* @param r
* @param cnum
* denote the number of final clusters
*/
public void cluster(double[][] r, int cnum) {
/**//*if(cc< cnum)
System.err.println("聚类数大于实例数");*/
while (cc > cnum) {// 继续聚类,循环直至聚类个数等于cnum
int mx = 0, my = 0;
double vmin = MAX;
for (int i = 0; i < n; i++) { // 寻找最小距离所在的行列
for (int j = 0; j < n; j++) {
if (j > i) {
if (vmin > r[i][j]) {
vmin = r[i][j];
mx = i;
my = j;
}
}
}
}
ds.union(ds.find(mx), ds.find(my)); // 将最小距离所在的行列实例聚类合并
double o1[] = r[mx];
double o2[] = r[my];
double v[] = new double[n];
double vv[] = new double[n];
for (int i = 0; i < n; i++) {
double tm = Math.min(o1[i], o2[i]);
if (tm != 0)
v[i] = tm;
else
v[i] = MAX;
vv[i] = MAX;
}
r[mx] = v;