K 均值聚类算法 K-means Clustering Algorithm
k-means算法又名k均值算法
。K-means算法中的k表示的是聚类为k个簇,means代表取每一 个聚类中数据值的均值作为该簇的中心,或者称为质心,即用每-一个的类的质心对该簇进行描述。
其算法思想大致为:先从样本集中随机选取k个样本作为簇中心,并计算所有样本与这k个“簇中心”的距离,对于每一个样本, 将其划分到与其距离最近的“簇中心”所在的簇中,对于新的簇计算各个簇的新的“簇中心”。
根据以上描述,我们大致可以猜测到实现kmeans算法的主要四点:
(1) 簇个数k的选择
(2) 各个样本点到“簇中心的距离
(3) 根据新划分的簇,更新"簇中心”
(4) 重复上述2、 3过程,直至"簇中心"没有移动
优缺点:
优点: 容易实现
缺点: 可能收敛到局部最小值, 在大规模数据上收敛较慢
avg(x)
和avg(y)
, 那作为新的簇心;dataset
是一个listdef createDataSet():
return [[1, 1], [1, 2], [2, 1], [6, 4], [6, 3], [5, 4]]
if __name__ == '__main__':
dataset = createDataSet() # type(dataset)='list'
# k-means 算法实现
def kmeans(dataSet, k):
# (1) 随机选定k个质心
centroids = random.sample(dataSet, k)
print("输出两个质心", centroids)
# (2) 计算样本值到质心之间的距离,直到质心的位置不再改变
changed, newCentroids = Update_cen(dataSet, centroids, k)
while np.any(changed):
changed, newCentroids = Update_cen(dataSet, centroids, k)
centroids = sorted(newCentroids.tolist())
# (3) 根据最终的质心,计算每个集群的样本
cluster = []
dis = Distance(dataSet, centroids, k) # 调用欧拉距离
minIndex = np.argmin(dis, axis=1)
for i in range(k):
cluster.append([])
for i, j in enumerate(minIndex): # enumerate()可同时遍历索引和遍历元素
cluster[j].append(dataSet[i])
return centroids, cluster
表示从指定序列sequence
中,随机获取k
个数据元素,随机排列,并以列表的形式返回。
*注sample不会修改原有序列
dataset = [[1, 1], [1, 2], [2, 1], [6, 4], [6, 3], [5, 4]]
data = random.sample(dataset, k=4)
print(data)
[[1, 2], [6, 4], [5, 4], [2, 1]]
True
,否则返回Flase
True
,输出为True
。data
在指定axis维度上最小值对应的位置索引(索引从0开始)。(若有多个相同的最小值,则返回第一个)data
可以是list类型,可以是array类型a=np.array([[1, 5, 5, 2],
[9, 6, 2, 8],
[3, 7, 9, 1]])
min_a0 = np.argmin(a, axis=0)
min_a0
array([0, 0, 1, 2], dtype=int64)
min_a1 = np.argmin(a, axis=1)
min_a1
array([0, 2, 3], dtype=int64)
newCentroids = pd.DataFrame(dataSet).groupby(minIndex).mean()
# 计算欧式距离
def Distance(dataSet, centroids, k) -> np.array():
dis = []
for data in dataSet:
diff = np.tile(data, (k, 1)) - centroids # 行数上复制k份,方便作差
temp1 = diff ** 2
temp2 = np.sum(temp1, axis=1) # 按行相加
dis_temp = temp2 ** 0.5
dis.append(dis_temp)
dis = np.array(dis) # 转换为一个array类型
return dis
x:扩展行数
,y:扩展列数
import numpy as np
arr1 = np.array([4, 2])
arr1
array([4, 2])
np.tile(arr1, (6, 1))
array([[4, 2],
[4, 2],
[4, 2],
[4, 2],
[4, 2],
[4, 2]])
np.tile(arr1, (6, 2))
array([[4, 2, 4, 2],
[4, 2, 4, 2],
[4, 2, 4, 2],
[4, 2, 4, 2],
[4, 2, 4, 2],
[4, 2, 4, 2]])
arr1
array([4, 2])
k=2
个,所以说需要np.tile()
函数复制k份,然后就可以一次性把每一个样本点到这k=2
个质心的欧式距离都算出来了。【k是用户指定值】import numpy as np
data = np.arange(6).reshape(2, 3)
data
array([[0, 1, 2],
[3, 4, 5]])
np.sum(data)
15
np.sum(data, axis=0)
array([3, 5, 7])
np.sum(data, axis=1)
array([ 3, 12])
import random
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# 计算欧式距离
def Distance(dataSet, centroids, k) -> np.array:
dis = []
for data in dataSet:
diff = np.tile(data, (k, 1)) - centroids # 行数上复制k份,方便作差
temp1 = diff ** 2
temp2 = np.sum(temp1, axis=1) # 按行相加
dis_temp = temp2 ** 0.5
dis.append(dis_temp)
dis = np.array(dis) # 转换为一个array类型
return dis
# 更新质心
def Update_cen(dataSet, centroids, k):
# 计算每个样本到质心的距离,返回值是array数组
distance = Distance(dataSet, centroids, k)
# print("输出所有样本到质心的距离:", distance)
# 分组并计算新的质心
minIndex = np.argmin(distance, axis=1) # axis=1 返回每行最小值的索引
# print("输出最小值索引", minIndex)
newCentroids = pd.DataFrame(dataSet).groupby(minIndex).mean()
# print("新的质心(dataframe):", newCentroids)
newCentroids = newCentroids.values
# print("新的质心(值):", newCentroids)
# 计算变化量
changed = newCentroids - centroids
return changed, newCentroids
# k-means 算法实现
def kmeans(dataSet, k):
# (1) 随机选定k个质心
centroids = random.sample(dataSet, k)
print("随机选定两个质心:", centroids)
# (2) 计算样本值到质心之间的距离,直到质心的位置不再改变
changed, newCentroids = Update_cen(dataSet, centroids, k)
while np.any(changed):
changed, newCentroids = Update_cen(dataSet, newCentroids, k)
centroids = sorted(newCentroids.tolist())
# (3) 根据最终的质心,计算每个集群的样本
cluster = []
dis = Distance(dataSet, centroids, k) # 调用欧拉距离
minIndex = np.argmin(dis, axis=1)
for i in range(k):
cluster.append([])
for i, j in enumerate(minIndex): # enumerate()可同时遍历索引和遍历元素
cluster[j].append(dataSet[i])
return centroids, cluster
# 创建数据集
def createDataSet():
return [[1, 1], [1, 2], [2, 1], [6, 4], [6, 3], [5, 4]]
if __name__ == '__main__':
dataset = createDataSet() # type(dataset)='list'
centroids, cluster = kmeans(dataset, 2) # 2 代表的是分为2类=2个质心
print('质心为:%s' % centroids)
print('集群为:%s' % cluster)
# x = list(np.array(dataset).T[0])
# y = list(np.array(dataset).T[1])
plt.scatter(list(np.array(dataset).T[0]), list(np.array(dataset).T[1]), marker='o', color='green', label="数据集" )
plt.scatter(list(np.array(centroids).T[0]), list(np.array(centroids).T[1]), marker='x', color='red', label="质心")
plt.show()