kmeans聚类算法是一种简单实用的聚类算法,matlab自带函数kmeans可直接对数据进行kmeans聚类。为了方便更好地掌握kmeans聚类算法,今天我们自己来实现一个弱化的版本mykmeans。 mykmeans输入包含三项,分别为聚类所使用的数据data,data每一行代表一个样本,每一列代表一个特征;聚类中心数量numclass;第三项为所使用的距离的定义,默认情况下为欧式距离。
function [cluster,clusterhistory]=mykmeans(data,numclass,varargin)% kmeans聚类。% 聚类过程动画显示% INPUTS:% data:每一行代表一个样本,每一列代表一个特征% numclass:聚类中心的数量% varargin{1}: 距离定义。支持euclidean(默认)、hamming% OUTPUT:% cluster: numsample行的列向量,代表每个样本的分类归属% clusterhistory{i}.cluster第i次迭代的聚类% clusterhistory{i}.core第i次迭代的聚类中心%% 公众号【数学建模公会】,HCLO4,20190902if nargin==2 method='euclidean';else method=varargin{1};end% 初始化聚类中心坐标,在数据点中随机选择numclass个点作为初始聚类中心。numsample=size(data,1);temp=randperm(numsample);core=data(temp(1:numclass),:);dis=caldis(data,core,method); %存储每个样本到当前聚类中心的距离% 执行迭代过程maxIter=20; % 最大迭代次数numiter=1;clusterhistory=cell(1,maxIter);while 1 newcore=zeros(size(core)); % 迭代聚类中心 [~,ind]=min(dis,[],2); %计算每个sample归属于哪个聚类中心,如果某个聚类中心没有一个点? for i=1:numclass newcore(i,:)=mean(data(ind==i,:)); end clusterhistory{numiter}.cluster=ind; clusterhistory{numiter}.core=core; if all(newcore(:)==core(:))||numiter>=maxIter % 迭代终止条件,聚类中心不再改变 cluster=ind; break end core=newcore; dis=caldis(data,core,method); numiter=numiter+1; end clusterhistory=clusterhistory(1:numiter);end % mykmeansfunction dis=caldis(data,core,method)%计算每个样本到当前聚类中心的距离numsample=size(data,1);numclass=size(core,1);dis=zeros(numsample,numclass);switch method case 'euclidean' for i=1:numclass dis(:,i)=sqrt(sum((data-repmat(core(i,:),numsample,1)).^2,2)); end case 'hamming' for i=1:numclass dis(:,i)=mean(data~=repmat(core(i,:),numsample,1),2); endend
下面一段代码用于测试mykmeans和kmeans的运行结果。生成两组服从二维高斯分布的数据,作为两个数据类别,分别使用mykmeans和matlab自带的kmeans函数进行聚类分析。
%%%%%%%%%%%%%%%%%%%%%%% 测试mykmeans函数 %%%%%%%%%%%%%%%%%%%%%%%%% 公众号【数学建模公会】,HCLO4,20190902%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 生成模拟数据,平面上的两组点,均服从二维高斯分布mu1=[2,8];sigma1=[2,2];mu2=[8,2];sigma2=[3,3];numsample1=100;numsample2=200;data1=zeros(numsample1,2);data1(:,1)=normrnd(mu1(1),sigma1(1),numsample1,1);data1(:,2)=normrnd(mu1(2),sigma1(2),numsample1,1);data2=zeros(numsample2,2);data2(:,1)=normrnd(mu2(1),sigma2(1),numsample2,1);data2(:,2)=normrnd(mu2(2),sigma2(2),numsample2,1);data=[data1;data2];tic,[cluster1,clusterhistory]=mykmeans(data,2);tocM=getFrame_Kmeans(data,clusterhistory); % 生成聚类动图tic,cluster2=kmeans(data,2);toc
下面的函数实现mykmeans聚类过程的可视化,仅针对二维数据和三维数据类型。
function M=getFrame_Kmeans(data,clusterhistory)% kmeans聚类过程可视化程序。只能对二维和三维数据可视化!%% INPUTS: % data: 聚类用的数据,每行代表一个样本,每列代表一个特征% clusterhistory: mykmeans函数输出的聚类过程数据%% OUTPUT:% kmeans聚类过程的动画。dimension=size(data,2);if dimension>3 % 若三维以上,只能通过pca降维处理 error('无法实现高于三维的数据的聚类可视化')endcolorset=cell(1,1000);colorset(1:8)={'r','g','b','k','c','m','y','k'};for i=9:1000 %如果聚类中心数量大于8,就使用随机的颜色。 colorset{i}=rand(1,3);endnumcore=length(unique(clusterhistory{1}.cluster));numiter=length(clusterhistory);for k=1:numiter figure hold on switch dimension case 2 for i=1:numcore ind=clusterhistory{k}.cluster==i; scatter(data(ind,1),data(ind,2),6,colorset{i}) core=clusterhistory{k}.core; plot(core(i,1),core(i,2),[colorset{i},'.'],'MarkerSize',20) end case 3 for i=1:numcore ind=clusterhistory{k}.cluster==i; scatter3(data(ind,1),data(ind,2),data(ind,3),6,colorset{i}) core=clusterhistory{k}.core; plot3(core(i,1),core(i,2),core(i,3),colorset{i},'MarkerSize',6) end end M(k)=getframe(gcf); frame = getframe(gcf); im = frame2im(frame); %将影片动画转换为编址图像,因为图像必须是index索引图像 imshow(im); [I,map] = rgb2ind(im,20); %将真彩色图像转化为索引图像 if k==1 imwrite(I,map,'kmeans.gif','gif','Loopcount',inf,'DelayTime',0.3); %Loopcount只是在i==1的时候才有用 else imwrite(I,map,'kmeans.gif','gif','WriteMode','append','DelayTime',1);%DelayTime:帧与帧之间的时间间隔 end close(gcf); end