K-means 算法试图将数据集中的样本划分为若干个子集,每个子集称为一个簇,通过该算法使得每个聚类内的数据点尽可能相似(即距离该聚类的中心点最近),而不同聚类之间的数据点尽可能不相似。
算法步骤如下:
-
从样例数据中随机选择
k
个点作为初始质心,k
表示簇的个数。 -
根据质心点循环进行计算分类。当质心点不发生变化时,结束循环,返回最终的质心点。详细计算步骤如下:
- 根据质心计算每个点到质心的欧氏距离。
- 对于每个数据点,寻找距离最近的质点归类。
- 计算每个簇中数据点的平均距离。
- 以该平均值作为新的质点,继续计算。
举个例子,假设计算得到的欧式距离数据如下:
[[1,2,3],[2,3,1],[4,5,6],[7,6,4]....[3,1,2]]
表示有 3 个簇,样本数据的第一个点距离这三个质点的距离分别为 1、2、3,第二个点距离三个质点的距离分别为 2、3、1,后边的数据依次类推。那么会将第一个点分类到簇 1,第二点分类到簇 3,依次类推。
-
根据第 2 步骤得到的质心点,计算获取簇数据。参考 2.1-2.2 步骤。
如下是根据你的需求给出的 Python 示例代码,请在你的环境上提前安装pandas
库和numpy
库。
python
import random
import numpy as np
import pandas as pd
# 计算欧氏距离
def euclidean_distance(dataset, centroids, k):
clalist = []
for data in dataset:
# 平铺数据,计算每个点到质心的距离
diff = np.tile(data, (k, 1)) - centroids
squared_diff = diff ** 2
squared_dist = np.sum(squared_diff, axis=1)
distance = squared_dist ** 0.5
clalist.append(distance)
# 返回一个每个点到质点的距离的数组
clalist = np.array(clalist)
return clalist
# 分类并计算变化量
def classify(dataset, centroids, k):
# 计算单个点到每个质心的的距离
# 数据结构为:[[1,2,3],[2,3,1],[4,5,6],[7,6,4]....[3,1,2]]
# 表示有三个质心,数组中的第一个元素表示样本的第一个点分别到三个质心的距离
clalist = euclidean_distance(dataset, centroids, k)
# 对于每个点,将会分配到距离它最近的质心,这里给出的是分类结果的索引
min_dist_indices = np.argmin(clalist, axis=1)
# 按照 min_dist_indices 进行统计分类,对分类结果求均值
new_centro_ids = pd.DataFrame(dataset).groupby(min_dist_indices).mean()
new_centro_ids = new_centro_ids.values
# 计算变化量
changed = new_centro_ids - centroids
return changed, new_centro_ids
# k-means 算法
def kmeans(dataset, k):
# 随机取质心
centroids = random.sample(dataset, k)
# 更新质心,直到变化量全为 0
changed, new_centro_ids = classify(dataset, centroids, k)
while np.any(changed != 0):
changed, new_centro_ids = classify(dataset, new_centro_ids, k)
centroids = sorted(new_centro_ids.tolist())
# 根据质心计算每个集群
cluster = []
clalist = euclidean_distance(dataset, centroids, k)
min_dist_indices = np.argmin(clalist, axis=1)
for _ in range(k):
cluster.append([])
for data_idx, cluster_idx in enumerate(min_dist_indices):
cluster[cluster_idx].append(dataset[data_idx])
return centroids, cluster
# 创建数据集
dataset = [[1, 1], [1, 2], [2, 1], [6, 4], [6, 3], [5, 4]]
# k-means 算法
centroids, cluster = kmeans(dataset, 2)
print('质心为:{}'.format(centroids))
print('集群为:{}'.format(cluster))
上述代码中,定义了一个主函数kmeans
和两个辅助函数classify
和euclidean_distance
,创建了一个数据集dataset
,主函数接受数据集dataset
和聚类的类别数k
作为输入,然后调用两个辅助函数实现聚类计算的功能。
需要注意的是,K-means 算法虽然有效,但是容易受到初始簇质心的情况而影响,有可能陷入局部最优解。