k-medoids聚类

什么是k-medoids

k-means受异常值或极端值的影响比较大,因此,针对k-means在离群噪声点表现出的不够鲁棒,提出了改进的k-medoids聚类方法,其核心思想在于“中心”,也就是说算法计算出的聚类中心一定是出现在样本中的点。

与k-means有何不同

不一样的地方在于中心点的选取

  • K-means中,我们将中心点取为当前cluster中所有数据点的平均值
  • K-medoids中,我们将从当前cluster 中选取这样一个点——它到其他所有(当前cluster中的)点的距离之和最小——作为中心点。

步骤

这里以1维数据的聚类为例加以说明,首先介绍下数据定义:

vector<double> data;    // 每个样本是一个1维数据
int clusterNum;         // 要聚成几类(K)
vector<double> center;  // 存放聚类中心的容器
vector<set<double> > cluster;   // 存放聚类数据,cluster[i]表示第i类的数据是一个点的集合
  • 1. 初始化聚类中心

    任意选取K个对象作为medoids O1,O2,OiOk ,这一步和k-means是差不多的。

void medoids::initial(string fileName,int k)
{
    getData(fileName);
    if(data.size()<k)
        clusterNum = data.size();
    else
        clusterNum = k;

    // 应该随机初始化,这里只是为了渐变
    for(int i=0;i<clusterNum;i++)
        center.push_back(data[i]);

    cluster.resize(clusterNum);
    int s = cluster.size();
}

// 从文件中读取数据
void medoids::getData(string file)
{
    ifstream in(file.c_str(),ios::in);
    if(!in.is_open())
    {
        cout<<"open file failed!";
        system("pause");
        exit(1);
    }
    int size;
    in>>size;
    for(int i=0;i<size;i++)
    {
        double temp;
        in>>temp;
        data.push_back(temp);
    }
    in.close();
}
  • 2. 将样本点分配到各个类中去

    根据与medoid最相近的原则,这一步也可k-means是一样的。

// 对所有数据进行分配,指定聚类中心
void medoids::distribute()
{
    cluster.clear();
    cluster.resize(clusterNum);
    int s = cluster.size();
    for(int i=0;i<data.size();i++)
    {
        int pos = closet(data[i]);
        // 向指定的中心插入该条数据
        cluster[pos].insert(data[i]);
    }
}

// 对于给定数据返回离自己最近的聚类中心
int medoids::closet(int num)
{
    int pos = -1;
    double min = 9.9e+20;
    for(int i=0;i<center.size();i++)
        if(min>fabs(center[i]-num))
        {
            min = fabs(center[i]-num);  // 距离计算
            pos = i;
        }
    return pos;
}
  • 3. 每一类中,各自更新聚类中心

    在每一类中,都尝试一下把其他的点作为聚类中心,他的消耗有多大,找一个使消耗最小的点作为新的聚类中心,有如下步骤。

    1)计算cluster内所有样本点到其中一个样本点的曼哈顿距离和(绝对误差)
    2)选出使cluster绝对误差最小的样本点作为质心

// 聚类中心的更新,返回是否还需更新
bool medoids::findNewCenter()
{
    bool again = false;
    for(int i=0;i<cluster.size();i++)
    {
        // 每一类单独去更新
        double newCenter = calCost(cluster[i]);
        if(newCenter!=center[i])
        {
            center[i]=newCenter;
            again = true;
        }
    }
    return again;
}

// 根据所属类点集,找到一个是成本最小的点
double medoids::calCost(set<double>& s)
{
    set<double>::iterator ite = s.begin();
    int minCost = 9.9e+20;
    double newCenter = -1;
    while(ite!=s.end())
    {
        set<double>::iterator ite1 = s.begin();
        // 误差平方和
        double total=0;

        while(ite1!=s.end())
        {
            double delta = (*ite1)-(*ite);
            total = total + delta*delta;
            ite1++;
        }

        // 找出使得误差平方和最小的那个点作为新的中心
        if(total<minCost)
        {
            minCost = total;
            newCenter = (*ite);
        }
        ite++;
    }
    return newCenter;
}
  • 4. 重复2、3步直到K个medoids固定下来
#include <iostream>
#include "medoids.h"
using namespace std;

void test(){
    medoids m;

    m.initial("data.txt", 3);
    int max_iter = 100;
    int iter = 0;
    while (iter++<max_iter &&m.findNewCenter())
        m.distribute();

    cout << "iter : " << iter << endl;
    m.print();

}

不足

  • k-medoids的运行速度较慢,计算质心的步骤时间复杂度是O(n^2),因为他必须计算任意两点之间的距离,而k-means只需平均即可。

    注意:k-medoids的是曼哈顿距离和,k-means的则是欧式距离和

参考

k中心聚类
K-means和K-medoids对比

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值