K-means算法
以空间中k个点为中心进行聚类,对最靠近他们的对象归类。通过迭代的方法,逐次更新各聚类中心的值,直至得到最好的聚类结果。
kmeans.py
import random
import pandas as pd
import numpy as np
# 计算欧拉距离
def calcDis(dataSet, centroids, k):
clalist=[]
for data in dataSet:
diff = np.tile(data, (k, 1)) - centroids
squaredDiff = diff ** 2
squaredDist = np.sum(squaredDiff, axis=1)
distance = squaredDist ** 0.5
clalist.append(distance)
clalist = np.array(clalist)
return clalist
# 计算质心
def classify(dataSet, centroids, k):
# 计算样本到质心的距离
clalist = calcDis(dataSet, centroids, k)
# 分组并计算新的质心
minDistIndices = np.argmin(clalist, axis=1)
newCentroids = pd.DataFrame(dataSet).groupby(minDistIndices).mean()
newCentroids = newCentroids.values
#assert k == len(newCentroids)
# 计算变化量
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())
# 根据质心计算每个集群
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):
cluster[j].append(dataSet[i])
return centroids, cluster
def main():
# 创建数据集
def createDataSet():
return [[1, 1], [1, 2], [2, 1], [6, 4], [6, 3], [5, 4]]
dataset = createDataSet()
centroids, cluster = kmeans(dataset, 3)
print('质心为:%s' % centroids)
print('集群为:%s' % cluster)
if __name__=='__main__':
exit(main())
输出结果:
质心为:[[1.3333333333333333, 1.3333333333333333], [5.0, 4.0], [6.0, 3.5]]
集群为:[[[1, 1], [1, 2], [2, 1]], [[5, 4]], [[6, 4], [6, 3]]]
PS:
上面代码有个小问题:在迭代更新的过程中如果有的质心没有一个点离它最近,会有问题。
查了很多资料也不知道还怎样处理,看其他实现的代码都没注意这个问题,有知道怎么解决的希望能告诉我。
这里给个这样的例子:
数据集:[[1, 1], [1, 2], [2, 1], [4, 4], [4, 3], [3, 4]]
k = 3 并且正好随机到[1, 1], [1, 2], [2, 1]这三个。
第一次:
[[1, 1]] [[1, 2], [3, 4], [4, 4]] [[2, 1], [4, 3]]
质心分别为:
[1, 1] [2.67, 3.33] [3, 2]
第二次:
[[1, 1], [1, 2], [2, 1]] [[3, 4], [4, 3], [4, 4]] []
质心分别为:
[1.33, 1.33] [3.67, 3.67] [???]
这样有一个变成空的了。