R-数据挖掘-聚类Kmeans(六)

海林老师《数据挖掘》(韩佳炜书)课程作业系列

要求:自己写R/Python代码、函数实现一系列算法

其他参见:

R-数据挖掘-求混合型数据对象距离(一)

R-数据挖掘-主成分分析PCA(二)

R-数据挖掘-关联规则(三)

R-数据挖掘-决策树ID3(四)

R-数据挖掘-贝叶斯分类(五)

R-数据挖掘-聚类Kmeans(六)

R-数据挖掘-聚类DBSCAN(七)

全文逻辑:

  1. 分析
  2. 算法/函数
  3. 测试数据
  4. 测试代码
  5. 测试结果(截图)

分析:

#绘图包:library(ggplot2)
#测试数据:iris及自定义数据
##输入:n维数值型对象
##返回:[[1]]聚类中心;[[2]]每个簇中包含的对象序号
###可能的问题:Error in if (distance < mindis) { : missing value where TRUE/FALSE needed
###原因:随机数的问题。。

 

算法实现(编写函数):

k_means<-function(data,k){
  #求欧式距离
  ###data1,data2可以为两个数值向量
  ###返回两向量/数值之间的欧式距离
  osdis<-function(data1,data2){
    return(sqrt(sum((data1-data2)^2)))
  }
  #随机初始化K个质心(质心满足数据边界之内)
  ###返回初始质心矩阵
  randheart<-function(data,k){
    #得到数据维度-列数
    n=ncol(data)
    #创建初始化质心矩阵,k行n列
    results=matrix(NA,nrow = k,ncol = n)
    #遍历列
    for (i in 1:n) {
      range=max(data[,i])-min(data[,i])
      results[,i]=min(data[,i])+range*runif(k) #这里需要要转换思维,这里的min(dataset[,i])、runif(k)以及我们的结果都是k维的向量,只有range是常数
    }
    return(results)
  }
  
  #获取样本数
  m=nrow(data)
  n=ncol(data)#列数
  #创建初始化质心矩阵
  heart=randheart(data,k)
  #创建聚类结果是否发生变化-布尔变量
  ischanged=T
  #创建向量存储-对象属于哪一类
  fenlei=rep(0,m)
  #只要聚类结果一直发生变化,就一直执行聚类算法,直至所有数据点聚类结果不变化
  while(ischanged){
    #先将聚类结果改为F
    ischanged=F
    #遍历数据集
    for (i in 1:m) {
      #初始化数据与质心之间最小距离为Inf,
      mindis=Inf
      minlei=-1
      #遍历初始化质心
      for(j in 1:k){
        #计算每条数据与每个质心的距离
        distance=osdis(data[i,],heart[j,])
        if(distance<mindis){
          mindis=distance
          minlei=j
        }
      }
      if(fenlei[i]!=minlei){
        ischanged=T
        fenlei[i]=minlei
      }
    }
    
    #更新当前质心
    if(ischanged){
      for (q in 1:k) {
        linshi=data[which(fenlei==q),]
        heart[q,]=apply(linshi, 2, mean)
      }
    }
  }
  #################循环结束,达到最佳聚类。此时fenlei记录每个对象所属类标签,heart记录最终的类中心
  results=list()
  #类中心
  results[[1]]=heart
  #求每个类找出相应的对象序号
  cu=NULL
  for (q in 1:k) {
    cu=c(cu,list(which(fenlei==q)))
  }
  results[[2]]=cu
  #返回每个类包含哪几个数据以及每个类的中心
  return(results)
}

数据测试:

测试数据:第一个测试数据iris

#########################第一个测试数据iris
library(ggplot2)
newIris<-iris[,1:2]
kc<-k_means(newIris,3)
##为画图做准备!!即给每个对象添加类标签
newIris[kc[[2]][[1]],3]=1
newIris[kc[[2]][[2]],3]=2
newIris[kc[[2]][[3]],3]=3
##对类中心的处理(为了画图)
newIris_heart=kc[[1]]
newIris_heart=as.data.frame(newIris_heart)
newIris_heart[,3]=0
names(newIris_heart)=names(newIris)
newIris=rbind(newIris,newIris_heart)
####画图!!标注类中心
ggplot(newIris,aes(Sepal.Length,Sepal.Width)) + geom_point(size=2.5)+
  labs(title="Iris原数据分布")
ggplot(newIris,aes(Sepal.Length,Sepal.Width)) + geom_point(size=2.5, aes(colour=factor(newIris[,3])))+
  labs(title="Iris kmeans分为三类聚类结果")

结果:

 

测试数据:第二个测试数据(为了和基于密度的聚类dbscan效果对比)详见此链接

x1 <- seq(0,pi,length.out=100)
y1 <- sin(x1) + 0.1*rnorm(100)
x2 <- 1.5+ seq(0,pi,length.out=100)
y2 <- cos(x2) + 0.1*rnorm(100)
data <- data.frame(c(x1,x2),c(y1,y2))
names(data) <- c('x','y')
##############################################

ggplot(data,aes(x,y))+geom_point()+
  labs(title="原数据展示")
p<-ggplot(data,aes(x,y))
##############################################指定聚为两类
mm2=k_means(data,2)
data[mm2[[2]][[1]],3]=1
data[mm2[[2]][[2]],3]=2
p + geom_point(size=2.5, aes(colour=factor(data[,3])))+
  labs(title="kmeans分为两类聚类结果")
################################################指定聚为三类
mm3=k_means(data,3)
data[mm3[[2]][[1]],3]=1
data[mm3[[2]][[2]],3]=2
data[mm3[[2]][[3]],3]=3
p + geom_point(size=2.5, aes(colour=factor(data[,3])))+
  labs(title="kmeans分为三类聚类结果")

测试结果:(效果明显不如基于密度的聚类方法好)

猜你喜欢

转载自blog.csdn.net/kelanj/article/details/89577319