K均值聚类算法
K均值聚类简介:聚类是一种无监督的学习,即数据没有所属分类的标签,通过计算数据间的“距离”,将“距离”上靠近的一些数据划分为一类,这样就将无序的数据进行了分类,至于最后划分成几类,也就是K均值聚类算法中的K了。
K均值的工作流程:(因为要计算距离,所以k均值适用的数据类型是数值型的,不是该类型的想办法转化为数值型数据。) 首先,随机选取K个点做为质心,(注意,这些点也即质心的维度和数据的特征维度是一样的,因为要计算距离啊,总得是一样维度的吧啊哈哈哈),第一次遍历整个数据集,计算每一条数据和K个质心的距离,过程是这样的,每一条数据都要和k个质心计算一下距离,都计算完了找到距离最小的,记录该距离并把对应的是哪个质心记录下来,然后对下一条数据进行计算。第一次遍历结束所有数据都有了所属的分类,整个数据集被分为了K类。
然后更新质心,计算每个类里数据的平均值,以该值做为新的质心。更新完所有的质心,重新遍历数据集,和第一次过程一样,不同的是找到最小距离后先比较一下和原来的类别是否一样,不一样的话记录距离和所属类别,然后因为分类有变化需要再次更新质心和进行下一轮的遍历,直到所有的质心都不再变化,即所有数据的分类都不再变化算法结束。
上述过程的伪代码如下:
创建K个点做为初始质心(一般是随机选择)
当任意一个点的分类结果有变化时:
对数据集中的每条数据
对每个质心(一共K个)
计算质心和某一条数据间的距离
将数据分类到距离最近的质心
基于每个分类里的数据,以它们的均值做为新的质心
程序实现如下:
支持函数部分
def loadDataSet(fileName):
dataMat = []
fr = open(fileName)
for line in fr.readlines():
curLine = line.strip().split(‘\t’)
fltLine = list(map(float,curLine))
dataMat.append(fltLine)
return dataMat
def disEclud(numA,numB):
return sqrt(sum(power(numA-numB,2)))
def randCent(dataSet,k):
n = shape(dataSet)[1]
centropoint = mat(zeros((k,n)))
for j in range(n):
minj = min(dataSet[:,j])
maxj = max(dataSet[:,j])
rangej = float(maxj-minj)
centropoint[:,j] = minj + rangej*random.rand(k,1)
return centropoint
对支持函数的解析:
第一个loadDataSet函数是读取数据集,数据是以txt文本格式保存的,每条数据的不同特征之间用空格分开,程序每次读取一行数据,strip()函数是除去每行开头或结尾指定的字符,默认是空格或换行符;split()是分割函数,以指定字符分割字符串,本程序是以空格将不同的特征分开;map()函数有两个参数,第一个是一个函数,第二个是待处理的数据,map将第二个参数提供的数据依次经过第一个函数处理,返回结果,本程序中是将得到的特征数据都转换为浮点型变量,每一行数据存为一个list,通过append函数(比较与extend函数的区别)将list加在dataMat中,得到可用的数据集。
第二个disEclud函数是计算距离的函数,两个n维的数据,对应位置相减计算平方和,再开根号可得到两点的距离。
第三个randCent函数是得到随机的K个初始质心,虽然说是随机的但是也是在一定范围的,范围就是保证每一维度得到的点都在整个数据集内部,(如果初始值选在整个数据集的外部那误差就太大了)。Shape()函数得到dataSet的行和列,[1]表示取列,zeros()先得到一个k行n列的值为零的数组,mat()函数将数组转换为矩阵;找到数据集每一列的最小值和最大值,最大值减去最小值即数据的范围,最小值加一个变化范围乘以0到1的随机值就得到了在数据集范围内随机选出的质心。
K均值算法实现:
def kMeans(dataSet,k,disMeans=disEclud,createCent=randCent):
m = shape(dataSet)[0]
dataTag = mat(zeros(m,2))
centropoint = createCent(dataSet,k)
flag = True
while flag:
flag = False
for i in range(m):
minj = inf
minindex = -1
for j in range(k):
distance = createCent(centropoint[j,:],dataSet[i,:])
if distance < minj:
minj = distance
minindex = j
if dataTag[i,0] != minindex:
dataTag[i,:] = minindex,minj**2
flag = True
print(centropoint)
for n in range(k):
dataSelect = dataSet[nonzero(dataTag[:,0].A==n)[0]]
centropoint[k,:] = mean(dataSelect,axis=0)
return centropoint,dataTag
对K均值算法的解析:
首先得到整个数据集的行数,定义一个m行2列的矩阵用来记录每条数据的分类信息和与质心的距离;然后调用函数生成k个随机质心,标志位位true,遍历整个数据集用minj和minindex记录最小的距离和所属分类,并且如果分类有变化标志位计为true,更新质心再次遍历数据集进行计算并分类;每一次遍历完数据集更新质心,更新质心关键的一句是
dataSelect = dataSet[nonzero(dataTag[:,0].A==n)[0]],nonzeros(a)返回数组a中值不为零的元素的下标,true不为零,false为零,所以通过这一句可以选出数据集中属于某一类的数据,然后计算筛选出数据的平均值,mean函数计算平均值,axis=0表示按列计算平均值,即每一个特征列计算其平均值,得到的就是该类数据的中心。最后返回更新的质心和每条数据所属的分类以及他们距离各自中心点的距离。
至此,k均值聚类算法基本实现。
(算法存在的问题,请看系列文章二分k均值算法)