二 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');

 

效果还是非常好

posted @ 2019-04-18 22:44  罗亚雄  阅读(794)  评论(0编辑  收藏  举报