KMeans聚类算法(转载)

KMeans算法是很典型的基于距离的聚类算法,采用距离作为相似性的评价指标,即认为两个对象的距离越近,其相似度就越大。该算法认为簇是由距离靠近的对象组成的,因此把得到紧凑且独立的簇作为最终目标。


k-means 算法基本步骤

(1)  从 n个数据对象任意选择 k 个对象作为初始聚类中心;

(2)  根据每个聚类对象的均值(中心对象),计算每个对象与这些中心对象的距离;并根据最小距离重新对相应对象进行划分;

(3)  重新计算每个(有变化)聚类的均值(中心对象);

(4)  计算标准测度函数,当满足一定条件,如函数收敛时,则算法终止;如果条件不满足则回到步骤(2)。


以下是C++实现:

头文件kmeans.h:

  1. #ifndef KMEANS  
  2. #define KMEASN  
  3. /*********************************************\ 
  4.   *           KMeans 聚类算法 
  5.   * 1.数据格式为文本文件,每行一条数据 
  6.   * 2.开头为数据名,string类型。后面紧接着是属性信息, 
  7.       此处为double,每个属性之间用tab或空格隔开。 
  8.   * 3.每个数据在计算时仅使用了第一列的属性信息 
  9.   * 4.距离使用的是绝对值距离 
  10.   * 5.如果使用更多的属性,则需要修改相应的函数 
  11.   * 6.测试中显示了每次的计算结果,不需要的话可以在 
  12.       KMeans::cluster()中删除printResult()即可。 
  13.   * Author: yuyang 
  14.   * Date: 2013-03-30 
  15. / ********************************************/  
  16. #include <vector>  
  17. #include <iostream>  
  18.   
  19. using namespace std;  
  20.   
  21. ///需要聚类的数据类型  
  22. class   Item  
  23. {///data item  
  24. public:  
  25.     string  name;  
  26.     vector<double>  vovalue;  
  27. };  
  28. bool    operator==(const Item &ia, const Item &ib);  
  29. bool    operator!=(const Item &ia, const Item &ib);  
  30.   
  31. class KMeans  
  32. {  
  33. public:  
  34.     KMeans(int km=2);  
  35.     int     setData(string name);///读入数据  
  36.     void    cluster();///聚类函数  
  37.     void    printData();///输出原始数据  
  38.     void    printResult();///输出聚类结果  
  39.     virtual ~KMeans();  
  40.   
  41. protected:  
  42.     int     ptcDistence(const Item & item);///某个点到类中心的距离  
  43.             ///返回到最近的类的类编号  
  44.     int     stable();///判断是否收敛  
  45.   
  46. private:  
  47.   
  48.     void    classify();///将每个数据放到最近的类中  
  49.     void    init();///初始化,选取前k个数据作为k类  
  50.     void    calCen();///计算当前每个类的中心  
  51.   
  52.     int     k;  
  53.     vector<Item>  datas;///保存的原始数据  
  54.     vector<double>  ocen;///本轮聚类前类中心  
  55.     vector<double>  cen;///本轮聚类后的聚类中心,size=k  
  56.     vector<int>    *kclass;///kclass中有k个指针,分别指向k个vector<int>,  
  57.     ///每个vector<int>为该类在vector<Item>中的编号  
  58.   
  59.     static  const   int     READ_ERR=1;  
  60.     static  const   int     OK=0;  
  61.     static  const   double  AC_ERR = 1E-3;  
  62. };  
  63. #endif // KMEANS  


