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分爲三類聚類結果")

測試結果:(效果明顯不如基於密度的聚類方法好)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章