利用K-均值聚类算法对未标注数据进行分组
实验数据:
testSet.txt,每行包含两个数值型数据
实验要求:
利用K-均值聚类算法对数据进行分组,由于是无标注数据,实验结果为误差平方和,这里的误差是一个点到簇中心的距离。
实验步骤:
1.处理数据:将数据转化为矩阵形式,函数为loadDataSet(fileName),注释:fileName为文件名称,输出为数据集的矩阵形式;
2.距离计算:计算点到簇中心的距离,使用欧式距离即可,函数为distEclud(vecA,vecB),vecA和vecB分别是数据点和簇中心的坐标
3.初始化簇中心:随机质心必须要在整个数据集的边界之内,可以用列的最小值+random.rand(k,1)*该列的最大值和最小值之差来得到,函数为randCent(dataSet,k),dataSet为数据矩阵,k表示有k个质心,输出为初始质心;
4.K-均值聚类算法:伪代码如下:
创建k个点作为起始质心
当任意一个点的簇分配结果发生改变时
对数据集中的每个数据点
对每个质心计算质心与数据点之间的距离
将数据点分配到距其最近的簇,并保存该点到簇中心的距离
对每一个簇,计算簇中所有点的均值并将均值作为质心
函数为kMeans(dataSet,k),dataSet为数据矩阵,k表示有k个质心,输出为质心、每个点所属的类别和误差,这里的误差是当前点到簇质心的距离;
5.测试算法:使用量化的误差指标误差平方和来评价算法的结果,函数为metric(clusterAssment),clusterAssment包含两列,一列记录索引值,一列记录误差,返回误差平方和。
import matplotlib.pyplot as plt
from numpy import *
# 加载数据
def loadDataSet(fileName): # 解析文件,按tab分割字段,得到一个浮点数字类型的矩阵
dataMat = [] # 文件的最后一个字段是类别标签
fr = open(fileName)
for line in fr.readlines():
curLine = line.strip().split('\t')
curLine = [float(i) for i in curLine] # 将每个元素转成float类型
dataMat.append(curLine)
return dataMat
# 计算欧几里得距离
def distEclud(vecA, vecB):
return sum(power(vecA - vecB, 2)) # 求两个向量之间的距离
def randCent(dataSet, k):
n = shape(dataSet)[1]
centroids = mat(zeros((k, n))) # 每个质心有n个坐标值,总共要k个质心
for j in range(n):
minJ = min(dataSet[:, j])
maxJ = max(dataSet[:, j])
rangeJ = float(maxJ - minJ)
centroids[:, j] = minJ + rangeJ * random.rand(k, 1)
return centroids
# k-means 聚类算法
def kMeans(dataSet, k, distMeans=distEclud, createCent=randCent):
m = shape(dataSet)[0]
clusterAssment = mat(zeros((m, 2))) # 用于存放该样本属于哪类及质心距离
# clusterAssment第一列存放该数据所属的中心点,第二列是该数据到中心点的距离
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 = distMeans(centroids[j, :], dataSet[i, :])
if distJI < minDist:
minDist = distJI
minIndex = j # 如果第i个数据点到第j个中心点更近,则将i归属为j
if clusterAssment[i, 0] != minIndex:
clusterChanged = True # 如果分配发生变化,则需要继续迭代
clusterAssment[i, :] = minIndex, minDist # 并将第i个数据点的分配情况存入字典
for i in range(m):
if clusterAssment[i, 0] == 0:
plt.plot(dataSet[i, 0], dataSet[i, 1], 'ro', c='#00FF00')
if clusterAssment[i, 0] == 1:
plt.plot(dataSet[i, 0], dataSet[i, 1], 'ro', c='#0066FF')
if clusterAssment[i, 0] == 2:
plt.plot(dataSet[i, 0], dataSet[i, 1], 'ro', c='#FF0000')
if clusterAssment[i, 0] == 3:
plt.plot(dataSet[i, 0], dataSet[i, 1], 'ro', c='#FFCC00')
plt.plot(centroids[:, 0], centroids[:, 1], 'ro', c='#000000')
plt.xlim((-6, 6))
plt.ylim((-6, 6))
plt.show()
for cent in range(k): # 重新计算中心点
ptsInClust = dataSet[nonzero(clusterAssment[:, 0].A == cent)[0]] # 去第一列等于cent的所有列
centroids[cent, :] = mean(ptsInClust, axis=0) # 算出这些数据的中心点
return centroids, clusterAssment
if __name__ == '__main__':
datMat = mat(loadDataSet('testSet.txt'))
myCentroids, clustAssing = kMeans(datMat, 4)
print(myCentroids)
print(clustAssing)
运行结果
经过4次迭代,便得到分组。