K-means算法原理、代码实现,优缺点及改进

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013317445/article/details/87931606

k-Means是一种无监督的聚类算法,实现起来比较简单,聚类效果也不错,因此被广泛应用。

原理

物以类聚,人以群分。
无监督 聚类
简单地说,就是把相似的物体聚到一个簇。同一簇内相似度尽可能大,不同簇间相似度尽可能低。采用距离度量相似程度

算法

1、初始化k个中心点,有了k个簇
2、对所有样本,计算每个样本与k个中心点的距离,将各样本划分到距离最近的中心点所在的簇
3、重新计算各簇的中心:为各簇所有点的均值
4、不断迭代2、3,直到各簇不再发生变化或者达到迭代次数

优缺点

优点:
是解决聚类问题的一种经典算法,简单、快速;
对处理大数据集,该算法高效率;
当结果是密集的,它的效果较好。
缺点:
k值选取不好把握;
对初值敏感(初始聚类中心的选择:改进1:k-means++,改进2:二分k-means);
对噪声和异常点敏感(改进:离群点检测,去掉离群点后再聚类,减少它们对聚类效果的影响);
只能收敛到局部最小,不适合于发现非凸形状的簇还有,不能处理大小、密度差别很大的簇(改进:基于密度的聚类:擅于解决不规则形状的聚类问题,能克服基于距离的算法只能发现“类圆形”的聚类的缺点。如,DBSCAN算法)

实现

import numpy as np

#文件解析,将值封装到矩阵(实质是list里每个元素list)里
#dataset文件每行数字以制表符分
def loadDataset(filename):
    data = []
    f = open(filename)
    for line in f:
        line = line.strip().split('\t')
        floatline = map(float, line)
        data.append(floatline)
    return data


#欧式距离公式
def distance(vecA, vecB):
    return np.sqrt(np.sum(np.square(vecA - vecB)))


#构建质心
#随机选择k个样本点做质心
def randCenter(dataMat, k):
    row, col = dataMat.shape
    centers = []
    centersIndex = np.random.randint(0, row, size=k)
    for i in centersIndex:
        centers.append(dataMat[i])
    centers = np.mat(centers)
    return centers


#输入:样本点集合(矩阵形式,m个样本),聚类簇数k

# 算法:
# 随机选择k个样本作为初始质心:{u1,u2,..uk},即均值向量集合
# 1、对每一个点:
#         计算它与每一个簇的质心的距离;
#         找出最小距离;
#         划分到最小距离所在簇
#     (所有点都被划分了一遍)
# 2、对每一个簇:
#         重新计算质心(均值向量)
#         如果质心改变了,则更新质心;否则,不用更新
# 直到当前的所有簇的质心均未改变,算法结束【换个思路:即是如果一有改变就继续循环1、2】
# 返回:质心、簇划分

def kmeans(dataMat, k):
    m = dataMat.shape[0]  #也就是row,m个样本点
    centers= randCenter(dataMat, k)

    #簇划分 初始时为:{0:u1, 1:u2,..., k:uk}
    clusters = {}
    for cent in range(k):
        clusters[cent]= centers[cent]

    clusterChanged = True
    while clusterChanged:
        clusterChanged= False
        for i in range(m):
            minDist= distance(dataMat[i], centers[0])
            minIndex= 0
            for j in range(k):
                distIJ= distance(dataMat[i], centers[j])
                if distIJ< minDist:
                    minDist= distIJ
                    minIndex= j
            #将样本点划分到最近簇
            clusters[minIndex]= np.row_stack((clusters[minIndex], dataMat[i])) #minIndex键下的值——矩阵增一行

        #一轮划分完后(即所有样本划分一遍后),计算质心
        for cent in range(k):
            newCenter= np.mat([clusters[cent][:,0].mean(), clusters[cent][:,1].mean()])
            if newCenter[cent, 0]!= centers[cent, 0] or newCenter[cent, 1]!= centers[cent, 1]:
                clusterChanged= True
                #更新质心
                centers[cent]= newCenter 
           
        
    return centers, clusters

猜你喜欢

转载自blog.csdn.net/u013317445/article/details/87931606