Kmeas原理
1、从数据集D中随机取k个元素,作为k个簇的各自的中心(质心)。
2、分别计算剩下的元素到k个簇中心的相似度,并将其归属为和其做相似的簇。
3、根据聚类结果,重新计算k个簇各自的中心,计算方法是取簇中所有元素各自维度的算术平均数。
4、重复2-3,直到满足停止条件。
停止条件通常为:聚类结果几乎不再发生变化,或者达到一定的迭代次数。
import random
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 计算欧拉距离
def calcDis(dataSet, centroids, k):
clalist = []
for data in dataSet:
diff = np.tile(data, (k, 1)) - centroids
# 相减 (np.tile(a,(2,1))就是把a先沿x轴复制1倍,即没有复制,仍然是 [0,1,2]。 再把结果沿y方向复制2倍得到array([[0,1,2],[0,1,2]]))
squaredDiff = diff ** 2 # 平方
squaredDist = np.sum(squaredDiff, axis=1) # 和 (axis=1表示行)
distance = squaredDist ** 0.5 # 开根号
clalist.append(distance)
clalist = np.array(clalist) # 返回一个每个点到质点的距离len(dateSet)*k的数组
return clalist
# 计算质心
def classify(dataSet, centroids, k):
# 计算样本到质心的距离
clalist = calcDis(dataSet, centroids, k)
# 分组并计算新的质心
minDistIndices = np.argmin(clalist, axis=1) # axis=1 表示求出每行的最小值的下标
print(minDistIndices)
newCentroids = pd.DataFrame(dataSet).groupby(
minDistIndices).mean() # DataFramte(dataSet)对DataSet分组,groupby(min)按照min进行统计分类,mean()对分类结果求均值
newCentroids = newCentroids.values
# 计算变化量
changed = newCentroids - centroids
return changed, newCentroids
# 使用k-means分类
def kmeans(dataSet, k):
# 随机取质心
centroids = random.sample(dataSet, k)
# 更新质心 直到变化量全为0
changed, newCentroids = classify(dataSet, centroids, k)
while np.any(changed != 0):
changed, newCentroids = classify(dataSet, newCentroids, k)
centroids = sorted(newCentroids.tolist()) # tolist()将矩阵转换成列表 sorted()排序
# 根据质心计算每个集群
cluster = []
clalist = calcDis(dataSet, centroids, k) # 调用欧拉距离
minDistIndices = np.argmin(clalist, axis=1)
for i in range(k):
cluster.append([])
for i, j in enumerate(minDistIndices): # enymerate()可同时遍历索引和遍历元素
cluster[j].append(dataSet[i])
return centroids, cluster
# 创建数据集
def createDataSet():
return [[1, 1], [1, 2], [2, 1], [6, 4], [6, 3], [5, 4],[1,5],[6,4],[3,5]]
import torch
if __name__ == '__main__':
featuress= createDataSet()
centroids, cluster = kmeans(featuress, 3)
print('质心为:%s' % centroids)
print('集群为:%s' % cluster)
for i in range(len(featuress)):
plt.scatter(featuress[i][0], featuress[i][1], marker='o', color='green', s=40, label='原始点')
# 记号形状 颜色 点的大小 设置标签
for j in range(len(centroids)):
plt.scatter(centroids[j][0], centroids[j][1], marker='x', color='red', s=50, label='质心')
plt.show()
流程演示图
定义一组数据集
# 创建数据集
def createDataSet():
return [[1, 1], [1, 2], [2, 1], [6, 4], [6, 3], [5, 4],[1,5],[6,4],[3,5]]
以上述代码为例,k=3,即生成3组
因为每组都有1个质心,有3个质心。
首先,在数据集中随机采样3个点,作为质心。centroids是质心数组
centroids = random.sample(dataSet, k)
调用classfiy函数计算最终的质心
然后,不断调用classfiy函数,更新质心。直到质心不再改变
# 更新质心 直到变化量全为0
changed, newCentroids = classify(dataSet, centroids, k)
while np.any(changed != 0):
changed, newCentroids = classify(dataSet, newCentroids, k)
对于任何一次的classfiy函数调用过程中,调用欧拉计算对所有点计算到每个质心的距离,得到clalist数组。
# 计算样本到质心的距离
clalist = calcDis(dataSet, centroids, k)
如下图,9行3列。一行表示一个点,一列表示一个质点,(1,2)表示第1个点到第2个质点的距离。
利用以下代码求得每行的最小值的坐标,即a点到3个质点哪个距离最短。
minDistIndices = np.argmin(clalist, axis=1) # axis=1 表示求出每行的最小值的下标
按照每组分类,得到3个大类。每类中包含距离该质心相对近的点。
将每类的点的横纵坐标取平均值,如以下图
newCentroids = pd.DataFrame(dataSet).groupby(
minDistIndices).mean() # DataFramte(dataSet)对DataSet分组,groupby(min)按照min进行统计分类,mean()对分类结果求均值
行表示3个大类,列表示横纵坐标。此时,3个新质点的坐标是(2.5,3)(5.75,3.75)(1.0,2.6667)
计算changed,看看新旧质心有没有改变。如果改变了,说明旧质心不是最终点,如果没改变,说明旧质心是最终点。同时,可以print可视化一下。
changed = newCentroids - centroids
print('旧质点: %s ' % centroids)
print('新质点:%s ' % newCentroids)
print('changed %s ' %changed)
以上就是这一层的调用classfiy过程
通过质心找到各所属集群
在质心不再改变时,即找到3个最终质心后。将最后一层的classfiy调用时生成的clalist数组按照之前的方法找到所属的质点,即a点到3个质点哪个距离最短。将该点最终加入到所属群中。
minDistIndices = np.argmin(clalist, axis=1)
for i in range(k):
cluster.append([])
for i, j in enumerate(minDistIndices): # enymerate()可同时遍历索引和遍历元素
cluster[j].append(dataSet[i])
以下是最终结果
最终质心为(1.33333,1.333333)、(2,5)(5.75,3.75)
而集群1为(1,1)(1,2) (2,1) 集群2为(1,5)(3,5)集群3为(6,4)(6,3)(5,4)(6,4)
在稍大点数据集的聚类效果
在1500个样本中随机采样出100个点,使用kmeans聚类出3个所属类别