试编程实现k均值聚类算法,设置三组不同的初始中心点,在西瓜数据集4.0上进行实验比较
代码全是《机器学习》上的,只是将其整合到了一起,能够运行手写体识别。
内容大部分进行了注释,可能有些注释不够精准或者不容理解,见谅!
代码:
import numpy as np #用来调整数组和矩阵
import matplotlib as mpl #用来设置字体和正负号
import matplotlib.pyplot as plt #画图框
import warnings #用来设置警告
dataMat= [[0.697, 0.460],[0.774, 0.376],[0.634, 0.264],[0.608, 0.318],\
[0.556, 0.215],[0.430, 0.237],[0.481, 0.149],[0.437, 0.211],\
[0.666, 0.091],[0.243, 0.267],[0.245, 0.057],[0.343, 0.099],\
[0.639, 0.161],[0.657, 0.198],[0.360, 0.370],[0.593, 0.042],\
[0.719, 0.103],[0.359, 0.188],[0.339, 0.241],[0.282, 0.257],\
[0.748, 0.232],[0.714, 0.346],[0.483, 0.312],[0.478, 0.437],\
[0.525, 0.369],[0.751, 0.489],[0.532, 0.472],[0.473, 0.376],\
[0.725, 0.445],[0.446, 0.459]] #输入数据
def distEclud(vecA, vecB): #计算距离
return np.sqrt(np.sum(np.power(vecA - vecB, 2)))
def randCent(dataSet, k): #构建质心
warnings.simplefilter("ignore") #将警告忽略掉
n = np.shape(dataSet)[1] #记录矩阵的宽度
centroids = np.mat(np.zeros((k, n))) #生成一个矩阵用来存放质心
for j in range(n):
minJ = min (dataSet[:,j])
rangeJ = float(max(dataSet[:, j]) - minJ) #确定随机点再数据集内
centroids[:, j] = minJ + rangeJ * np.random.rand(k, 1) # 构建簇质心
return centroids
def kMeans(dataSet, k, distMeas=distEclud, createCent=randCent):
m = np.shape(dataSet)[0] # 矩阵行数
clusterAssment = np.mat(np.zeros((m, 2))) #记录簇索引和误差
centroids = createCent(dataSet, k) #质心
clusterChanged = True
while clusterChanged:
clusterChanged = False
for i in range(m):
minDist = np.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):
ptsInClust = dataSet[np.nonzero(clusterAssment[:, 0].A == cent)[0]]]#获取簇分配结果矩阵中属于当前cent簇的所有索引列表,并对应到数据集中的点,全部取出放入新的列表
centroids[cent, :] = np.mean(ptsInClust, axis=0) #对得到的列表按列取均值,作为该簇的最新的质心放入簇质心列表
return centroids, clusterAssment
def testKmeans(k):
global dataMat
dataMat = np.mat(dataMat)
myCentroids, clustAssing = kMeans(dataMat, k)
print ('myCentroids:\n', myCentroids)
mpl.rcParams['font.sans-serif'] = [u'SimHei'] # 指定显示字体
mpl.rcParams['axes.unicode_minus'] = False # 解决保存图像中负号'-'显示为方块的问题
plt.figure(1, facecolor='white') # 创建一个新图形, 背景色设置为白色
plt.scatter(np.array(myCentroids[:, 0]), np.array(myCentroids[:, 1]), marker='+', alpha=1, s=150) #绘制质心
for cent in range(k): #开始绘制西瓜数据的点
xing=['o','v','^','1','2','3','*','s','+','D','x'] #设置形状
ptsInClust = dataMat[np.nonzero(clustAssing[:, 0].A == cent)[0]]
plt.scatter(np.array(ptsInClust[:, 0]), np.array(ptsInClust[:, 1]), marker=xing[cent], alpha=1) #设置画点时的形状
plt.show()
if __name__=='__main__':
k_numy=int(input("请输入质心数:"))
testKmeans(k_numy) #画图
运行结果
质心为6:
质心为5:
质心为3: