k-means算法原理上可以说蛮简单的,面试上也会经常问到,但一旦面试官问到如何用python写出来,有些同学可能一时半会还不知道咋下手,导致写的磕磕绊绊,影响面试体验。今个我们就来彻底学懂它!
先介绍原理:
先给定样本data和聚类数k;
(1) 初始化。随机选取k个样本点作为初始聚类中心;
(2)对样本进行聚类。计算样本到每个聚类中心的距离,将该样本指派到与其最近的聚类中心的类去。
(3)计算新的聚类中心。对于聚类结果,计算当前类中各个样本的均值作为新的聚类中心。
(4)如果迭代收敛(新旧聚类中心不变)或符合迭代条件,输出并结束;否则,继续回到步(2)。
给出python代码
import numpy as np
import random
import matplotlib.pyplot as plt
def distance(point1, point2): # 计算距离(欧几里得距离)
return np.sqrt(np.sum((point1 - point2) ** 2))
def k_means(data, k, max_iter=500):
centers = {} # 初始聚类中心
# 初始化,选k个样本作为初始聚类中心
# random.sample(): 随机不重复抽取k个值
for idx, i in enumerate(random.sample(range(data.shape[0]), k)):
# idx取值范围[0, k-1],代表第几个聚类中心; i为随机选取的样本作为聚类中心
centers[idx] = data[i]
# 开始迭代
for i in range(max_iter): # 迭代次数
print("开始第{}次迭代".format(i+1))
clusters = {} # 聚类结果,聚类中心的索引idx -> [样本集合]
for i in range(k): # 初始化为空列表
clusters[i] = []
for row in data: # 遍历每个样本
distances = [] # 计算该样本到每个聚类中心的距离 (只会有k个元素)
for c in centers: # 遍历每个聚类中心
# 添加该样本点到聚类中心的距离
distances.append(distance(row, centers[c]))
idx = np.argmin(distances) # 最小距离的索引
clusters[idx].append(row) # 将该样本添加到第idx个聚类中心
pre_centers = centers.copy() # 记录之前的聚类中心点
for c in clusters.keys():
# 重新计算中心点(计算该聚类中心的所有样本的均值)
centers[c] = np.mean(clusters[c], axis=0)
optimize = True
for c in centers:
if distance(pre_centers[c], centers[c]) > 1e-8: # 中心点是否变化
optimize = False
break
if optimize == True:
# 如果新旧聚类中心不变,则迭代停止
break
return centers, clusters
def predict(p_data, centers): # 预测新样本点所在的类
# 计算p_data 到每个聚类中心的距离,然后返回距离最小所在的聚类。
distances = [distance(p_data, centers[c]) for c in centers]
return np.argmin(distances)
测试
随机生成200个样本点,维度为2,聚为3类;
聚类输出
可视化聚类输出
-完结-