KMeans聚类算法

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


k-means 算法基本步骤

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

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

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

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


以下是C++实现:

头文件kmeans.h:

#ifndef KMEANS
#define KMEASN
/*********************************************\
  *           KMeans 聚类算法
  * 1.数据格式为文本文件,每行一条数据
  * 2.开头为数据名,string类型。后面紧接着是属性信息,
      此处为double,每个属性之间用tab或空格隔开。
  * 3.每个数据在计算时仅使用了第一列的属性信息
  * 4.距离使用的是绝对值距离
  * 5.如果使用更多的属性,则需要修改相应的函数
  * 6.测试中显示了每次的计算结果,不需要的话可以在
      KMeans::cluster()中删除printResult()即可。
  * Author: yuyang
  * Date: 2013-03-30
/ ********************************************/
#include <vector>
#include <iostream>

using namespace std;

///需要聚类的数据类型
class   Item
{///data item
public:
    string  name;
    vector<double>  vovalue;
};
bool    operator==(const Item &ia, const Item &ib);
bool    operator!=(const Item &ia, const Item &ib);

class KMeans
{
public:
    KMeans(int km=2);
    int     setData(string name);///读入数据
    void    cluster();///聚类函数
    void    printData();///输出原始数据
    void    printResult();///输出聚类结果
    virtual ~KMeans();

protected:
    int     ptcDistence(const Item & item);///某个点到类中心的距离
            ///返回到最近的类的类编号
    int     stable();///判断是否收敛

private:

    void    classify();///将每个数据放到最近的类中
    void    init();///初始化,选取前k个数据作为k类
    void    calCen();///计算当前每个类的中心

    int     k;
    vector<Item>  datas;///保存的原始数据
    vector<double>  ocen;///本轮聚类前类中心
    vector<double>  cen;///本轮聚类后的聚类中心,size=k
    vector<int>    *kclass;///kclass中有k个指针,分别指向k个vector<int>,
    ///每个vector<int>为该类在vector<Item>中的编号

    static  const   int     READ_ERR=1;
    static  const   int     OK=0;
    static  const   double  AC_ERR = 1E-3;
};
#endif // KMEANS


实现文件kmeans.cpp:

#include "kmeans.h"
#include <fstream>
#include <sstream>
#include <cmath>
#include <algorithm>

///判断2个Item是否相等
bool    operator==(const Item &ia, const Item &ib)
{
    if(abs(ia.vovalue[0] - ib.vovalue[0])<1E-4)
        return  true;
    return false;
}
bool    operator!=(const Item &ia, const Item &ib)
{
    return !(ia==ib);
}

KMeans::KMeans(int km):
    k(km),ocen(km),cen(km)
{
    kclass = new vector<int> [k];
}
KMeans::~KMeans()
{
    delete[]    kclass;
    cout<<"KMeans bye."<<endl;
}
int    KMeans::setData(string name)
{
    fstream inf;
    inf.open(name.c_str(),ios::in);
    if(!inf)
    {
        cerr<<"open file "<<name<<" error !"<<endl;
        return KMeans::READ_ERR;
    }
    string line;
    while(getline(inf,line))
    {
        istringstream is(line);
        string iname,ivalue;
        Item    it;
        is>>it.name;///get class name
        double d;
        while(is>>d)///get value
        {
            it.vovalue.push_back(d);
        }
        datas.push_back(it);
    }
    inf.close();
    return KMeans::OK;
}
void    KMeans::printData()
{
    vector<Item>::iterator it = datas.begin();
    for(; it!=datas.end(); ++it)
    {
        Item i = *it;
        cout<<i.name<<"\t";
        vector<double>::iterator id=i.vovalue.begin();
        for(; id!=i.vovalue.end(); ++id)
        {
            cout<<*id<<"\t";
        }
        cout<<"\n";
    }
    cout<<endl;
}
int     KMeans::stable()///判断是否收敛
{
    for(int i=0; i<k; ++i)
        if(abs(cen[i]-ocen[i])>AC_ERR)//cen[i]!=ocen[i]
            return  false;
    return  true;
}

void    KMeans::printResult()
{///output cluster result
    for(int i=0; i<k; ++i)
    {
        int siz = (kclass+i)->size();
        double center=cen[i];
        cout<<"class : "<< i <<" ,size= "
            <<siz
            <<" , class center: "<<center
            <<endl;
        for(int j=0; j<siz; ++j)
        {
            int index = (kclass+i)->at(j);
            cout<<datas[index].name<<"\t";
        }
        cout<<endl<<endl;
    }
}
///返回到最近的类的类编号
int KMeans::ptcDistence(const Item & item)
{///某个点到类中心的距离
    double d=10E9 ;//= new double[k];
    int c=0;
    for(int i=0; i<k; ++i)
    {///item 到第i个类中心的距离
        double poi = item.vovalue[0];
        double poc = cen[i];
        if(d>abs(poi-poc))
        {
            d = abs(poi-poc);///绝对值距离
            c = i;
        }
    }
    return c;
}
void    KMeans::classify()///将每个数据放到最近的类中
{
    ///清空上次分类信息
    for(int i=0; i<k; ++i)
    {
        (kclass+i)->clear();
    }

    unsigned    int iiter = 0;//datas.begin();
    for(; iiter<datas.size(); ++iiter)
    {
        int  c = ptcDistence(datas[iiter]);
        ///将第i个数据分到第c个类中
        (kclass+c)->push_back(iiter);
    }
}

void    KMeans::init()
{
    ///选取前k个互不相等的数据作为初始的k个类
    for(int i=0,cnt=0; cnt<k && i<datas.size(); ++i)
    {
        vector<Item>::iterator it=
        find(datas.begin(),datas.begin()+i,datas.at(i));
        if(it!=datas.begin()+i)///find one
            continue;

        (kclass+cnt)->push_back(i);
        cen[cnt]  = datas.at(i).vovalue[0];///类中心
        ++cnt;
    }

}
void    KMeans::calCen()
{///计算当前每个类的中心
    for(int i=0; i<k; ++i)
    {///class i
        double  sum=0;
        int classnum = (kclass+i)->size();
        for(int j=0; j<classnum; ++j)
        {
            int index = (*(kclass+i))[j];
            sum += datas[index].vovalue[0];
        }
        ocen[i] = cen[i]; ///save old value
        cen[i] = sum/classnum;
    }
}

/*** 核心聚类函数 **************************\
 * 1.初始化
 * 2.判断类中心是否稳定
 * 3.如果已经稳定,则结束算法
 * 4.如果不稳定,调用分类函数classify()
 * 5.转2
\********************************************/
void    KMeans::cluster()
{
    init();
    int round=0;
    cout<<"init:"<<endl;
    printResult();
    while(!stable())
    {
        cout<<"round = "<<++round<<endl;
        classify();
        calCen();
        printResult();
    }
}


测试用例:

#include <iostream>
#include "kmeans.h"

using namespace std;


int main()
{
    string file="a.txt";
    KMeans  km(2);
    km.setData(file);
    km.printData();
    km.cluster();
    cout<<"\nfinished..."<<endl;
    //km.printResult();
    return 0;
}


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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值