Kmeans算法及其示例

10 篇文章 2 订阅
2 篇文章 0 订阅

Kmeans算法

Kmeans是简单的聚类分析算法。其常用在数据分析与人工智能中。

简单说,Kmeans算法就是把一个集合中的东西分为若干子集,这几个子集内的元素具有空间相近或者特点相近。


做法:

1.    随机选取K各中心点,生成对应的k个簇。

2.    遍历所有的数据点,依据“距离’”将每一个数据点划分到最近的中心点所在的簇。

3.    计算每个簇所有的数据点的平均值,并作为该簇新的中心。

4.    重复2-3步,直到这k个簇的中心点不再变化,或者达到我们规定的迭代次数。


注意:

1.    kmean算法思想本身比较简单,但是设置k数量与初始中心点却比较难以确定。

2.    需要判断处理空聚类以防某个初始中心点设置不合理。

3.    这里的“距离”指欧氏距离或者是余弦相似度,也可以指某种特征,不要被“距离”二字局限

4.    每个数据点是几维的,那么数据中心也应该是几维的,这里kmeans可以想象成将高维空间中的若干个数据点分为若干团。(其实也有特殊情况)

 

Kmeans++算法


Kmeans++算法,主要可以解决初始中心的选择问题,不可解决k的个数。

Kmeans++主要思想是选择的初始聚类中心要尽量的远。

做法:

1.    在输入的数据点中随机选一个作为第一个聚类中心。

2.    对于所有数据点,计算它与最近的聚类中心的距离D(x)

3.    重新选择一个数据点作为聚类中心,选择原则:D(x)较大的点被选为聚类中心的概率较大。

4.    重复2-3直到选出k个聚类中心。

5.    运行Kmeans算法。

 

 

kmeans示例


PS:其初始中心的设置我在这直接取了数据点中最大与最小点的等分值,数据预处理部分代码去掉。

题目是对一个具有四波段的海洋遥感图做聚类分析。

将整幅图划分为若干块,然后对每个块取其波段均值,然后做聚类分析。


#include <iostream>
#include <cmath>
#include <list>
#include <fstream>
#include <cstdlib>
//kmeans算法 
//数据预处理代码已去除 
using namespace std;

class Map{
public:
	int row,col;
	int valid;			//有效值
	float value[4];
	int which_center = -1; 
	
public:
	Map() {
		row = 0;
		col = 0;
		valid = 0;
		value[0] = value[1] =value[2] =value[3]= 0;
	}	 
	void calc_row_column(int x,int y) {
		row = x*300;
		col = y*300;
	}
	bool is_valid() {
		return (value[0]!=0||value[1]!=0||value[2]!=0||value[3]!=0);
	}
};

class vector4 {
public:
	float val[4];
	float& operator[](int n) {
		return val[n];
	}
	vector4() {
		val[0] = val[1] = val[2] = val[3] =0;
	}
};

template<typename T_>
class KCenter {
public:
	int index;		//编号 
	vector4 center;		//中心 
	int num;		//成员数 
	
	
	void set_kcenter(int index,vector4 center){
		this->index = index;
		this->center = center;
	}
	typename list<T_>::iterator is_member(T_ map) {
		for(typename list<T_>::iterator itr = member.begin(); itr!=member.end() ; itr++) {
			if(*itr == map) {
				return itr;
			}
		}
		return member.end();
	}
	void push(T_ map) {
		member.push_back(map);
		map->which_center = index;
		num ++;
	}
	void pop(T_ map) {
		typename list<T_>::iterator pos = is_member(map);
		if(pos != member.end()) {
			(*pos)->which_center = -1;
			member.erase(pos);
			num--;
		}
	}
	float distance_from_map(T_ samp) {
		//计算samp距离KCenter的距离 
		float dist  = 0;
		dist += pow( (center[0]- (samp->value)[0]), 2);
		dist += pow( (center[1]- (samp->value)[1]), 2);
		dist += pow( (center[2]- (samp->value)[2]), 2);
		dist += pow( (center[3]- (samp->value)[3]), 2);
		return sqrt(dist);
	}
	
	void adjust_center() {
		//依据成员重新调整自身中心 
		center[0] = center[1] = center[2] = center[3] = 0;
		for(typename list<T_>::iterator itr = member.begin(); itr!=member.end() ; itr++) {
			for(int j=0; j<4; j++)
				center[j] += ((*itr)->value)[j];
		}
		for(int j=0; j<4; j++)
			center[j] /= num;
	}
	
	float all_distance() {
		float dist = 0;
		for(typename list<T_>::iterator itr = member.begin(); itr!=member.end() ; itr++) {
			dist += distance_from_map(*itr);
		}
		return dist;
	}
	
private:
	list<T_> member;
	
};

const int N_K = 4; //K中心个数 
KCenter<Map*> kcenters[N_K];


//寻找初始簇的质心
void find_centers(vector4 centers[],int n,Map maps[],int n_maps) {

	//平均取样值作为簇中心 (预先计算了数据中最大147最小值0) 
	int span = 147;
	for(int i=0;i<n;i++) {
		for(int j=0;j<4;j++)
			centers[i][j] = i*span/(N_K-1);
	}
	for(int i=0;i<N_K;i++)
			cout<<"cu "<<centers[i][0]<<' '<<centers[i][1]<<' '<<centers[i][2]<<' '<<centers[i][3]<<endl; 
}

void init_centers(vector4 centers[],int n) {
	//设置初始簇的质心 
	for(int i=0; i<n; i++) {
		kcenters[i].set_kcenter(i,centers[i]);
	}
}

bool divide(Map maps[],int n) {
	//为每个样本分类 
	//并返回这次是否重新调整过的标志 
	float dist = 0;
	int num_select = 0;	
	bool is_adjust = false;
	
	for(int i=0; i<n; i++) {
		//寻找最近簇 
		dist = kcenters[0].distance_from_map(maps+i);
		num_select = 0;
		for(int j=1; j<N_K; j++) {
			if( dist > kcenters[j].distance_from_map(maps+i) ) {
				dist = kcenters[j].distance_from_map(maps+i);
				num_select = j;		
			}
		}
		if(maps[i].which_center == -1) {
			//从未分类过 
			kcenters[num_select].push(maps+i);
			is_adjust = true;
		} else if (maps[i].which_center != num_select){
			//被分到其他簇
			//从之前簇取出 
			kcenters[maps[i].which_center].pop(maps+i);
			//压入新簇 
			kcenters[num_select].push(maps+i);
			is_adjust = true;
		}
	}
	for(int i=0; i<N_K; i++)
		kcenters[i].adjust_center();
	return is_adjust;
}





/	以下为获得有效数据	//
int n_maps = -1; //有效矩阵数 
Map *maps;

int main() {

	//get_data(maps,&n_maps);//读入原始数据 maps数组保存,n_maps表示元素个数 
	vector4 centers[5];
	find_centers(centers,N_K,maps,n_maps);
	init_centers(centers,N_K);
	
	//各簇内数据不再变动作为迭代判断 
	while(divide(maps,n_maps));
	
	for(int i=0;i<N_K;i++) {
		cout<<kcenters[i].num<<' ';
		for(int j=0;j<4;j++) cout<<kcenters[i].center[j]<<' ';
		cout<<endl;
	}
	delete [] maps;
	
	return 0;
}


 

 

 

 

分析结果为每种聚类赋一种颜色。

 

  • 5
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值