层次聚类方法是古老而且常用的聚类方法。层次聚类方法又有两种产生层次聚类的基本方法。
凝聚的:该方法是自底向上的方法,初始每个对象看做一个簇,每一步合并最相近的簇,最终形成一个簇。
分类的:该方法是自顶向下的方法,从包含的所有点的簇开始,每一步分裂一个簇,知道仅剩下单点的簇。
在凝聚的层次聚类方法中,需要定义簇之间的相近性。有许多凝聚层次的聚类技术,本文中介绍单链、全链、组平均。
单链:
该方法中两个簇的邻近度定义为两个不同簇中任意两点之间的最短距离。单链技术擅长处理非椭圆形的簇,但是对噪音和离群点很敏感。
两个簇之间的相似度计算公式为:
dist({m1,m2},{m3,m4})=min(dist(m1,m3),dist(m1,m4),dist(m2,m3),dist(m2,m4))
该方法中两个簇的邻近度定义为两个不同簇中任意两点之间的最长距离。单链技术擅长处理圆形的簇,但是对噪音和离群点不太敏感。
两个簇之间的相似度计算公式为:
dist({m1,m2},{m3,m4})=max(dist(m1,m3),dist(m1,m4),dist(m2,m3),dist(m2,m4))
组平均:
该方法中两个簇的邻近度定义为两个不同簇中任意两点之间的平均距离。该方法是位于单链和全链之间的这种方法。
两个簇之间的相似度计算公式为:
dist({m1,m2},{m3,m4})=(dist(m1,m3)+dist(m1,m4)+dist(m2,m3)+dist(m2,m4))/4
AGNES算法
AGNES(Agglomerative Nesting) 是凝聚的层次聚类算法,如果簇C1中的一个对象和簇C2中的一个对象之间的距离是所有属于不同簇的对象间欧式距离中最小的,C1和C2可能被合并。这是一种单连接方法,其每个簇可以被簇中的所有对象代表,两个簇之间的相似度由这两个簇中距离最近的数据点对的相似度来确定。
算法描述:
输入:包含n个对象的数据库,终止条件簇的数目k
输出:k个簇
(1) 将每个对象当成一个初始簇
(2) Repeat
(3) 根据两个簇中最近的数据点找到最近的两个簇
(4) 合并两个簇,生成新的簇的集合
(5) Until达到定义的簇的数目
算法性能:
(1) 简单,但遇到合并点选择困难的情况。
(2) 一旦一组对象被合并,不能撤销
(3) 算法的复杂度为O(n的平方),不适合大数据集计算
DIANA算法
DIANA(Divisive Analysis)算法属于分裂的层次聚类,首先将所有的对象初始化到一个簇中,然后根据一些原则(比如最邻近的最大欧式距离),将该簇分类。直到到达用户指定的簇数目或者两个簇之间的距离超过了某个阈值。
DIANA用到如下两个定义:
(1) 簇的直径:在一个簇中的任意两个数据点都有一个欧氏距离,这些距离中的最大值是簇的直径
(2) 平均相异度(平均距离):
算法描述:
输入:包含n个对象的数据库,终止条件簇的数目k
输出:k个簇,达到终止条件规定簇数目
(1) 将所有对象整个当成一个初始簇
(2) For ( i=1;i!=k;i++) Do Begin
(3) 在所有簇中挑选出具有最大直径的簇;
(4) 找出所挑出簇里与其他点平均相异度最大的一个点放入splinter group,剩余的放入old party中。
(5) Repeat
(6) 在old party里找出到splinter group中点的最近距离不大于old party中点的最近距离的点,并将该点加入splinter group
(7) Until 没有新的old party的点被分配给splinter group;
(8) Splinter group 和old party为被选中的簇分裂成的两个簇,与其他簇一起组成新的簇集合
(9) END
算法性能:
缺点是已做的分裂操作不能撤销,类之间不能交换对象。如果在某步没有选择好分裂点,可能会导致低质量的聚类结果。大数据集不太适用。
缺点:
传统的层次聚类算法的效率比较低 O(tn2 ) t: 迭代次数 n: 样本点数,最明显的一个缺点是不具有再分配能力,即如果样本点 A 在某次迭代过程中已经划分给类簇 C1 ,那么在后面的迭代过程中 A 将永远属于类簇 C1 ,这将影响聚类结果的准确性。
其他算法
层次聚类方法比较简单,但是经常遇到的一个问题,就是在合并或分裂点选择困难的问题。一个有希望的改进方向是将层级聚类和其他聚类技术进行集成,形成多阶段聚类。
(1) BIRCH算法
BIRCH(利用层次方法的平衡迭代规约和聚类)是一个总和的层次聚类方法,
(2) CURE算法
----------------贴上本人实现的AGENES算法,大家有兴趣可以把DIANA算法自己实现下-------------- -
package com.agenes;
public class DataPoint {
String dataPointName; // 样本点名
Cluster cluster; // 样本点所属类簇
private double dimensioin[]; // 样本点的维度
public DataPoint(){
}
public DataPoint(double[] dimensioin,String dataPointName){
this.dataPointName=dataPointName;
this.dimensioin=dimensioin;
}
public double[] getDimensioin() {
return dimensioin;
}
public void setDimensioin(double[] dimensioin) {
this.dimensioin = dimensioin;
}
public Cluster getCluster() {
return cluster;
}
public void setCluster(Cluster cluster) {
this.cluster = cluster;
}
public String getDataPointName() {
return dataPointName;
}
public void setDataPointName(String dataPointName) {
this.dataPointName = dataPointName;
}
}
package com.agenes;
import java.util.ArrayList;
import java.util.List;
public class Cluster {
private List<DataPoint> dataPoints = new ArrayList<DataPoint>(); // 类簇中的样本点
private String clusterName;
public List<DataPoint> getDataPoints() {
return dataPoints;
}
public void setDataPoints(List<DataPoint> dataPoints) {
this.dataPoints = dataPoints;
}
public String getClusterName() {
return clusterName;
}
public void setClusterName(String clusterName) {
this.clusterName = clusterName;
}
}
package com.agenes;
import java.util.ArrayList;
import java.util.List;
public class ClusterAnalysis {
public List<Cluster> startAnalysis(List<DataPoint> dataPoints,int ClusterNum){
List<Cluster> finalClusters=new ArrayList<Cluster>();
List<Cluster> originalClusters=initialCluster(dataPoints);
finalClusters=originalClusters;
while(finalClusters.size()>ClusterNum){
double min=Double.MAX_VALUE;
int mergeIndexA=0;
int mergeIndexB=0;
for(int i=0;i<finalClusters.size();i++){
for(int j=0;j<finalClusters.size();j++){
if(i!=j){
Cluster clusterA=finalClusters.get(i);
Cluster clusterB=finalClusters.get(j);
List<DataPoint> dataPointsA=clusterA.getDataPoints();
List<DataPoint> dataPointsB=clusterB.getDataPoints();
for(int m=0;m<dataPointsA.size();m++){
for(int n=0;n<dataPointsB.size();n++){
double tempDis=getDistance(dataPointsA.get(m),dataPointsB.get(n));
if(tempDis<min){
min=tempDis;
mergeIndexA=i;
mergeIndexB=j;
}
}
}
}
} //end for j
}// end for i
//合并cluster[mergeIndexA]和cluster[mergeIndexB]
finalClusters=mergeCluster(finalClusters,mergeIndexA,mergeIndexB);
}//end while
return finalClusters;
}
private List<Cluster> mergeCluster(List<Cluster> clusters,int mergeIndexA,int mergeIndexB){
if (mergeIndexA != mergeIndexB) {
// 将cluster[mergeIndexB]中的DataPoint加入到 cluster[mergeIndexA]
Cluster clusterA = clusters.get(mergeIndexA);
Cluster clusterB = clusters.get(mergeIndexB);
List<DataPoint> dpA = clusterA.getDataPoints();
List<DataPoint> dpB = clusterB.getDataPoints();
for (DataPoint dp : dpB) {
DataPoint tempDp = new DataPoint();
tempDp.setDataPointName(dp.getDataPointName());
tempDp.setDimensioin(dp.getDimensioin());
tempDp.setCluster(clusterA);
dpA.add(tempDp);
}
clusterA.setDataPoints(dpA);
// List<Cluster> clusters中移除cluster[mergeIndexB]
clusters.remove(mergeIndexB);
}
return clusters;
}
// 初始化类簇
private List<Cluster> initialCluster(List<DataPoint> dataPoints){
List<Cluster> originalClusters=new ArrayList<Cluster>();
for(int i=0;i<dataPoints.size();i++){
DataPoint tempDataPoint=dataPoints.get(i);
List<DataPoint> tempDataPoints=new ArrayList<DataPoint>();
tempDataPoints.add(tempDataPoint);
Cluster tempCluster=new Cluster();
tempCluster.setClusterName("Cluster "+String.valueOf(i));
tempCluster.setDataPoints(tempDataPoints);
tempDataPoint.setCluster(tempCluster);
originalClusters.add(tempCluster);
}
return originalClusters;
}
//计算两个样本点之间的欧几里得距离
private double getDistance(DataPoint dpA,DataPoint dpB){
double distance=0;
double[] dimA = dpA.getDimensioin();
double[] dimB = dpB.getDimensioin();
if (dimA.length == dimB.length) {
for (int i = 0; i < dimA.length; i++) {
double temp=Math.pow((dimA[i]-dimB[i]),2);
distance=distance+temp;
}
distance=Math.pow(distance, 0.5);
}
return distance;
}
public static void main(String[] args){
ArrayList<DataPoint> dpoints = new ArrayList<DataPoint>();
double[] a={2,3};
double[] b={2,4};
double[] c={1,4};
double[] d={1,3};
double[] e={2,2};
double[] f={3,2};
double[] g={8,7};
double[] h={8,6};
double[] i={7,7};
double[] j={7,6};
double[] k={8,5};
// double[] l={100,2};//孤立点
double[] m={8,20};
double[] n={8,19};
double[] o={7,18};
double[] p={7,17};
double[] q={8,20};
dpoints.add(new DataPoint(a,"a"));
dpoints.add(new DataPoint(b,"b"));
dpoints.add(new DataPoint(c,"c"));
dpoints.add(new DataPoint(d,"d"));
dpoints.add(new DataPoint(e,"e"));
dpoints.add(new DataPoint(f,"f"));
dpoints.add(new DataPoint(g,"g"));
dpoints.add(new DataPoint(h,"h"));
dpoints.add(new DataPoint(i,"i"));
dpoints.add(new DataPoint(j,"j"));
dpoints.add(new DataPoint(k,"k"));
// dataPoints.add(new DataPoint(l,"l"));
dpoints.add(new DataPoint(m,"m"));
dpoints.add(new DataPoint(n,"n"));
dpoints.add(new DataPoint(o,"o"));
dpoints.add(new DataPoint(p,"p"));
dpoints.add(new DataPoint(q,"q"));
int clusterNum=3; //类簇数
ClusterAnalysis ca=new ClusterAnalysis();
List<Cluster> clusters=ca.startAnalysis(dpoints, clusterNum);
for(Cluster cl:clusters){
System.out.println("------"+cl.getClusterName()+"------");
List<DataPoint> tempDps=cl.getDataPoints();
for(DataPoint tempdp:tempDps){
System.out.println(tempdp.getDataPointName());
}
}
}
}