二 k-means聚类算法的手动实现
一、python实现k-means
import numpy as np import pandas as pd import matplotlib as mpl import matplotlib.pyplot as plt %matplotlib inline #为了减少迭代次数,我们可以尽量把质心初始化在数据分布的内部 def randCent(data, k): #定义随机选取质心的函数 data_min = data.iloc[:, :].min() #返回每一个特征的最小值 data_max = data.iloc[:, :].max() #返回每一个特征的最大值 data_cent = np.random.uniform(data_min,data_max,(k, data.shape[1]))#在最大值和最小值之间随机生成K个质心,返回k*data.shape[1]形状 return data_cent #将以上打包成函数,计算一个样本到所有质心的距离并且返回最小距离和对应的索引 def one_sample_datacent_distance(one_sample, data_cent): one_sample_distance = np.sum(np.power((one_sample.values - data_cent), 2), axis=1) #一个样本到所有质心的距离 distance_min_values = one_sample_distance.min() #最小的距离的值 distance_min_index = np.where(one_sample_distance == distance_min_values)#距离最短的的索引 return distance_min_values, distance_min_index def kmeans(data,k ): m, n = data.shape#提取出数据集的行列 data_cent = randCent(data, k) add_3 = np.zeros((m,3)) add_3[:,0] = np.inf add_3[:, 1: 3] = -1 data_add_3 = pd.concat([data, pd.DataFrame(add_3)] , axis=1, ignore_index = True) clusterChanged = True #设定一个循环因子,用来控制循环 while clusterChanged: clusterChanged = False #先更改循环因子,防止陷入死循环 for i in range(m): #对数据集中每一条样本进行循环 dist_min_values, dist_min_index = one_sample_datacent_distance(data_add_3.iloc[i, :n], data_cent) #计算当前样本到k个质心的距离 data_add_3.iloc[i, n] = dist_min_values #将距离的最小值放到容器的第一列(即第n列) #找到最小距离所在位置的索引即为簇的标号,将簇标号放入容器第二列(即第n+1列) data_add_3.iloc[i, n+1] = dist_min_index[0] #判断最后两列是否相等(簇是否还会变化),不相等则进入下一次循环,相等则终止循环 clusterChanged = not (data_add_3.iloc[:, -1] == data_add_3.iloc[:, -2]).all() #重新计算当前的质心 if clusterChanged: cent_df = data_add_3.groupby(n+1).mean() #对每个簇进行求均值 data_cent = cent_df.iloc[:,:n].values #将均值赋值给质心(确定了新质心) data_add_3.iloc[:, -1] = data_add_3.iloc[:, -2] #当前簇标号赋值给最后一列 return data_cent, data_add_3
效果如下:
from sklearn.datasets import make_blobs #导入生成数据集的包 #自己创建数据集 X, y = make_blobs(n_samples=500,n_features=2,centers=4,random_state=1) plt.scatter(X[:, 0], X[:, 1] ,marker='o' #点的形状 ,s=8 #点的大小 );
数据集的分布图:
调用自己写的k-means进行聚类:
test_cent, test_cluster = kmeans(pd.DataFrame(X), 4) #plt.scatter(testSet.iloc[:,0],testSet.iloc[:,1]) plt.scatter(test_cluster.iloc[:,0], test_cluster.iloc[:, 1], c=test_cluster.iloc[:, -1]) plt.scatter(test_cent[:,0],test_cent[:,1],c='r');
效果还是非常好