1.kmeans算法
通过随机选取k个质心,计算所有样本点距离k个质心的距离,选取最短的质心,记录下来,(样本点,质心下标,到质心点距离),质心(n个维度值)对应空间维度上一个点,这样就找到k个质心的相似点(简单的计算欧式几何距离),对每个质心的相似点,计算均值(所有n个维度的),然后不断循环,直到整个数据集收敛,没有距离更短的质心出现。
2.二分k均值算法
由于k均值算法刚开始随机选取的质心,而且需要选取k,容易出现k的选取不合适导致整个数据聚类效果不好,产生的是局部最小值,而不是全局最小值。二分k均值算法先把整个数据集作为一个簇,然后二分这个簇。之后选取一个簇继续二分,选取的标准是:是否可以最大程度的降低SSE的值。上述基于SSE值的划分会不断进行,直到得到用户指定的簇的个数。
3.
''' 实现k-means算法 ''' import matplotlib.pyplot as plt import numpy as np import random from numpy import * from operator import * def loadDataSet(fileName): #general function to parse tab -delimited floats dataMat = [] #assume last column is target value fr = open(fileName) for line in fr.readlines(): curLine = line.strip().split('\t') dataMat.append(list(map(lambda x:float(x),curLine))) return dataMat #计算两个向量之间的欧式距离 def distEclud(vecA,vecB): #return sqrt(sum((vecA - vecB).A**2)) return sqrt(sum(power(vecA-vecB,2))) #生成k个随机的质心 #质心需要在边界范围内0 def randCent(dataSet,k): m,n=shape(dataSet) #生成一个(k,n)的矩阵 #zeros(())注意里面参数是个元组 centValue=mat(zeros((k,n))) print('centValue',centValue) #循环生成n个列向量,对应矩阵的n个向量,在n个向量的最小值和最大值范围内,向量的元素个数为k for i in range(n): minV=min(dataSet[:,i]) ranV=max(dataSet[:,i])-minV centValue[:,i]=minV.tolist()[0][0]+(ranV.tolist()[0][0])*(random.rand(k,1)) return centValue #画图 #画散点图 def plot(dataSet,centValue,centList): x1=dataSet[:,0] x2=dataSet[:,1] fig=plt.figure('k均值算法') ax=fig.add_subplot(111) ax.scatter(list(x1),list(x2),s=15,c='red',marker='s') ax.scatter(list(centValue[:,0]), list(centValue[:,1]), s=15, c='green', marker='x') ax.scatter(list(centList[:, 0]), list(centList[:, 1]), s=15, c='blue', marker='x') plt.show() #k均值算法 def kMeans(dataSet,k,caclDistance=distEclud,gerRandCent=randCent): m,n=shape(dataSet) #随机生成k个质心 centValue=gerRandCent(dataSet, k) #创建存储样本点距离质心的最短距离和质心下标的临时列表,初始化的为一些无效值 clusterList=mat(zeros((m,2))) clusterList[:,0]=-1 clusterList[:,1]=inf #循环计算每个样本点距离质心的最短距离,直到获取到所有样本点最新的质心 isChanged=True while isChanged: isChanged=False for i in range(m): # 选择距离当前样本点最新的那个质心 minIndx = clusterList[i, 0] minDist = clusterList[i, 1] for j in range(k): tempDist = (caclDistance(dataSet[i, :], centValue[j, :])) ** 2 if tempDist < minDist: minDist = tempDist minIndx = j # 发现更小的 if clusterList[i, 0] != minIndx: isChanged=True clusterList[i, :] = minIndx, minDist # 更新质心 for i in range(k): # 获取第k个质心临近的样本点 kData = dataSet[nonzero(clusterList[:, 0] == i)[0]] # 取列方向的平均值 centValue[i, :] = mean(kData, axis=0) print('centValue:\n',centValue) return centValue,clusterList #二分法寻找最优聚类 def biKmeans(dataSet, k, distMeas=distEclud): m = shape(dataSet)[0] clusterAssment = mat(zeros((m,2))) centroid0 = mean(dataSet, axis=0).tolist()[0] centList =[centroid0] #create a list with one centroid for j in range(m):#calc initial Error clusterAssment[j,1] = distMeas(mat(centroid0), dataSet[j,:])**2 while (len(centList) < k): lowestSSE = inf #遍历当前的质心下标 for i in range(len(centList)): #选取等于当前质心下标的样本点 ptsInCurrCluster = dataSet[nonzero(clusterAssment[:,0].A==i)[0],:]#get the data points currently in cluster i #使用kMean算法对当前数据集进行二分,产生两个质心和对应样本点距离两个质心的最短距离,注意是针对上面等于当前质心下标的样本点 centroidMat, splitClustAss = kMeans(ptsInCurrCluster, 2, distMeas) #计算划分的SSE,即误差和(一个质心变两个,对应着一个样本集变两个) sseSplit = sum(splitClustAss[:,1])#compare the SSE to the currrent minimum #计算非当前质心的其他质心的SSE sseNotSplit = sum(clusterAssment[nonzero(clusterAssment[:,0].A!=i)[0],1]) print("sseSplit, and notSplit: ",sseSplit,sseNotSplit) #计算划分后的最优SSE,即最有序的生成簇 if (sseSplit + sseNotSplit) < lowestSSE: #最优的需要拆分的质心:一个簇,变成两个 bestCentToSplit = i #最优的二分质心点的值 bestNewCents = centroidMat #最优的距离上面两个质心的样本的距离 bestClustAss = splitClustAss.copy() #最优的SSE的值 lowestSSE = sseSplit + sseNotSplit #更新最优的距离上面两个质心的样本的距离矩阵等于1的(产生两个质心中的一个)质心值,取当前质心列表元素的个数(原来下标是0-len(cenList)-1) #对应下面的向质心列表中新增那个质心下标 bestClustAss[nonzero(bestClustAss[:,0].A == 1)[0],0] = len(centList) #change 1 to 3,4, or whatever #更新最优的距离上面两个质心的样本的距离矩阵等于0的(产生两个质心中的一个)质心值,取最优的质心,也就是要进行划分的那个质心 bestClustAss[nonzero(bestClustAss[:,0].A == 0)[0],0] = bestCentToSplit print('the bestCentToSplit is: ',bestCentToSplit) print('the len of bestClustAss is: ', len(bestClustAss)) #更新质心列表 #被划分的那个质心,对应的点的值,取0的对应的二分后的那个质质心值 centList[bestCentToSplit] = bestNewCents[0,:].tolist()[0]#replace a centroid with two best centroids #新增一个质心 centList.append(bestNewCents[1,:].tolist()[0]) print("划分前的最优距离:\n", shape(clusterAssment[nonzero(clusterAssment[:, 0].A == bestCentToSplit)[0], :])) print('划分后的最优距离:\n',shape(bestClustAss)) #更新原来到最优质心的那些样本点对应的值,改成现在新的质心和对应距离,完成划分的动作,两者的shape应该一样的,因为对bestCentToSplit #划分前后,分成质心0和质心1两个集合 clusterAssment[nonzero(clusterAssment[:,0].A == bestCentToSplit)[0],:]= bestClustAss#reassign new clusters, and SSE return mat(centList), clusterAssment fileName=r'D:\software\python\sourcecode_and_data\MLiA_SourceCode\machinelearninginaction\Ch10\testSet2.txt' dataSet=loadDataSet(fileName) print('数据集:\n',mat(dataSet)) dataSetMatrix=mat(dataSet) distance=distEclud(dataSetMatrix[0],dataSetMatrix[1]) print('向量距离:\n',distance) centValue2=randCent(dataSetMatrix,2) print('质心:\n',centValue2) centValue,clusterList=kMeans(dataSetMatrix,4) centList, clusterAssment=biKmeans(dataSetMatrix, 4) print('centList:\n',centList) print('clusterAssment:\n',clusterAssment) plot(dataSetMatrix,centValue,centList)