# kmeans 算法
import numpy as np
import matplotlib.pyplot as plt
def my_kmeans(input_data, k=3, show_flag=False):
# input_data = [[1, 5], [2, 4], [4, 1], [5, 0], [7, 6], [6, 7]]
input_data_np = np.array(input_data)
x, y = input_data_np[:,0], input_data_np[:,1]
index_cls = []
while index_cls.__len__() < k:
n = np.random.randint(0, input_data_np.shape[0], 1)
if not n in index_cls:
index_cls.append(n[0])
point_cls = input_data_np[np.array(index_cls)]
num = 1
while True:
# 2. 更新样本点类别
# 通过计算样本点到聚类中的使用欧式距离的平方(x1-x2)^2 + (y1-y2)^2
dis_cls = np.array([list(np.sum((input_data_np - i) ** 2, axis=1)) for i in point_cls])
# 3. 计算样本点新类别
# 选取每一列上的最小值的索引,作为这个样本点的类别
new_tag_samples = np.argmin(dis_cls, axis=0)
# 添加显示的部分
if show_flag:
plt.figure(num)
num += 1
for i in range(k):
plt.scatter(x[np.array(new_tag_samples) == i], y[np.array(new_tag_samples) == i])
# 4. 计算新类中心
# 计算新的类别中心
new_point_cls = np.array([list(np.average(input_data_np[new_tag_samples == i], axis=0)) for i in range(k)])
# 5. 计算新类中心的样本类别,做判断,如果类别发生了变化,计算更新类别,如果不变化了,那么就停止
# 可以比较两个类的中心是否满足一定条件,(一般算和小与一定的值,不建议使用相等判断)
sum_cls_point_dis = np.sum((new_point_cls - point_cls) ** 2)
# 可以比较样本的类别变化,如果样本类别没有变化了,那就停止
if sum_cls_point_dis < 0.05:
break
else:
point_cls = new_point_cls
if show_flag:
plt.show()
return new_point_cls,new_tag_samples
def my_kmeanspp(input_data, k=3, show_flag=False):
# input_data = [[1, 5], [2, 4], [4, 1], [5, 0], [7, 6], [6, 7]]
input_data_np = np.array(input_data)
x, y = input_data_np[:,0], input_data_np[:,1]
# 改成选择相距最远的点作为中心点
point_cls = []
# print(np.random.randint(0, input_data_np.shape[0], 1))
tmp = input_data_np[np.random.randint(0, input_data_np.shape[0], 1)[0],:]
# print(tmp)
point_cls.append(list(tmp))
# print(point_cls)
# 计算第二个点,就是获取离类中心点最远的点
while point_cls.__len__()<k:
samples_dis_cls = np.array([list(np.sum((input_data_np - i) ** 2, axis=1)) for i in point_cls])
samples_dis_cls_min = np.min(samples_dis_cls,axis=0)
index_max_dis_sample = np.argmax(samples_dis_cls_min)
point_cls.append(list(input_data_np[index_max_dis_sample]))
print(point_cls)
point_cls = np.array(point_cls)
num = 1
while True:
# 2. 更新样本点类别
# 通过计算样本点到聚类中的使用欧式距离的平方(x1-x2)^2 + (y1-y2)^2
dis_cls = np.array([list(np.sum((input_data_np - i) ** 2, axis=1)) for i in point_cls])
# 3. 计算样本点新类别
# 选取每一列上的最小值的索引,作为这个样本点的类别
new_tag_samples = np.argmin(dis_cls, axis=0)
# 添加显示的部分
if show_flag:
plt.figure(num)
num += 1
for i in range(k):
plt.scatter(x[np.array(new_tag_samples) == i], y[np.array(new_tag_samples) == i])
# 4. 计算新类中心
# 计算新的类别中心
new_point_cls = np.array([list(np.average(input_data_np[new_tag_samples == i], axis=0)) for i in range(k)])
# 5. 计算新类中心的样本类别,做判断,如果类别发生了变化,计算更新类别,如果不变化了,那么就停止
# 可以比较两个类的中心是否满足一定条件,(一般算和小与一定的值,不建议使用相等判断)
sum_cls_point_dis = np.sum((new_point_cls - point_cls) ** 2)
# 可以比较样本的类别变化,如果样本类别没有变化了,那就停止
if sum_cls_point_dis < 0.05:
break
else:
point_cls = new_point_cls
if show_flag:
plt.show()
return new_point_cls,new_tag_samples
# 测试封装好的函数
input_data = [[1, 5], [2, 4], [4, 1], [5, 0], [7, 6], [6, 7]]
# 使用散点图显示出来
# x = np.array(input_data)[:,0] # 二维数组的第1列
# y = np.array(input_data)[:,1] # 二维数组的第2列
# plt.scatter(x,y)
# plt.scatter(x-1,y-1)
# plt.show()
cls_point, tag_samples = my_kmeanspp(input_data,3)
print(input_data)
print(cls_point)
print(tag_samples)