经典算法之k-means聚类
来源:互联网 发布:dnf怎么老网络中断 编辑:程序博客网 时间:2024/05/01 11:33
原理
给定一系列样本点,想要分成k类
(1)首先从中随机选取k个点作为质心
(2)分别计算每个点到这k个质心的欧式距离(为例),到哪个质心距离最小,这个点就属于这个质心,这样把所有点分成k类
(3)再分别计算k类中每一类新的质心,根据公式2
(4)迭代(2)(3),直到达到最优条件,条件如最小距离平方和公式3;
公式1
公式2
公式3
close all;clc;%第一类数据mu1=[0 0 0]; %均值S1=[0.3 0 0;0 0.35 0;0 0 0.3]; %协方差data1=mvnrnd(mu1,S1,100); %产生高斯分布数据%%第二类数据mu2=[1.25 1.25 1.25];S2=[0.3 0 0;0 0.35 0;0 0 0.3];data2=mvnrnd(mu2,S2,100);%第三个类数据mu3=[-1.25 1.25 -1.25];S3=[0.3 0 0;0 0.35 0;0 0 0.3];data3=mvnrnd(mu3,S3,100);%显示数据plot3(data1(:,1),data1(:,2),data1(:,3),'+');hold on;plot3(data2(:,1),data2(:,2),data2(:,3),'r+');plot3(data3(:,1),data3(:,2),data3(:,3),'g+');grid on;%三类数据合成一个不带标号的数据类data=[data1;data2;data3]; %这里的data是不带标号的%k-means聚类[u re]=KMeans(data,3); %最后产生带标号的数据,标号在所有数据的最后,意思就是数据再加一维度[m n]=size(re);%最后显示聚类后的数据figure;hold on;for i=1:m if re(i,4)==1 plot3(re(i,1),re(i,2),re(i,3),'ro'); elseif re(i,4)==2 plot3(re(i,1),re(i,2),re(i,3),'go'); else plot3(re(i,1),re(i,2),re(i,3),'bo'); endendgrid on;KMeans.m
%N是数据一共分多少类%data是输入的不带分类标号的数据%u是每一类的中心%re是返回的带分类标号的数据function [u re]=KMeans(data,N) [m n]=size(data); %m是数据个数,n是数据维数 ma=zeros(n); %每一维最大的数 mi=zeros(n); %每一维最小的数 u=zeros(N,n); %随机初始化,最终迭代到每一类的中心位置 for i=1:n ma(i)=max(data(:,i)); %每一维最大的数 mi(i)=min(data(:,i)); %每一维最小的数 for j=1:N u(j,i)=ma(i)+(mi(i)-ma(i))*rand(); %随机初始化,不过还是在每一维[min max]中初始化好些 end end while 1 pre_u=u; %上一次求得的中心位置 for i=1:N tmp{i}=[]; % 公式一中的x(i)-uj,为公式一实现做准备 for j=1:m tmp{i}=[tmp{i};data(j,:)-u(i,:)]; end end quan=zeros(m,N); for i=1:m %公式一的实现 c=[]; for j=1:N c=[c norm(tmp{j}(i,:))]; end [junk index]=min(c); quan(i,index)=norm(tmp{index}(i,:)); end for i=1:N %公式二的实现 for j=1:n u(i,j)=sum(quan(:,i).*data(:,j))/sum(quan(:,i)); end end if norm(pre_u-u)<0.1 %不断迭代直到位置不再变化 break; end end re=[]; for i=1:m tmp=[]; for j=1:N tmp=[tmp norm(data(i,:)-u(j,:))]; end [junk index]=min(tmp); re=[re;data(i,:) index]; end end
C++实现
#include <iostream>#include <sstream>#include <fstream>#include <vector>#include <math.h>#include <stdlib.h>#define k 3//簇的数目using namespace std;//存放元组的属性信息typedef vector<double> Tuple;//存储每条数据记录int dataNum;//数据集中数据记录数目int dimNum;//每条记录的维数//计算两个元组间的欧几里距离double getDistXY(const Tuple& t1, const Tuple& t2) {double sum = 0;for(int i=1; i<=dimNum; ++i){sum += (t1[i]-t2[i]) * (t1[i]-t2[i]);}return sqrt(sum);}//根据质心,决定当前元组属于哪个簇int clusterOfTuple(Tuple means[],const Tuple& tuple){double dist=getDistXY(means[0],tuple);double tmp;int label=0;//标示属于哪一个簇for(int i=1;i<k;i++){tmp=getDistXY(means[i],tuple);if(tmp<dist) {dist=tmp;label=i;}}return label;}//获得给定簇集的平方误差double getVar(vector<Tuple> clusters[],Tuple means[]){double var = 0;for (int i = 0; i < k; i++){vector<Tuple> t = clusters[i];for (int j = 0; j< t.size(); j++){var += getDistXY(t[j],means[i]);}}//cout<<"sum:"<<sum<<endl;return var;}//获得当前簇的均值(质心)Tuple getMeans(const vector<Tuple>& cluster){int num = cluster.size();Tuple t(dimNum+1, 0);for (int i = 0; i < num; i++){for(int j=1; j<=dimNum; ++j){t[j] += cluster[i][j];}}for(int j=1; j<=dimNum; ++j)t[j] /= num;return t;//cout<<"sum:"<<sum<<endl;}void print(const vector<Tuple> clusters[]){for(int lable=0; lable<k; lable++){cout<<"第"<<lable+1<<"个簇:"<<endl;vector<Tuple> t = clusters[lable];for(int i=0; i<t.size(); i++){cout<<i+1<<".(";for(int j=0; j<=dimNum; ++j){cout<<t[i][j]<<", ";}cout<<")\n";}}}void KMeans(vector<Tuple>& tuples){vector<Tuple> clusters[k];//k个簇Tuple means[k];//k个中心点int i=0;//一开始随机选取k条记录的值作为k个簇的质心(均值)srand((unsigned int)time(NULL));for(i=0;i<k;){int iToSelect = rand()%tuples.size();if(means[iToSelect].size() == 0){for(int j=0; j<=dimNum; ++j){means[i].push_back(tuples[iToSelect][j]);}++i;}}int lable=0;//根据默认的质心给簇赋值for(i=0;i!=tuples.size();++i){lable=clusterOfTuple(means,tuples[i]);clusters[lable].push_back(tuples[i]);}double oldVar=-1;double newVar=getVar(clusters,means);cout<<"初始的的整体误差平方和为:"<<newVar<<endl; int t = 0;while(abs(newVar - oldVar) >= 1) //当新旧函数值相差不到1即准则函数值不发生明显变化时,算法终止{cout<<"第 "<<++t<<" 次迭代开始:"<<endl;for (i = 0; i < k; i++) //更新每个簇的中心点{means[i] = getMeans(clusters[i]);}oldVar = newVar;newVar = getVar(clusters,means); //计算新的准则函数值for (i = 0; i < k; i++) //清空每个簇{clusters[i].clear();}//根据新的质心获得新的簇for(i=0; i!=tuples.size(); ++i){lable=clusterOfTuple(means,tuples[i]);clusters[lable].push_back(tuples[i]);}cout<<"此次迭代之后的整体误差平方和为:"<<newVar<<endl; }cout<<"The result is:\n";print(clusters);}int main(){char fname[256];cout<<"请输入存放数据的文件名: ";cin>>fname;cout<<endl<<" 请依次输入: 维数 样本数目"<<endl;cout<<endl<<" 维数dimNum: ";cin>>dimNum;cout<<endl<<" 样本数目dataNum: ";cin>>dataNum;ifstream infile(fname);if(!infile){cout<<"不能打开输入的文件"<<fname<<endl;return 0;}vector<Tuple> tuples;//从文件流中读入数据for(int i=0; i<dataNum && !infile.eof(); ++i){string str;getline(infile, str);istringstream istr(str);Tuple tuple(dimNum+1, 0);//第一个位置存放记录编号,第2到dimNum+1个位置存放实际元素tuple[0] = i+1;for(int j=1; j<=dimNum; ++j){istr>>tuple[j];}tuples.push_back(tuple);}cout<<endl<<"开始聚类"<<endl;KMeans(tuples);return 0;}引用
http://www.cnblogs.com/tiandsp/archive/2013/04/24/3040883.html
http://blog.csdn.net/qll125596718/article/details/8243404/
0 0
- 经典算法之k-means聚类
- 十大经典数据挖掘算法之K-Means算法
- 大数据十大经典算法之k-means
- 数据挖掘十大经典算法之二:K-means
- 机器学习算法 之 K-Means聚类
- 【机器学习算法】之K-means聚类
- 聚类:K-means算法
- 数据挖掘十大经典算法学习之K均值(K-means)聚类算法
- 机器学习之K-means算法:深入浅出K-Means算法
- 机器学习经典算法9-k-means
- 经典算法(5):K-均值算法(K-Means)
- 文本聚类算法之k-means聚类
- 聚类 K-means & K-medoids 算法
- 数据挖掘十大经典算法(二)The k-means algorithm 即K-Means算法
- 聚类算法之K-means
- 聚类算法之K-means
- 聚类算法之K-MEANS
- Weka -- 聚类算法之K-means
- java系统程序员修炼之道
- 《剑指offer》——用两个栈实现队列
- 解决jQuery-validation框架remote校验只能返回true、false问题
- 黑马程序员--约瑟夫环的问题
- Android笔记(3)---Intent之间的传值
- 经典算法之k-means聚类
- oracle数据库表用序列实现主键自增张
- Single Number II
- 第7周项目项目4 - 队列数组.cpp
- UVa 11178 - 计算几何初步
- ORA-12547: TNS:lost contact 解决方法
- Java输入输出 Stream
- 欢迎使用CSDN-markdown编辑器
- 第7周项目项目5 -排队看病模拟.cpp