伪代码
随机创建K个点作为起始质心
当任意一个点的簇分配结果发生变动
对数据集中的每个点
对每个质心
计算质心和数据点之间的距离
把当前数据点分配到最近的簇
对每个簇,计算簇里所有点的均值,以此作为质心
运行代码
from numpy import *
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):
disTance = sqrt(sum(power(vecA-vecB,2)))
return disTance
# 用于初始化聚类中心的函数
def randCent(dataSet,k):
n = shape(dataSet)[1]
centroids = mat(zeros((k,n))) # 每一行都是一个质心的坐标,有n维度
for j in range(n):
minJ = min(dataSet[:,j])
rangeJ = float(max(dataSet[:,j]) - minJ)
centroids[:,j] = minJ + rangeJ * random.rand(k,1)
# 随机构建k个聚类中心,每一个中心的每一个坐标都不会超过已有样本的范围
return centroids
def kMeans(dataSet, k, distMeas = distEclud, createCent = randCent):
# 与之前的类似,后两个参数为函数的引用,当想用另外的方式计算距离或者初始化
# 就改这两个参数即可
m = shape(dataSet)[0]
clusterAssment = mat(zeros((m,2))) # 用来存储样本点所属的类和距离
centroids = createCent(dataSet,k) # 初始化k个类的质心
clusterChanged = True # 用来记录当前循环是否有样本点改变了类别
while clusterChanged:
clusterChanged = False
for i in range(m):
minDist = inf # 初始化
minIndex = -1
for j in range(k):
distIJ = distMeas(centroids[j,:],dataSet[i,:]) # 计算距离
if distIJ < minDist: # 更新距离
minDist = distIJ
minIndex = j
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
if __name__ == '__main__':
datMat=mat(loadDataSet('./8/testSet.txt'))
rand_centroids=randCent(datMat, 2)
print('Random centroids:',rand_centroids)
myCentroids,clustAssing=kMeans(datMat,4)
print('myCentroids:',myCentroids)
原理和上个实验差不多,但是要提取信息有点麻烦,比如txt文本中有一段的文本需要提取出以下内容:
名称:Dolphin II
地址:10860 SW Beaverton-Hillsdale Hwy
城市和州:Beaverton, OR
纬度:45.486502
经度:-122.788346
执行的代码:
scatter_markers表示用多种图形代表一类地点
def cluster_clubs(num_clust=5):
dat_list = []
with open("8/places.txt", 'r') as fr:
for line in fr.readlines():
line_arr = line.split('\t')
dat_list.append([float(line_arr[4]), float(line_arr[3])])
dat_mat = mat(dat_list)
my_centroids, clust_assing = bi_k_means(dat_mat, num_clust, dist_meas=dist_slc)
fig = plt.figure()
rect = [0.1, 0.1, 0.8, 0.8]
scatter_markers = ['s', 'o', '^', '8', 'p', 'd', 'v', 'h', '>', '<']
axprops = dict(xticks=[], yticks=[])
ax0 = fig.add_axes(rect, label='ax0', **axprops)
img_p = plt.imread("8/Portland.png")
ax0.imshow(img_p)
ax1 = fig.add_axes(rect, label='ax1', frameon=False)
for i in range(num_clust):
pts_in_curr_cluster = dat_mat[nonzero(clust_assing[:, 0].A == i)[0], :]
marker_style = scatter_markers[i % len(scatter_markers)]
ax1.scatter(pts_in_curr_cluster[:, 0].flatten().A[0], pts_in_curr_cluster[:, 1].flatten().A[0],
marker=marker_style, s=90)
ax1.scatter(my_centroids[:, 0].flatten().A[0], my_centroids[:, 1].flatten().A[0], marker='+', s=300)
plt.show()
plt.savefig('hh.png')
K-均值聚类算法是无监督学习的一种,随机选择一个点作为一个簇的质心,新加入的质点可能影响质心的选择,重复这个过程直到质心稳定。这个算法感觉比较直观,但是迭代次数很大,并且如果有几个点比较离谱,可能会影响结果的准确性,比如地理聚类实验左上角、右下角的点。