海林老师《数据挖掘》(韩佳炜书)课程作业系列
要求:自己写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分为三类聚类结果")