Hierarchical cluster算法介绍

  突然想记录几个聚类算法,由于实力有限就先介绍一下层次聚类算法(Hierarchical cluster algorithm),这个聚类算法思想简单,但实现起来感觉复杂度挺大;以前看过《集体智慧编程》里介绍过,里面是用python实现的,由于python里面的列表和字典用起来方便,故实现该算法还行;这里我用c++重新写了一下,感觉代码蛮臃肿,可能是自己的c++没有学习好吧!!!对于容器的使用还不够熟练,这里贴出来的目的是希望哪位大牛看到了指导一二,这里感激不尽。废话不多说了,进入正题吧!

************************************************************************************************************

Hierarchical cluster Algorithm的大致介绍

  层次聚类算法有两种实现思想,一种是初始时将每个待聚类的数据样本视为一个cluster,采用合并的方式,每次合并两个"距离"最近的cluster,直到合并成一个cluster为止(当然可以在达到自己设定想得到的cluster个数时终止迭代);另一种刚好与第一种相反,初始时将所有的数据样本视为一个cluster,采用分解的方式(这里没有实现就不说太多)。

************************************************************************************************************

算法的步骤及相关问题

  算法步骤:  (1)初始时,将每个数据样本视为一个cluster(选取一个度量两个cluster距离的方式),

       (2)计算任意两个cluster之间的距离;每次选取距离最小的两个cluster,

       (3)合并(2)中选择的两个cluster,将合并产生的新cluster加入cluster set中,并删除被合并的两个cluster,

       (4)重复(2)(3),知道cluster set中元素只剩下一个为止。

  相关问题: (1)度量两个cluster之间的距离,应该选择哪种距离???《集体智慧编程》中选择的是Pearson,当然也可以直接选用欧氏距离

        (2)如何合并两个cluster,即新的cluster对应的属性值如何表示???这里是用被合并的两个cluster的平均值表示新的cluster

******************************************************************************************************************

  1 /**
  2 ** Hierarchical cluster Algorithm
  3 ** step:(1)Firstly,regard each sample as a cluster, and
  4          (2)Each time merge two clusters if the distance between them is lowest.
  5          (3)then add the new cluster into cluster set, and delete two clusters merged from cluster set.
  6 ** method: (1)as to merging, here replace the old two clusters with their average;
  7            (2)measure the distance with the Pearson similarity.
  8 ** Time:2013/7/10 
  9 **/
 10 #include <iostream>
 11 #include <map>
 12 #include <vector>
 13 #include <string>
 14 #include <fstream> 
 15 #include <cstring>
 16 #include <sstream> 
 17 #include <cmath>
 18 #include <iterator>
 19 using namespace std;
 20 //cluster
 21 typedef    struct bicluster{
 22     vector<double> attri;//attribute
 23     int  cid;//cluster id 
 24 }Bicluster;
 25 //a pair
 26 typedef struct lowpair{
 27     int leftid;
 28     int rightid;
 29     double dist;
 30 }Lpair;
 31 
 32 /*****************************************************************
 33 ** convert string(char*) to double(or other type)
 34 ** here should be included <sstream> before using the stringstream
 35 ******************************************************************/
 36 double str2double(char* str){
 37     stringstream ss;
 38     ss << str;
 39     double tmp;
 40     ss >> tmp;
 41     return tmp;    
 42 }
 43 /*****************************************************************
 44 ** split the string containing some special tokens
 45 ******************************************************************/
 46 string split(string &str, vector<double>& dvec, const char* tok){
 47     char *pch = NULL;
 48     pch = strtok(const_cast<char*>(str.c_str()), tok);
 49     string stmp(pch);
 50     while( pch != NULL ){
 51         pch = strtok(NULL, tok);
 52         if( !pch )
 53             break;
 54         dvec.push_back(str2double(pch));
 55     }
 56     return stmp;
 57 }
 58 /******************************************************************
 59 ** read data from 'blogdata.txt'
 60 ** @is ------- a reference to ifstream object(input)
 61 ** @data ----- a map used to store the data (output)
 62 ******************************************************************/
 63 bool readfile(ifstream &is, map<string, vector<double> >& mydata){
 64     if( is.fail() ){
 65         cerr << "can't open the file !!!" << endl;
 66         return false;
 67     }
 68     //ignore the first line of file
 69     string str;
 70     getline(is, str);
 71     
 72     //store the data read from file into mydata 
 73     while( !is.eof() ){
 74         vector<double> dtmp;
 75         string tmp;
 76         getline(is, str);
 77         tmp = split(str, dtmp, "	");
 78         mydata.insert(pair<string,vector<double> >(tmp, dtmp));
 79     }
 80     return true;         
 81 }
 82 /*****************************************************************
 83 ** compute the distance between two clusters
 84 ** Note that Pearson value devotes to the similarity between 
 85     two clusters, that is, the greater the Pearson value, the 
 86     lower the distance between them.
 87 *****************************************************************/ 
 88 double distPearson(vector<double>& left, vector<double>& right){
 89     double sum1 = 0;
 90     double sum2 = 0;
 91     int len = left.size();
 92     for(int i=0; i<len; ++i){
 93         sum1 += left[i];
 94         sum2 += right[i];
 95     }
 96     
 97     /**
 98     ** maybe you will feel it's complex, 
 99     **  and here we could replace Pearson with Euclidean distance
100     **/
101     double sum1Sq = 0;
102     double sum2Sq = 0;
103     for(int j=0; j<len; ++j){
104         sum1Sq += pow(left[j], 2);
105         sum2Sq += pow(right[j], 2);
106     }
107     
108     double pSum = 0, num, den;
109     for(int k=0; k<len; ++k)
110         pSum += left[k]*right[k];
111     num = pSum - sum1*sum2 / len;
112     den = sqrt((sum1Sq - pow(sum1,2)/len) * (sum1Sq - pow(sum2,2)/len));
113     if( den == 0 )
114         return 0;
115     return 1.0 - num/den;
116 }
117 /*************************************************************
118 ** Given two clusters, the distance between them 
119     should be checked whether it exists before compute it.
120 **************************************************************/
121 bool isExist(vector<Lpair> &lp, int leftid, int rightid, double &d){
122     vector<Lpair>::iterator it = lp.begin();
123     for(; it!=lp.end(); ++it){
124         if( (it->leftid==leftid) && (it->rightid==rightid) ){
125             d = it->dist;//if the distance has been computed, assign its value to d
126             return true;
127         }        
128     }
129     d = 0;
130     return false;
131 }
132 /*************************************************************
133 ** Given a cluster's id, delete the cluster from cluster set
134 **************************************************************/
135 void Del(vector<Bicluster> &cvec, int clusterid){
136     vector<Bicluster>::iterator it = cvec.begin();
137     for(; it!=cvec.end(); ++it){
138         if( it->cid == clusterid )
139             break;
140     }
141     cvec.erase(it);
142 } 
143 /*************************************************************
144 ** Hierarchical Cluster Algorithm
145 **************************************************************/
146 void HierarchicalCluster(map<string, vector<double> > &mydata){
147     vector<Lpair> distances;//used to store the distance
148      
149     //firstly,regard each sample as a cluster
150     vector<Bicluster> cvec;
151     map<string, vector<double> >::iterator it = mydata.begin();
152     int myid = 0;
153     for(; it!= mydata.end(); ++it){
154         Bicluster btmp;
155         btmp.attri = it->second;
156         btmp.cid = myid++;
157         cvec.push_back(btmp);
158     } 
159     myid = -1;
160     //search the pair
161     while( cvec.size()>1 ){
162         Lpair lowp;
163         double closedis = distPearson(cvec[0].attri,cvec[1].attri);
164         lowp.leftid = cvec[0].cid, lowp.rightid = cvec[1].cid;
165         lowp.dist = closedis;
166         
167         int leftps = 0, rightps = 1;
168         for(int ix=0; ix<cvec.size(); ++ix){
169             for(int iy=ix+1; iy<cvec.size(); ++iy){
170                 double d;
171                 int lid = cvec[ix].cid, rid = cvec[iy].cid;
172                 if( !isExist(distances,lid,rid,d) ){
173                     Lpair lptmp;
174                     lptmp.dist = distPearson(cvec[ix].attri, cvec[iy].attri);
175                     lptmp.leftid = lid;
176                     lptmp.rightid= rid;
177                     distances.push_back(lptmp);
178                     d = lptmp.dist;
179                   } 
180                  if( d < lowp.dist ){
181                      lowp.leftid = lid;
182                      lowp.rightid = rid;
183                      leftps = ix;
184                      rightps = iy;
185                      lowp.dist = d;
186                  }
187             }
188         }
189         //create a new cluster
190         Bicluster ncluster;
191         for(int i=0; i<cvec[0].attri.size(); ++i){
192             double av;
193             av = (cvec[leftps].attri[i] + cvec[rightps].attri[i]) / 2.0;
194             ncluster.attri.push_back(av);
195         }
196         ncluster.cid = myid--;//assign negative to the new cluster's id
197         cout << "leftid: " << lowp.leftid <<  ", rightid: " << lowp.rightid << endl;
198         //delete the pair
199         Del(cvec, lowp.leftid); 
200         Del(cvec, lowp.rightid);
201         cvec.push_back(ncluster);
202     } 
203 } 
204 int main()
205 {
206     ifstream is("blogdata.txt");
207     if( is.fail() ){
208         cerr << "error!!!" << endl;
209         exit(-1);
210     }
211     map<string, vector<double> > mydata;
212     if(readfile(is, mydata))
213         HierarchicalCluster(mydata);
214     return 0;
215 }

  代码写的有点乱且复杂,最后显示的结果不是树状图(python很易实现),只是简单的显示了每次被合并的两个cluster的id.代码中用到的数据可以从http://kiwitobes.com/clusters/blog.txt下载得到。

原文地址:https://www.cnblogs.com/Happyhe/p/3183828.html