实现文件kmeans.cpp:

  1. #include "kmeans.h"  
  2. #include <fstream>  
  3. #include <sstream>  
  4. #include <cmath>  
  5. #include <algorithm>  
  6.   
  7. ///判断2个Item是否相等  
  8. bool    operator==(const Item &ia, const Item &ib)  
  9. {  
  10.     if(abs(ia.vovalue[0] - ib.vovalue[0])<1E-4)  
  11.         return  true;  
  12.     return false;  
  13. }  
  14. bool    operator!=(const Item &ia, const Item &ib)  
  15. {  
  16.     return !(ia==ib);  
  17. }  
  18.   
  19. KMeans::KMeans(int km):  
  20.     k(km),ocen(km),cen(km)  
  21. {  
  22.     kclass = new vector<int> [k];  
  23. }  
  24. KMeans::~KMeans()  
  25. {  
  26.     delete[]    kclass;  
  27.     cout<<"KMeans bye."<<endl;  
  28. }  
  29. int    KMeans::setData(string name)  
  30. {  
  31.     fstream inf;  
  32.     inf.open(name.c_str(),ios::in);  
  33.     if(!inf)  
  34.     {  
  35.         cerr<<"open file "<<name<<" error !"<<endl;  
  36.         return KMeans::READ_ERR;  
  37.     }  
  38.     string line;  
  39.     while(getline(inf,line))  
  40.     {  
  41.         istringstream is(line);  
  42.         string iname,ivalue;  
  43.         Item    it;  
  44.         is>>it.name;///get class name  
  45.         double d;  
  46.         while(is>>d)///get value  
  47.         {  
  48.             it.vovalue.push_back(d);  
  49.         }  
  50.         datas.push_back(it);  
  51.     }  
  52.     inf.close();  
  53.     return KMeans::OK;  
  54. }  
  55. void    KMeans::printData()  
  56. {  
  57.     vector<Item>::iterator it = datas.begin();  
  58.     for(; it!=datas.end(); ++it)  
  59.     {  
  60.         Item i = *it;  
  61.         cout<<i.name<<"\t";  
  62.         vector<double>::iterator id=i.vovalue.begin();  
  63.         for(; id!=i.vovalue.end(); ++id)  
  64.         {  
  65.             cout<<*id<<"\t";  
  66.         }  
  67.         cout<<"\n";  
  68.     }  
  69.     cout<<endl;  
  70. }  
  71. int     KMeans::stable()///判断是否收敛  
  72. {  
  73.     for(int i=0; i<k; ++i)  
  74.         if(abs(cen[i]-ocen[i])>AC_ERR)//cen[i]!=ocen[i]  
  75.             return  false;  
  76.     return  true;  
  77. }  
  78.   
  79. void    KMeans::printResult()  
  80. {///output cluster result  
  81.     for(int i=0; i<k; ++i)  
  82.     {  
  83.         int siz = (kclass+i)->size();  
  84.         double center=cen[i];  
  85.         cout<<"class : "<< i <<" ,size= "  
  86.             <<siz  
  87.             <<" , class center: "<<center  
  88.             <<endl;  
  89.         for(int j=0; j<siz; ++j)  
  90.         {  
  91.             int index = (kclass+i)->at(j);  
  92.             cout<<datas[index].name<<"\t";  
  93.         }  
  94.         cout<<endl<<endl;  
  95.     }  
  96. }  
  97. ///返回到最近的类的类编号  
  98. int KMeans::ptcDistence(const Item & item)  
  99. {///某个点到类中心的距离  
  100.     double d=10E9 ;//= new double[k];  
  101.     int c=0;  
  102.     for(int i=0; i<k; ++i)  
  103.     {///item 到第i个类中心的距离  
  104.         double poi = item.vovalue[0];  
  105.         double poc = cen[i];  
  106.         if(d>abs(poi-poc))  
  107.         {  
  108.             d = abs(poi-poc);///绝对值距离  
  109.             c = i;  
  110.         }  
  111.     }  
  112.     return c;  
  113. }  
  114. void    KMeans::classify()///将每个数据放到最近的类中  
  115. {  
  116.     ///清空上次分类信息  
  117.     for(int i=0; i<k; ++i)  
  118.     {  
  119.         (kclass+i)->clear();  
  120.     }  
  121.   
  122.     unsigned    int iiter = 0;//datas.begin();  
  123.     for(; iiter<datas.size(); ++iiter)  
  124.     {  
  125.         int  c = ptcDistence(datas[iiter]);  
  126.         ///将第i个数据分到第c个类中  
  127.         (kclass+c)->push_back(iiter);  
  128.     }  
  129. }  
  130.   
  131. void    KMeans::init()  
  132. {  
  133.     ///选取前k个互不相等的数据作为初始的k个类  
  134.     for(int i=0,cnt=0; cnt<k && i<datas.size(); ++i)  
  135.     {  
  136.         vector<Item>::iterator it=  
  137.         find(datas.begin(),datas.begin()+i,datas.at(i));  
  138.         if(it!=datas.begin()+i)///find one  
  139.             continue;  
  140.   
  141.         (kclass+cnt)->push_back(i);  
  142.         cen[cnt]  = datas.at(i).vovalue[0];///类中心  
  143.         ++cnt;  
  144.     }  
  145.   
  146. }  
  147. void    KMeans::calCen()  
  148. {///计算当前每个类的中心  
  149.     for(int i=0; i<k; ++i)  
  150.     {///class i  
  151.         double  sum=0;  
  152.         int classnum = (kclass+i)->size();  
  153.         for(int j=0; j<classnum; ++j)  
  154.         {  
  155.             int index = (*(kclass+i))[j];  
  156.             sum += datas[index].vovalue[0];  
  157.         }  
  158.         ocen[i] = cen[i]; ///save old value  
  159.         cen[i] = sum/classnum;  
  160.     }  
  161. }  
  162.   
  163. /*** 核心聚类函数 **************************\ 
  164.  * 1.初始化 
  165.  * 2.判断类中心是否稳定 
  166.  * 3.如果已经稳定,则结束算法 
  167.  * 4.如果不稳定,调用分类函数classify() 
  168.  * 5.转2 
  169. \********************************************/  
  170. void    KMeans::cluster()  
  171. {  
  172.     init();  
  173.     int round=0;  
  174.     cout<<"init:"<<endl;  
  175.     printResult();  
  176.     while(!stable())  
  177.     {  
  178.         cout<<"round = "<<++round<<endl;  
  179.         classify();  
  180.         calCen();  
  181.         printResult();  
  182.     }  
  183. }  


测试用例:

  1. #include <iostream>  
  2. #include "kmeans.h"  
  3.   
  4. using namespace std;  
  5.   
  6.   
  7. int main()  
  8. {  
  9.     string file="a.txt";  
  10.     KMeans  km(2);  
  11.     km.setData(file);  
  12.     km.printData();  
  13.     km.cluster();  
  14.     cout<<"\nfinished..."<<endl;  
  15.     //km.printResult();  
  16.     return 0;  
  17. }  


如果要使用更多的信息量参与计算,则需要修改的地方有:距离计算,类中心计算和判断是否稳定这些地方。


 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值