海林老師《數據挖掘》(韓佳煒書)課程作業系列
要求:自己寫R/Python代碼、函數實現一系列算法
其他參見:
全文邏輯:
- 分析
- 算法/函數
- 測試數據
- 測試代碼
- 測試結果(截圖)
分析:
#繪圖包: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分爲三類聚類結果")