摘要:使用Python实现K-Means算法
一、K-Means算法基本流程
1)随机选取K个聚类中心
2)计算每个样本到聚类中心的距离
3)更新样本聚类中心
4)迭代,直到样本聚类中心不再发生变化
二、详细步骤
1.导入训练集数据,并做些许处理,方便之后的使用,因为原始数据是两列,列名分别为AB,所以使用data['A'].values和data['B'].values来获取文件中的具体数值。
# 导入数据集,文件名为testSet.csv
data = pd.read_csv('testSet.csv')
# 将文件中数据转换为数组
f1 = data['A'].values
f2 = data['B'].values
X = np.array(list(zip(f1, f2)))
2.由簇数确定初始质心的个数,然后把随机生成的质心画到图上,数据集的数据用黑色点表示,质心用蓝色“×”表示,如图1所示。
#设置簇数,根据簇数确定生成初始质心的个数
k = 4
#随机获取初始质心并画到示例数据上
C_x = np.random.randint(0, np.max(X)-2, size=k)
C_y = np.random.randint(0, np.max(X)-2, size=k)
C = np.array(list(zip(C_x, C_y)), dtype=np.float32)
plt.scatter(f1, f2, c='black', s=7)
plt.scatter(C_x, C_y, marker='x', s=100, c='b')
图1 生成初始质心的效果
3. 通过计算欧几里得距离,不断对质心进行迭代,直到质心间距离不再发生变化,那么就进行迭代的终止,此时的质心间距离指的是:上次迭代完的质心与本次迭代完的质心之间的距离。倘若迭代结果一直在改变,那么我们就通过设置最大迭代次数来终止迭代,次数根据具体要求可进行改变。下面仅列出前两次循环的结果,作为参考示例。
while tmp <= 15 and newDist.any() != 0:
注:此处判断迭代结果是否一样,主要是比较迭代前后的两次质心位置有没有改变,辅助以计算质心距离和(簇内各点到该簇质心的距离的和) ,但实际上应该是使用质心距离和差异来判断,即判断迭代前后两次的质心距离和是否改变,如果读者有精力,可以自己再琢磨琢磨。
三、详细代码
import numpy as np
import pandas as pd
from copy import deepcopy
from matplotlib import pyplot as plt
# 导入数据集
data = pd.read_csv('testSet.csv')
# 将文件中数据转换为数组
f1 = data['A'].values
f2 = data['B'].values
X = np.array(list(zip(f1, f2)))
# 计算两个坐标点之间的距离,采用欧几里得法
def dist(a, b,ax=1):
c=a-b
dist=np.sum(np.power(c,2),axis=ax)
return np.sqrt(dist)
#设置簇数
k = 4
# 随机获得中心点的X轴坐标
C_x = np.random.randint(0, np.max(X)-2, size=k)
# 随机获得中心点的Y轴坐标
C_y = np.random.randint(0, np.max(X)-2, size=k)
C = np.array(list(zip(C_x, C_y)), dtype=np.float32)
# 将初始化中心点画到输入的样例数据上
plt.scatter(f1, f2, c='black', s=7)
plt.scatter(C_x, C_y, marker='x', s=100, c='b')
# 用于保存中心点更新前的坐标
C_old = np.zeros(C.shape)
print('C:',C)
# 用于保存数据所属中心点
clusters = np.zeros(len(X))
#设置迭代标识位
newDist = dist(C, C_old, 1)
tmp = 1
while tmp <= 15 and newDist.any() != 0:
# 循环计算出每个点对应的最近中心点
for i in range(len(X)):
# 计算出每个点与中心点的距离
distances = dist(X[i], C, 1)
# 记录0 - k-1个点中距离近的点
cluster = np.argmin(distances)
# 记录每个样例点与哪个中心点距离最近
clusters[i] = cluster
# 采用深拷贝将当前的中心点保存下来
C_old = deepcopy(C)
# 从属于中心点放到一个数组中,然后按照列的方向取平均值
for i in range(k):
points = [X[j] for j in range(len(X)) if clusters[j] == i]
C[i] = np.mean(points, axis=0)
colors = ['b', 'g', 'r', 'y', 'c', 'm']
#建立一个fig和axis对象
fig, ax = plt.subplots()
# 不同的子集使用不同的颜色
for i in range(k):
points = np.array([X[j] for j in range(len(X)) if clusters[j] == i])
ax.scatter(points[:, 0], points[:, 1], s=15, c=colors[i])
ax.scatter(C[:, 0], C[:, 1], marker='x', s=100, c='black')
plt.show()
print ('第%d次循环' % tmp)
tmp = tmp + 1
# 迭代标识位,计算新旧质心的的距离
newDist = dist(C, C_old, 1)
print("新旧质心间的距离:",newDist)
print("质心距离和:")
for i in range(k):
p = C[i]
poin = [X[j] for j in range(len(X)) if clusters[j] == i]
e=0
for x in poin:
d = dist(x, p, 0)
e += d
print(e)