一、聚类
1.1聚类任务
聚类试图将数据集中的样本划分为若干个通常是不相交的子集,每个子集称为一个“簇”(cluster)。
1.2 性能度量
聚类性能度量亦称聚类“有效性指标”(validity index)。与监督学习中的性能度量作用类似,对于聚类结果,我们需要通过某种性能度量来评估其好坏;另一方面,若明确了最终要使用的性能度量,则可直接将其作为聚类过程的优化目标,从而更好地得到符合要求的聚类结果。
1.2.1 常用的外部指标:
1.2.2 常用内部指标
1.3 距离计算
二、K-Means算法
2.1 定义
K-Means算法可以发现k个不同的簇, 且每个簇的中心采用簇中所含值的均值计算而成。
2.2 K-Means算法过程
2.3 实现代码
# -*- coding: utf-8 -*- """ Created on Thu May 10 10:12:53 2018 @author: lizihua """ from numpy import * import matplotlib.pyplot as plt #加载数据 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 distEclud(vecA,vecB): return sqrt(sum(power(vecA-vecB,2))) #随机选取k个簇的质心 def randCent(dataSet,k): n = shape(dataSet)[1] centroids = mat(zeros((k,n))) for j in range(n): minJ = min(dataSet[:,j]) rangeJ = float(max(dataSet[:,j])-minJ) #random.rand(k,1)随机产生k行1列的array,数组的值的范围:[0,1) #min+(max-min)*(0,1)之间的数,保证了质心在数据集边界之内 centroids[:,j] = minJ + rangeJ*random.rand(k,1) return centroids #K-means聚类算法 def kMeans(dataSet,k,distM = distEclud,createCent = randCent): m = shape(dataSet)[0] #创建矩阵来存储每个点的簇的分配结果,第一列记录簇索引值,第二列存储误差(点到簇质心的距离) #且默认所有点的簇都为0 clusterAssment = mat(zeros((m,2))) #随机选取k个簇质心 centroids = createCent(dataSet,k) #标志变量clusterChanged,用以标志簇分配结果是否发生变化 clusterChanged = True while clusterChanged: clusterChanged = False #对于每一个点,计算每个点与k个簇质心的距离,并每个点距离k个簇质心中的最小距离的那个簇 for i in range(m): minDist = inf minIndex = -1 for j in range(k): distJI = distM(centroids[j,:],dataSet[i,:]) if distJI < minDist: minDist = distJI ; minIndex = j #如果簇索引发生变化,则,标志变量clusterChanged为True if clusterAssment[i,0] != minIndex: clusterChanged = True #反之,则将离该点最近的簇索引和距离的平方存储到clusterAssment clusterAssment[i,:] = minIndex,minDist**2 print("centroids:\n",centroids) #遍历所有簇质心 for cent in range(k): #通过数组过滤来获得给定簇的所有点 pstInClust = dataSet[nonzero(clusterAssment[:,0].A == cent)[0]] #计算所有点的均值 centroids[cent,:] = mean(pstInClust,axis = 0) #返回类质心和分配结果 return centroids,clusterAssment def kMeansPlot(dataMat,centroids,clusterAssment): k=len(centroids) fig = plt.figure() ax = fig.add_subplot(111) ax.scatter(centroids[:,0].tolist(),centroids[:,1].tolist(),marker='+',c='r') markers=['o','s','v','*'];colors=['blue','green','yellow','red'] for i in range(k): data_class=dataMat[nonzero(clusterAssment[:,0].A == i)[0]] ax.scatter(data_class[:,0].tolist(),data_class[:,1].tolist(),marker=markers[i],c=colors[i]) plt.show() #测试 if __name__ =="__main__": dataMat = mat(loadDataSet('testSet.txt')) print("簇质心:\n",randCent(dataMat,2)) print("距离:\n",distEclud(dataMat[0],dataMat[1])) #距离: 5.18463281668 myCentroids, clustAssing = kMeans(dataMat,4) print("类质心:\n",myCentroids) print("点分配结果:\n",clustAssing) kMeansPlot(dataMat,myCentroids, clustAssing)
2.4 结果显示
三、二分K-Means算法
3.1 实现方法
二分K-Means算法首先将所有点作为一个簇,然后将该簇一分为二。之后选择其中一个簇继续进行划分,选择哪一个簇进行划分取决于对其划分是否可以最大程度降低SSE(Sum of Squared Error,误差平方和)的值。上述基于SSE的划分过程不断重复,直到得到用户指定的簇数目为止。
另一种做法是:选择SSE最大的簇进行划分,直到簇数目达到用户指定的数目为止。
3.2 实现代码
#二分K-Means算法 def biKmeans(dataSet, k, distM = distEclud): m = shape(dataSet)[0] #创建矩阵来存储每个点的簇的分配结果,第一列记录簇索引值,第二列存储误差(点到簇质心的距离) clusterAssment = mat(zeros((m,2))) #计算整个数据集的每个特征的质心,centroid0:1*n列表 centroid0 = mean(dataSet,axis = 0).tolist()[0] #用centList列表保存所有质心 centList = [centroid0] #遍历所有点,计算每个点到质心的误差值,并保存 for j in range(m): clusterAssment[j,1] = distM(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],:] centroidMat,splitClustAss = kMeans(ptsInCurrCluster,2,distM) sseSplit = sum(splitClustAss[:,1]) #剩余数据集的误差 sseNotSplit = sum(clusterAssment[nonzero(clusterAssment[:,0].A != i)[0],1]) print("sseSplit,and notSplit:",sseSplit,sseNotSplit) #若该簇划分后的误差和剩余数据的误差之和作为本次最终误差, #若本次划分的SSE值最小,则本次划分保存,即执行划分操作 if (sseSplit + sseNotSplit) <lowestSSE: bestCenToSplit = i bestNewCents = centroidMat bestClustAss = splitClustAss.copy() lowestSSE = sseSplit + sseNotSplit #更新簇的分配结果,且新的质点会被添加到centList中 bestClustAss[nonzero(bestClustAss[:,0].A == 1)[0],0] = len(centList) bestClustAss[nonzero(bestClustAss[:,0].A == 0)[0],0] = bestCenToSplit print("the bestCentToSplit is :",bestCenToSplit) print("the len of bestClustAss is:",len(bestClustAss)) centList[bestCenToSplit] = bestNewCents[0,:].tolist()[0] centList.append(bestNewCents[1,:].tolist()[0]) clusterAssment[nonzero(clusterAssment[:,0].A == bestCenToSplit)[0],:] =bestClustAss return mat(centList),clusterAssment def kMeansPlot(dataMat,centroids,clusterAssment): k=len(centroids) fig = plt.figure() ax = fig.add_subplot(111) ax.scatter(centroids[:,0].tolist(),centroids[:,1].tolist(),marker='+',c='r') markers=['o','s','v','*'];colors=['blue','green','yellow','red'] for i in range(k): data_class=dataMat[nonzero(clusterAssment[:,0].A == i)[0]] ax.scatter(data_class[:,0].tolist(),data_class[:,1].tolist(),marker=markers[i],c=colors[i]) plt.show() #测试 if __name__ =="__main__": dataMat = mat(loadDataSet('testSet2.txt')) centList,myNewAssments = biKmeans(dataMat,3) print("centList:\n",centList) kMeansPlot(dataMat,centList,myNewAssments)