目录
前言
本文是基于划分的聚类算法研究与实现,实现了k均值及k中心点聚类算法,并在数据集上完成测试。用户输入k的值,可对数据集中的数据进行聚类。
一、k-means算法
k-means算法使用簇的均值点作为簇的形心。
首先在数据集中随机选择k个点作为k个簇的初始均值,对于数据集中的每个点,根据欧式距离将其分配至距离最近的簇。然后k均值算法开始迭代,对于每个簇,使用该簇内的点计算均值作为新的簇中心,再重新分配所有的点。迭代继续,直到分配稳定,所有的簇中心不在发生变化。
具体实现代码如下:
import random
import math
import numpy
def dataSet():#处理数据集
dataset=[]
fr=open("聚类实验数据/data.txt")
for line in fr.readlines():
currLineListFloat = []
line = line.split(" ")
line=line[0:-1]
for i in line: # 逐行将字符串数据转化为浮点数
currLineListFloat.append(float(i))
dataset.append(currLineListFloat)#数组矩阵
return dataset
def distance(x,y):#计算距离
error=[]
a=0
for i in range(0,len(x)):
error.append(abs(x[i]-y[i]))
for i in range(0,len(error)):
a=a+math.pow(error[i],2)
return math.sqrt(a)
data=dataSet()#320*690
k=int(input("please enter k:"))
k_spot=[]
for i in range(0,k):
j=random.randint(0,320)
k_spot.append(data[j])#k个随机点
time=0
while True:
time=time+1
new_k_spot=[]
kk=[]#存放数据属于哪一簇
for i in range(0,320):#分类
dist=[]
for j in range(0,k):
dist.append(distance(data[i],k_spot[j]))
kk.append(dist.index(min(dist)))
#print(kk)
print("第",time,"次迭代循环:")
for i in range(0,k):
print(kk.count(i),end=' ')
print("")
for i in range(0,k):#对k个簇分别求均值点
newspot=[]#存放第i簇分类后所有的点坐标
a=[]#存放新的均值点
for j in range(0,320):
if kk[j]==i:
newspot.append(data[j])#第i簇所有点的坐标
#print(len(newspot))
for t in range(0,690):
b=0
for m in range(0,len(newspot)):
b=b+newspot[m][t]
c=format(b/len(newspot), '.4f')#在t维度上的均值
a.append(float(c))
new_k_spot.append(a)
#print(new_k_spot)
if k_spot==new_k_spot:
break
else:
k_spot=new_k_spot[:]
二、k-medoids算法
k-medoids算法围绕中心点划分,使用簇的中心点作为簇的形心。
大致过程与k-means相同,就是在计算新的簇的形心时将均值方法改变为计算中心点方法。计算中心点使用该点到簇内其他所有点的距离之和作为根据,选择距离之和最小的点作为新的中心点(形心)。
具体实现代码如下:
import random
import math
import numpy
def dataSet():#处理数据集
dataset=[]
fr=open("聚类实验数据/data.txt")
for line in fr.readlines():
currLineListFloat = []
line = line.split(" ")
line=line[0:-1]
for i in line: # 逐行将字符串数据转化为浮点数
currLineListFloat.append(float(i))
dataset.append(currLineListFloat)#数组矩阵
return dataset
def distance(x,y):#计算距离
error=[]
a=0
for i in range(0,len(x)):
error.append(abs(x[i]-y[i]))
for i in range(0,len(error)):
a=a+math.pow(error[i],2)
return math.sqrt(a)
data=dataSet()#320*690
k=int(input("please enter k:"))
k_spot=[]
for i in range(0,k):
j=random.randint(0,320)
k_spot.append(data[j])#k个随机点
time=0
while True:
time=time+1
new_k_spot=[]
kk=[]#存放数据属于哪一簇
for i in range(0,320):#分类
dist=[]
for j in range(0,k):
dist.append(distance(data[i],k_spot[j]))
kk.append(dist.index(min(dist)))
#print(kk)
print("第",time,"次迭代循环:")
for i in range(0,k):
print(kk.count(i),end=' ')
print("")
for i in range(0,k):
#找出中心点
newspot=[]
for j in range(0,320):
if kk[j]==i:
newspot.append(data[j])#第i簇所有点的坐标
#print(len(newspot))
dist=[]
for t in newspot:
a=0
for m in newspot:
a=a+(distance(t,m))
dist.append(a)#a是t到其他所有点的距离
new_k_spot.append(newspot[dist.index(min(dist))])
if k_spot==new_k_spot:
break
else:
k_spot=new_k_spot[:]
三、实验结果展示
如图,上面的是k-means算法的结果,下面的是k-medoids算法的结果。算法的输出是每次迭代的结果,即每个簇中的点个数。
两种算法在k都等于10的情况下,迭代次数差异很大,运行时时间差异也较大。
总结
K-means算法的优势是算法复杂度小,对凸状的簇分类效果较好,对于大数据集有较好的伸缩性,但是对噪声(极值点)敏感,而且因其求均值的特点,对数据集的要求很高,要求所有的点处于一个欧式空间里,有很多噪声就会造成极大的误差;k-medoids的中心点取值只能是数据空间里的点,所以对数据集要求较小,受极值点影响也较小,削弱了异常值对结果的影响,但只能适用于小数据集,运行速度较慢。