K-means聚类算法
算法优缺点:
优点:容易实现
缺点:可能收敛到局部最小值,在大规模数据集上收敛较慢
使用数据类型:数值型数据
算法思想
k-means算法实际上就是通过计算不同样本间的距离来判断他们的相近关系的,相近的就会放到同一个类别中去。
1.首先我们需要选择一个k值,也就是我们希望把数据分成多少类,这里k值的选择对结果的影响很大,Ng的课说的选择方法有两种一种是elbow method,简单的说就是根据聚类的结果和k的函数关系判断k为多少的时候效果最好。另一种则是根据具体的需求确定,比如说进行衬衫尺寸的聚类你可能就会考虑分成三类(L,M,S)等
2.然后我们需要选择最初的聚类点(或者叫质心),这里的选择一般是随机选择的,代码中的是在数据范围内随机选择,另一种是随机选择数据中的点。这些点的选择会很大程度上影响到最终的结果,也就是说运气不好的话就到局部最小值去了。这里有两种处理方法,一种是多次取均值,另一种则是后面的改进算法(bisecting K-means)
3.终于我们开始进入正题了,接下来我们会把数据集中所有的点都计算下与这些质心的距离,把它们分到离它们质心最近的那一类中去。完成后我们则需要将每个簇算出平均值,用这个点作为新的质心。反复重复这两步,直到收敛我们就得到了最终的结果。
函数
loadDataSet(fileName)
从文件中读取数据集distEclud(vecA, vecB)
计算距离,这里用的是欧氏距离,当然其他合理的距离都是可以的randCent(dataSet, k)
随机生成初始的质心,这里是虽具选取数据范围内的点kMeans(dataSet, k, distMeas=distEclud,
createCent=randCent)
kmeans算法,输入数据和k值。后面两个事可选的距离计算方式和初始质心的选择方式show(dataSet, k, centroids, clusterAssment)
可视化结果
#coding=utf-8 from numpy import * from matplotlib import pyplot as plt def loadDataSet(fileName): dataMat = [] fr = open(fileName) for line in fr.readlines(): curLine = line.strip().split(',') print curLine fltLine = map(float, curLine) dataMat.append(fltLine) return dataMat #计算两个向量的距离,用的是欧几里得距离 def distEclud(vecA, vecB): return sqrt(sum(power(vecA - vecB, 2))) #随机生成初始的质心(ng的课说的初始方式是随机选K个点) def randCent(dataSet, k): #数组 维数 列 n = shape(dataSet)[1] #66 centroids = mat(zeros((k,n))) print 'centroids',centroids for j in range(n): #某列中的最小值 minJ = min(dataSet[:,j]) #某列中最大值-最小值 rangeJ = float(max(array(dataSet)[:,j]) - minJ) print 'rangeJ',rangeJ centroids[:,j] = minJ + rangeJ * random.rand(k,1) return centroids def kMeans(dataSet, k, distMeas=distEclud, createCent=randCent): m = shape(dataSet)[0] #create mat to assign data points to a centroid, also holds SE of each point clusterAssment = mat(zeros((m,2))) centroids = createCent(dataSet, k) clusterChanged = True while clusterChanged: clusterChanged = False #for each data point assign it to the closest centroid for i in range(m): minDist = inf minIndex = -1 for j in range(k): distJI = distMeas(centroids[j,:],dataSet[i,:]) if distJI < minDist: minDist = distJI; minIndex = j if clusterAssment[i,0] != minIndex: clusterChanged = True clusterAssment[i,:] = minIndex,minDist**2 for cent in range(k):#recalculate centroids ptsInClust = dataSet[nonzero(clusterAssment[:,0].A==cent)[0]]#get all the point in this cluster centroids[cent,:] = mean(ptsInClust, axis=0) #assign centroid to mean return centroids, clusterAssment def show(dataSet, k, centroids, clusterAssment): #查看矩阵或者数组的维数 ,行数 列数 numSamples, dim = dataSet.shape mark = ['or', 'ob', 'og', 'ok', '^r', '+r', 'sr', 'dr', '<r', 'pr'] for i in xrange(numSamples): markIndex = int(clusterAssment[i, 0]) plt.plot(dataSet[i, 0], dataSet[i, 1], mark[markIndex]) mark = ['Dr', 'Db', 'Dg', 'Dk', '^b', '+b', 'sb', 'db', '<b', 'pb'] for i in range(k): plt.plot(centroids[i, 0], centroids[i, 1], mark[i], markersize = 12) plt.show() def main(): dataMat = mat(loadDataSet('testSet.txt')) myCentroids, clustAssing= kMeans(dataMat,4) show(dataMat, 4, myCentroids, clustAssing) if __name__ == '__main__': main()
36, 3624, 2.1, 69.05, 15.1, 41.3, 20, 50
365, 6315, 1.5, 69.31, 11.3, 66.7, 152, 56
2212, 4530, 1.8, 70.55, 7.8, 58.1, 15, 113
2110, 3378, 1.9, 70.66, 10.1, 39.9, 65, 519
21, 5114, 1.1, 71.71, 10.3, 62.6, 20, 156