agnes聚类算法 c语言,AGNES聚类算法的理解与应用

与前文介绍的DBSCAN聚类算法类似,AGNES算法也属于无监督的数据分类算法。更细地划分,该算法属于自底向上的层次聚类算法。该算法的核心思路是,首先设定一个期望的分类数目n,一开始把每个数据样本都分别看成一个类,然后计算所有类之间两两的距离,找出距离最短的两个类,并把这两个类合并为一个类,到此则总类数减1。接着再重复上述过程:计算所有类之间两两的距离,找出距离最短的两个类,并把这两个类合并为一个类。以此类推,类总数逐渐减少,直到类总数减少到n为止,则停止分类。

下面举一个简单的例子来说明AGNES算法的过程,假设有A、B、C、D、E、F这6个数据样本,要把他们分为3类,也即n=3,那么分类过程如下。

首先,把A、B、C、D、E、F都分别看成一个类,计算它们两两的距离:

A

B

C

D

E

F

A

d00

d01

d02

d03

d04

d05

B

d10

d11

d12

d13

d14

d15

C

d20

d21

d22

d23

d24

d25

D

d30

d31

d32

d33

d34

d35

E

d40

d41

d42

d43

d44

d45

F

d50

d51

d52

d53

d54

d55

上表中的所有距离,我们称之为该算法的距离矩阵,显然,该矩阵是一个对称矩阵。假设矩阵中d02与d20的距离最小,也即A与C的距离最短,那么将A与C合并为一个类,类总数由6减少为5:AC、B、D、E、F。然后再计算这5类的两两距离:

AC

B

D

E

F

AC

d00

d01

d02

d03

d04

B

d10

d11

d12

d13

d14

D

d20

d21

d22

d23

d24

E

d30

d31

d32

d33

d34

F

d40

d41

d42

d43

d44

上表中,假设矩阵中d32与d23的距离最小,也即D与E的距离最短,那么将D与E合并为一个类,类总数由5减少为4:AC、B、DE、F。然后再计算这4类的两两距离:

AC

B

DE

F

AC

d00

d01

d02

d03

B

d10

d11

d12

d13

DE

d20

d21

d22

d23

F

d30

d31

d32

d33

上表中,假设矩阵中d01与d10的距离最小,也即AC与B的距离最短,那么将AC与B合并为一个类,类总数由4减少为3:ACB、DE、F。至此,分类结束。

类与类的距离的计算,有多种方法,下面举例说明各个计算方法,假设有两个类{A, B}和{C, D},第一个类含有样本A、B,第二个类含有样本C、D。

1. 取两个类中各样本距离(欧式距离)的最大值:

d = max(d(A,C), d(A,D), d(B,C), d(B,D))

2. 取两个类中各样本距离的最小值:

d = min(d(A,C), d(A,D), d(B,C), d(B,D))

3. 取两个类中各样本距离的平均值:

d =(d(A,C) + d(A,D) + d(B,C) + d(B,D))/4

本文中,我们不使用以上方法来计算类的距离,我们使用类的中心点距离作为类距离,这样可以简化计算:

d = d((A+B)/2, (C+D)/2)

下面,我们使用C++来实现AGNES算法,对一组点进行分类,每个点都有x坐标与y坐标,因此每个点相当于一个二维向量。

定义点的类:

class point{public:float x; //x坐标float y; //y坐标point(){x = 0;y = 0;}};

计算两个类的距离:

float cal_cluster_distance(vector p1, vector p2){point m1;for(int i = 0; i < p1.size(); i++){m1.x += p1[i].x;m1.y += p1[i].y;}m1.x /= p1.size();m1.y /= p1.size();point m2;for(int i = 0; i < p2.size(); i++){m2.x += p2[i].x;m2.y += p2[i].y;}m2.x /= p2.size();m2.y /= p2.size();float d = sqrt((m1.x-m2.x)*(m1.x-m2.x) + (m1.y-m2.y)*(m1.y-m2.y));return d;}

计算距离矩阵:

/*例如:(A,B) (C,D) (E,F)(A,B) 0 d1 d2(C,D) d3 0 d4(E,F) d5 d6 0*/vector> cal_distance_array(vector> p){vector> d;for(int i = 0; i < p.size(); i++) //计算类集合中所有簇的两两之间的簇距离,组成距离矩阵{vector tmp;for(int j = 0; j < p.size(); j++){//如果不是同一个类,则计算不同两个类的中心点的距离作为类的距离{float dd = cal_cluster_distance(p[i], p[j]);tmp.push_back(dd);}else //如果是同一个类,则距离为0{tmp.push_back(0);}}d.push_back(tmp);}return d;}

求距离矩阵中最近的类的索引:

void get_min_point(vector> M, int &idx_i, int &idx_j){float min = 999999999.9999;for(int i = 0; i < M.size(); i++) //求距离矩阵中的非零最小值{for(int j = 0; j < M[i].size(); j++){if(min > M[i][j] && M[i][j] != 0)min = M[i][j];}}for(int i = 0; i < M.size(); i++) //求非零最小值的两个簇的索引{for(int j = 0; j < M[i].size(); j++){if(min == M[i][j]){if(i < j) //索引较小的簇在簇集合中位于较前面的位置,把索引较大的簇合并到索引较小的簇,当删除索引较大的簇之后,并不影响索引较小簇的索引值,方便重新计算合并后簇的距离{idx_i = i;idx_j = j;}else{idx_i = j;idx_j = i;}return;}}}}

删除距离矩阵的第idx行和第idx列:

void delete_row_col(vector> &d, int idx){d.erase(d.begin()+idx); //删除第idx行for(int i = 0; i < d.size(); i++) //删除第idx列{d[i].erase(d[i].begin()+idx);}}

将类B合并到类A的尾端,并将类B删除:

void combine_cluster(vector> &p, int A_idx, int B_idx){p[A_idx].insert(p[A_idx].end(), p[B_idx].begin(), p[B_idx].end()); //将簇B合并到簇Ap.erase(p.begin()+B_idx);     //删除类B}

下面上正餐,AGNES算法计算实现:

vector> Agnes(vector p, int cluster_num){vector> pp;for(int i = 0; i  tmp;tmp.push_back(p[i]);pp.push_back(tmp);}vector> M = cal_distance_array(pp);   //计算类集合的距离矩阵int cnt = p.size();int ii, jj;while(cnt > cluster_num){get_min_point(M, ii, jj);  //获取距离矩阵中距离为非零最小值的两个类的索引,ii为较小索引,jj为较大索引combine_cluster(pp, ii, jj);  //将索引为jj的类合并到索引为ii的类,并删除索引为jj的类cnt = pp.size();   //经过上一步之后,总类数减1,所以需要更新类计数器cnt的值#if 0M = cal_distance_array(pp); //重新计算距离矩阵#else   //这里完全重新计算距离矩阵也是可以的;或者先删除被合并类所在的行列,然后仅重新计算合并类与其它类的距离也是OK的,理论上后者的效率会更高一点delete_row_col(M, jj); //删除jj行与jj列for(int i = 0; i 

主函数代码:

int main(void){float dat[][2]= {{1,1}, {6,5}, {1,1.5}, {11,9.5}, {1.5,1}, {6,5.5}, {10.5,10}, {1.5,1.5}, {1.5,2},{6,6}, {10,9.5}, {6.5,5}, {10.5,9}, {6.5,5.5}, {2,1.5}, {6.5,6}, {11,10}, {7,6},{10,9}, {7,5}, {10,10}, {2,2}, {10.5,9.5}, {7,5.5}, {2,1}, {11,9}, {1,2}};int len = sizeof(dat)/sizeof(dat[0]);vector p;for(int i = 0; i < len; i++){point tmp; //新建一个类tmp.x = dat[i][0]; //将数据集写入类中tmp.y = dat[i][1];p.push_back(tmp); //将类加入到点集中}vector> Clusters = Agnes(p, 3); //设置期望的类总数为3printf("Clusters.size()= %d\n", Clusters.size());for(int i = 0; i < Clusters.size(); i++) //打印分类结果{cout<

运行上述代码,得到结果如下:

AGNES算法相比于DBSCAN算法,其参数很少,只需要输入要分类的总数以及数据样本即可,但是AGNES算法比DBSCAN算法慢得多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值