k-modes聚类算法(Python实现)
k-means 算法是一种简单且实用的聚类算法,但是传统的 k-means 算法只适用于连续属性的数据集,而对于离散属性的数据集,计算簇的均值以及点之间的欧氏距离就变得不合适了。
k-modes 作为 k-means 的一种扩展,适用于离散属性的数据集。
假设有 N 个样本,M 个属性且全是离散的,簇的个数为 k,其实现步骤为:
【实例】利用 k-modes 算法对给定的数据进行聚类。
k-modes 作为 k-means 的一种扩展,适用于离散属性的数据集。
假设有 N 个样本,M 个属性且全是离散的,簇的个数为 k,其实现步骤为:
- 随机确定 k 个聚类中心 C1,C2,…,Ck 是长度为 M 的向量,Ci=[C1i,C2i,…,CMi]。
- 对于样本 xj(j=1,2,…,N),分别比较其与 k 个中心之间的距离(此处的距离为不同属性值的个数,假如 x1=[1,2,1,3],C1=[1,2,3,4],那么 x1 与 C1 之间的距离为 2)。
- 将 xj 划分到距离最小的簇,在全部的样本都被划分完毕后,重新确定簇中心,向量 Ci 中的每一个分量都更新为簇 i 中的众数。
- 重复步骤 2 和 3,直到总距离(各个簇中样本与各自簇中心距离之和)不再降低,返回最后的聚类结果。
【实例】利用 k-modes 算法对给定的数据进行聚类。
import numpy as np if __name__ == '__main__': # 导入数据 datas = np.array([[1,2,3,4], [1,6,8,8], [1,2,3,3], [2,4,5,5], [4,7,8,7], [7,6,8,9], [4,4,3,3], [2,2,5,5], [7,5,5,5], [5,6,8,9]]) # 选取聚类中心 centers = np.array([datas[1],datas[6],datas[2]]) # 初始化各类中到中心的距离总和,但是由于距离总和不减少时,停止聚类,所以设置一个尽量大的数,以免影响过程 distance1 = 1000000 distance2 = 1000000 distance3 = 1000000 # 记录聚类次数 n = 0 # 开始聚类循环 while(True): n+=1 # 使用 numpy 的 array 作为两个类的容器 cluster1 = np.array([centers[0]]) cluster2 = np.array([centers[1]]) cluster3 = np.array([centers[2]]) # 使用 list 存放聚类中每个点到聚类中心的汉明距离 hamming1 = [] hamming2 = [] hamming3 = [] # 遍历所有数据 for i in datas: # 用于记录汉明距离 n1=0 n2=0 n3=0 # 循环每个位置,但有位置与聚类中心有相同属性时,n1/n2 加 1 for j in range(4): if(i[j]!=centers[0][j]): n1+=1 if(i[j]!=centers[1][j]): n2+=1 if(i[j]!=centers[2][j]): n3+=1 # 将每个汉明距离存储到 list hamming1.append(n1) hamming2.append(n2) hamming3.append(n3) # 将每个数据添加到其对应的类里面 if(n1==0 and n2==0 and n3!=0): cluster1 = np.vstack([cluster1,i]) if(n1<n2 and n2<n3): cluster2 = np.vstack([cluster2,i]) if(n3<n2 and n3<n1): cluster3 = np.vstack([cluster3,i]) # 将两个类别转置,方便求每个属性的众数。因为 NumPy 中没有直接求每一列众数的函数,所以这样操作后使用 list 的求众数操作 cluster1_t = np.transpose(cluster1) cluster2_t = np.transpose(cluster2) cluster3_t = np.transpose(cluster3) list1=[] list2=[] list3=[] # 将每一行的众数分别存储到两个列表中,作为下一次聚类的新中心。因为转置了,所以现在一行的众数,就是之前列的众数 for i in range(4): counts1 = np.bincount(cluster1_t[i]) list1.append(np.argmax(counts1)) counts2 = np.bincount(cluster2_t[i]) list2.append(np.argmax(counts2)) counts3 = np.bincount(cluster3_t[i]) list3.append(np.argmax(counts3)) # print(f"第{n}次聚类 新中心{list1}") # 将新中心作为下一次聚类的中心 centers = np.array([list1,list2,list3]) # 判断汉明距离和上一次聚类的差别,如果总的汉明距离没有减小,那么聚类结束 if(sum(hamming1)>=distance1 and sum(hamming2) >= distance2 and sum(hamming3)>=distance3): print(f"聚类{n}次后结束") break; else: distance1 = sum(hamming1) distance2 = sum(hamming2) distance3 = sum(hamming3) print(f"第一类\n{cluster1}") print(f"中心 {centers[0]}") print(f"第二类\n{cluster2}") print(f"中心 {centers[1]}") print(f"第三类\n{cluster3}") print(f"中心 {centers[2]}")运行程序,输出如下:
聚类3次后结束
第一类
[[1 6 8 9]
[1 6 8 8]
[4 7 8 7]
[7 6 8 9]
[5 6 8 9]]
中心[1 6 8 9]
第二类
[[2 4 3 3]
[2 4 5 5]
[4 4 3 3]]
中心[2 4 3 3]
第三类
[[1 2 3 3]
[1 2 3 4]]
中心[1 2 3 3]