k-均值:
因为可以发现k个不同的簇,且每个簇的中心采用簇中所含的均值计算而成。
聚类和分类的区别:
最大的区别在于,分类的目标事先已知,而聚类的不一样,其产生的结果和分类相同,但是类别没有预先定义,因此聚类有时候也被称为 无监督分类
代码如下:
from numpy import *
def loadDataSet(fileName):
dataMat = []
fr = open(fileName)
for line in fr.readlines():
curLine = line.strip().split('\t')
fltLine = map(float, curLine) #python2中源代码
fltLine = list(map(float, curLine)) #python3中修改后的代码
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)
centroids[:, j] = minJ + rangeJ * random.rand(k, 1)
return centroids
k-均值聚类算法:
def kMeans(dataSet, k, distMeas=distEclud, createCent=randCent):
m = shape(dataSet)[0]
clusterAssment = mat(zeros((m, 2)))
# 创建k个初识的质心向量
centroids = createCent(dataSet, k)
# 聚类结果是否发生变化的布尔类型
clusterChanged = True
# 如果聚类结果发生了变化,就一直执行聚类算法,直到所有的数据点聚类结果不再发生变化
while clusterChanged:
clusterChanged = False
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
# 当聚类结果中的第i个样本发生变化时,布尔类型变为真,继续聚类算法
if clusterAssment[i, 0] != minIndex:
clusterChanged = True
# 更新当前变化的聚类样本的质心索引和平方误差
clusterAssment[i, :] = minIndex, minDist**2
print(centroids)
for cent in range(k):
ptsInClust = dataSet[nonzero(clusterAssment[:, 0].A == cent)[0]]
centroids[cent, :] = mean(ptsInClust, axis=0)
return centroids, clusterAssment
提高聚类性能:有时候K-均值收敛但是聚类的较差,主要原因是k-均值算法收敛到了局部最小值,而非全局最小值(局部最小值指结果还可以但是并非是最好结果,全局最小值可能是最好结果)
一种用于度量聚类效果的指标是SSE(误差平均和),SSE值越小表示数据点越接近它们的质心,聚类效果也越好。
聚类的目标是在保持簇数目不变的情况下提高簇的质量。
改进方法:
1、合并最近的质心 或者 合并两个使得SSE增幅最小的质心
2、合并两个簇然后计算总的SSE,遍历所有可能的两个簇,找到最佳的两个簇。
二分k-均值算法
为了克服k-均值算法收敛于局部最小值的问题,有人提出了另一个称为二分k-均值的算法。该算法首先将所有点作为一个簇,然后将该簇一分为二。之后选择其中一个簇继续进行划分,选择哪一个簇进行划分取决于其划分是否可以最大程度降低SSE的值。不断重复上述的基于SSE的划分过程,直到得到指定的簇数目为止。
def biKmeans(dataSet, k, distMeas=distEclud):
m = shape(dataSet)[0]
# 初始化,簇点都为0
clusterAssment = mat(zeros((m, 2)))
# 起始的第一个聚类点,即所有点的质心
centroid0 = mean(dataSet, axis=0).tolist()[0] # axis=0:压缩行,对各列求均值,返回一个1*n的矩阵
centList = [centroid0]
for j in range(m):
# 计算当前聚为一类时各个数据点距离质心的平方距离
clusterAssment[j, 1] = distMeas(mat(centroid0), dataSet[j, :])**2
while (len(centList) < k):
# 将当前的最小误差设置为正无穷
lowestSSE = inf
for i in range(len(centList)):
# 通过数组过滤筛选出属于第i簇的数据集合
ptsInCurrCluster = dataSet[nonzero(clusterAssment[:, 0].A == i)[0], :]
# 对于该簇利用二分K-均值算法进行划分,返回划分后的结果及误差
centroidMat, splitClusterAss = kMeans(ptsInCurrCluster, 2, distMeas)
# 计算该簇划分为的两个簇的误差平方的和
sseSplit = sum(splitClusterAss[:, 1])
# 计算未划分的的簇中数据的误差平方的和
sseNotSplit = sum(clusterAssment[nonzero(clusterAssment[:, 0].A != i)[0], 1])
print("sseSplit, and notSplot: ", sseSplit, sseNotSplit)
# 遍历当前簇后划分后找到最佳的划分簇,更新
if (sseSplit + sseNotSplit) < lowestSSE:
bestCentToSplit = i
bestNewCents = centroidMat
bestClustAss = splitClusterAss.copy()
lowestSSE = sseSplit + sseNotSplit
# 对决定要划分的簇中所有点的簇分配结果进行修改
bestClustAss[nonzero(bestClustAss[:, 0].A == 1)[0], 0] = len(centList)
bestClustAss[nonzero(bestClustAss[:, 0].A == 0)[0], 0] = bestCentToSplit
print('the bestCentToSplit is:', bestCentToSplit)
print('the len of bestClustAss is: ', len(bestClustAss))
# 新的簇的分配结果更新,新的质心添加到centList中
centList[bestCentToSplit] = bestNewCents[0, :]
centList.append(bestNewCents[1, :])
clusterAssment[nonzero(clusterAssment[:, 0].A == bestCentToSplit)[0], :] = bestClustAss
return mat(centList), clusterAssment #python2中的源代码
return centList, clusterAssment #python3中修改