1、如何理解K-Means算法?

2、如何寻找K值及初始质心?

3、如何应用K-Means算法处理数据?

ISODATA聚类算法实现_ISODATA聚类算法实现

K-means聚类的算法原理

K-Means是聚类算法中的一种,其中K表示类别数,Means表示均值。顾名思义K-Means是一种通过均值对数据点进行聚类的算法。K-Means算法通过预先设定的K值及每个类别的初始质心对相似的数据点进行划分。并通过划分后的均值迭代优化获得最优的聚类结果。

K值及初始质心


K值是聚类结果中类别的数量。简单的说就是我们希望将数据划分的类别数。K值决定了初始质心的数量。K值为几,就要有几个质心。选择最优K值没有固定的公式或方法,需要人工来指定,建议根据实际的业务需求,或通过层次聚类(Hierarchical Clustering)的方法获得数据的类别数量作为选择K值的参考。这里需要注意的是选择较大的K值可以降低数据的误差,但会增加过拟合的风险。


以下是一组用户的年龄数据,我们将K值定义为2对用户进行聚类。并随机选择16和22作为两个类别的初始质心。


ISODATA聚类算法实现_迭代_02

 

ISODATA聚类算法实现_聚类_03

 


计算距离并划分数据


我们以图的形式展示聚类的过程。在这组年龄数据中,我们选择了16和22作为两个类别的初始质心,并通过计算所有用户的年龄值与初始质心的距离对用户进行第一次分类。

ISODATA聚类算法实现_数据_04

 


计算距离的方法是使用欧式距离。以下是欧式距离的计算公式。距离值越小表示两个用户间年龄的相似度越高。


ISODATA聚类算法实现_ISODATA聚类算法实现_05

 


通过计算,我们获得了每个年龄数据点与两个初始质心的距离。这里我们以黑色实心圆点标记较大的距离值,空心圆点标记较小的距离值。例如第一个数据点15,到第一个初始质心16的距离为1,到第二个初始质心22的距离为7。相比之下15与16的距离更近,距离值为1,并以空心圆点标记。因此15这个年龄数据点被划分在第一个组(16)中。如果年龄数据点到两个初始质心的距离相等,可以划分到任意组中,例如年龄数据点19,到16和22的距离都为3。在这个示例中我们将数据点19划分到第二个组(22)中。

ISODATA聚类算法实现_迭代_06

 


按相似程度(距离)对数据分完组后,分别计算两个分组中数据的均值15.33和36.25,并以这两个均值作为新的质心。在下图中可以看到,蓝色的数字为初始质心,红色的数字为新的质心。目前的质心和新的质心并不是同一个数据点,我们将以新的质心替代初始质心,迭代计算每个数据点到新质心的距离。直到新的质心和原质心相等,算法结束。

ISODATA聚类算法实现_数据_07

 


使用均值作为新质心


将两个分组中数据的均值作为新的质心,并重复之前的方法计算每个年龄数据点到新质心的距离。下面是年龄数据点到两个新质心的距离。以年龄数据点19为例,到新质心15.33的距离为3.67,到另一个新质心36.25的距离为17.25。相比之下数据点19到15.33的距离更近,为3.67。因此被分到第一组(15.33)中。

ISODATA聚类算法实现_聚类_08

 


以年龄数据点到新质心的距离值完成分组后,再次计算两组的均值18.56和45.90,并以均值作为新质心替代原质心。下图中蓝色数字为原质心,红色数字为新质心。在新质心下,年龄数据的分组情况发生了变化,但新质心与原质心没有重合。


ISODATA聚类算法实现_迭代_09

 


重复之前的方法和步骤,计算年龄数据点到新质心的距离。并对比数据点到两个新质心的距离,选择较小的距离值对年龄数据点进行分组。年龄数据点28到18.56的距离为9.44,到45.90的距离为17.90。因此年龄数据点28被分配到第一个18.56的分组中。

ISODATA聚类算法实现_ISODATA聚类算法实现_10

 


再次以年龄数据点到新质心的距离完成分组后,新质心(红色)与原质心(蓝色)仍然没有重合,但与之前相比分组的调整已经很小。我们继续计算新分组的均值19.50和47.89,并将均值作为新质心替代原质心。

ISODATA聚类算法实现_数据_11

 


算法停止条件


开始计算的第一步我们说迭代计算每个数据到新质心的距离,直到新的质心和原质心相等,算法结束。使用上一步分组的均值19.50和47.89作为新质心。并计算年龄数据点到新质心的距离。以下为计算结果。

ISODATA聚类算法实现_聚类_12

 


按照年龄数据点到新质心的距离对数据进行分组,并计算每组的均值作为新质心。这里两组的均值与原质心相等。也就是说新质心与原质心相等,都是19.50和47.89.。算法停止计算。年龄数据点被划分为两类,如下图所示分别为15-28和35-65。

ISODATA聚类算法实现_ISODATA聚类算法实现_13

 

k-means算法伪代码:

******************************************************************************

1.选取k个质心(这一步会影响整个聚类的结果)
2.将任意一个点分配到离质心最近的簇。
3.用每个簇的均值作为质心。
4.重复2和3直到所有点的分配结果不再发生变化,或者误差小于给定误差

********************************************************

 

K-means聚类算法实现(python代码)

Irst.txt数据集:

5.1 3.5 1.4 0.2

4.9 3.0 1.4 0.2

4.7 3.2 1.3 0.2

4.6 3.1 1.5 0.2

5.0 3.6 1.4 0.2

5.4 3.9 1.7 0.4

4.6 3.4 1.4 0.3

5.0 3.4 1.5 0.2

4.4 2.9 1.4 0.2

4.9 3.1 1.5 0.1

5.4 3.7 1.5 0.2

4.8 3.4 1.6 0.2

4.8 3.0 1.4 0.1

4.3 3.0 1.1 0.1

5.8 4.0 1.2 0.2

5.7 4.4 1.5 0.4

5.4 3.9 1.3 0.4

5.1 3.5 1.4 0.3

5.7 3.8 1.7 0.3

5.1 3.8 1.5 0.3

5.4 3.4 1.7 0.2

5.1 3.7 1.5 0.4

4.6 3.6 1.0 0.2

5.1 3.3 1.7 0.5

4.8 3.4 1.9 0.2

5.0 3.0 1.6 0.2

5.0 3.4 1.6 0.4

5.2 3.5 1.5 0.2

5.2 3.4 1.4 0.2

4.7 3.2 1.6 0.2

4.8 3.1 1.6 0.2

5.4 3.4 1.5 0.4

5.2 4.1 1.5 0.1

5.5 4.2 1.4 0.2

4.9 3.1 1.5 0.2

5.0 3.2 1.2 0.2

5.5 3.5 1.3 0.2

4.9 3.6 1.4 0.1

4.4 3.0 1.3 0.2

5.1 3.4 1.5 0.2

5.0 3.5 1.3 0.3

4.5 2.3 1.3 0.3

4.4 3.2 1.3 0.2

5.0 3.5 1.6 0.6

5.1 3.8 1.9 0.4

4.8 3.0 1.4 0.3

5.1 3.8 1.6 0.2

4.6 3.2 1.4 0.2

5.3 3.7 1.5 0.2

5.0 3.3 1.4 0.2

7.0 3.2 4.7 1.4

6.4 3.2 4.5 1.5

6.9 3.1 4.9 1.5

5.5 2.3 4.0 1.3

6.5 2.8 4.6 1.5

5.7 2.8 4.5 1.3

6.3 3.3 4.7 1.6

4.9 2.4 3.3 1.0

6.6 2.9 4.6 1.3

5.2 2.7 3.9 1.4

5.0 2.0 3.5 1.0

5.9 3.0 4.2 1.5

6.0 2.2 4.0 1.0

6.1 2.9 4.7 1.4

5.6 2.9 3.9 1.3

6.7 3.1 4.4 1.4

5.6 3.0 4.5 1.5

5.8 2.7 4.1 1.0

6.2 2.2 4.5 1.5

5.6 2.5 3.9 1.1

5.9 3.2 4.8 1.8

6.1 2.8 4.0 1.3

6.3 2.5 4.9 1.5

6.1 2.8 4.7 1.2

6.4 2.9 4.3 1.3

6.6 3.0 4.4 1.4

6.8 2.8 4.8 1.4

6.7 3.0 5.0 1.7

6.0 2.9 4.5 1.5

5.7 2.6 3.5 1.0

5.5 2.4 3.8 1.1

5.5 2.4 3.7 1.0

5.8 2.7 3.9 1.2

6.0 2.7 5.1 1.6

5.4 3.0 4.5 1.5

6.0 3.4 4.5 1.6

6.7 3.1 4.7 1.5

6.3 2.3 4.4 1.3

5.6 3.0 4.1 1.3

5.5 2.5 5.0 1.3

5.5 2.6 4.4 1.2

6.1 3.0 4.6 1.4

5.8 2.6 4.0 1.2

5.0 2.3 3.3 1.0

5.6 2.7 4.2 1.3

5.7 3.0 4.2 1.2

5.7 2.9 4.2 1.3

6.2 2.9 4.3 1.3

5.1 2.5 3.0 1.1

5.7 2.8 4.1 1.3

6.3 3.3 6.0 2.5

5.8 2.7 5.1 1.9

7.1 3.0 5.9 2.1

6.3 2.9 5.6 1.8

6.5 3.0 5.8 2.2

7.6 3.0 6.6 2.1

4.9 2.5 4.5 1.7

7.3 2.9 6.3 1.8

6.7 2.5 5.8 1.8

7.2 3.6 6.1 2.5

6.5 3.2 5.1 2.0

6.4 2.7 5.3 1.9

6.8 3.0 5.5 2.1

5.7 2.5 5.0 2.0

5.8 2.8 5.1 2.4

6.4 3.2 5.3 2.3

6.5 3.0 5.5 1.8

7.7 3.8 6.7 2.2

7.7 2.6 6.9 2.3

6.0 2.2 5.0 1.5

6.9 3.2 5.7 2.3

5.6 2.8 4.9 2.0

7.7 2.8 6.7 2.0

6.3 2.7 4.9 1.8

6.7 3.3 5.7 2.1

7.2 3.2 6.0 1.8

6.2 2.8 4.8 1.8

6.1 3.0 4.9 1.8

6.4 2.8 5.6 2.1

7.2 3.0 5.8 1.6

7.4 2.8 6.1 1.9

7.9 3.8 6.4 2.0

6.4 2.8 5.6 2.2

6.3 2.8 5.1 1.5

6.1 2.6 5.6 1.4

7.7 3.0 6.1 2.3

6.3 3.4 5.6 2.4

6.4 3.1 5.5 1.8

6.0 3.0 4.8 1.8

6.9 3.1 5.4 2.1

6.7 3.1 5.6 2.4

6.9 3.1 5.1 2.3

5.8 2.7 5.1 1.9

6.8 3.2 5.9 2.3

6.7 3.3 5.7 2.5

6.7 3.0 5.2 2.3

6.3 2.5 5.0 1.9

6.5 3.0 5.2 2.0

6.2 3.4 5.4 2.3

5.9 3.0 5.1 1.8

以下是以数据集Irst.txt进行算法实现:

# coding=utf-8
from numpy import *
# 加载数据
def loadDataSet(fileName):  # 解析文件,按tab分割字段,得到一个浮点数字类型的矩阵
    dataMat = []              # 文件的最后一个字段是类别标签
    fr = open(fileName)
    for line in fr.readlines():
        curLine = line.strip().split(' ')
        fltLine =map(float, curLine)# 将每个元素转成float类型
        dataMat.append(list(fltLine))
    return dataMat

# 计算欧几里得距离
def distEclud(vecA, vecB):
    return sqrt(sum(power(vecA - vecB, 2))) # 求两个向量之间的距离

# 构建聚簇中心,取k个(此例中为4)随机质心
def randCent(dataSet, k):
    n = shape(dataSet)[1]
    centroids = mat(zeros((k,n)))   # 每个质心有n个坐标值,总共要k个质心
    for j in range(n):
        minJ = min(dataSet[:,j])
        maxJ = max(dataSet[:,j])
        rangeJ = float(maxJ - minJ)
        centroids[:,j] = minJ + rangeJ * random.rand(k, 1)
    return centroids

# k-means 聚类算法
def kMeans(dataSet, k, distMeans =distEclud, createCent = randCent):
    m = shape(dataSet)[0]
    clusterAssment = mat(zeros((m,2)))    # 用于存放该样本属于哪类及质心距离
    # clusterAssment第一列存放该数据所属的中心点,第二列是该数据到中心点的距离
    centroids = createCent(dataSet, k)
    clusterChanged = True   # 用来判断聚类是否已经收敛
    while clusterChanged:
        clusterChanged = False;
        for i in range(m):  # 把每一个数据点划分到离它最近的中心点
            minDist = inf; minIndex = -1;
            for j in range(k):
                distJI = distMeans(centroids[j,:], dataSet[i,:])
                if distJI < minDist:
                    minDist = distJI; minIndex = j  # 如果第i个数据点到第j个中心点更近,则将i归属为j
            if clusterAssment[i,0] != minIndex: clusterChanged = True;  # 如果分配发生变化,则需要继续迭代
            clusterAssment[i,:] = minIndex,minDist**2   # 并将第i个数据点的分配情况存入字典
        print(centroids)
        for cent in range(k):   # 重新计算中心点
            ptsInClust = dataSet[nonzero(clusterAssment[:,0].A == cent)[0]]   # 去第一列等于cent的所有列
            centroids[cent,:] = mean(ptsInClust, axis = 0)  # 算出这些数据的中心点
    return centroids, clusterAssment
# --------------------测试----------------------------------------------------
# 用测试数据及测试kmeans算法
datMat = mat(loadDataSet('E:\\Iris.txt'))
myCentroids,clustAssing = kMeans(datMat,4)
print(myCentroids)
print(clustAssing)

部分运行结果:

ISODATA聚类算法实现_迭代_14

      

ISODATA聚类算法实现_聚类_15

聚类分析之K-means算法案例及其Python实现(二)


国家数学建模赛C题问题一求解参照

聚类分析之K-means算法案例及其Python实现(三)


scikit-learn 源码解读之Kmeans

http://midday.me/article/f8d29baa83ae41ec8c9826401eb7685e

Kmeans聚类过程的动态可视化


K的选择:肘部法则


如有不足改正,请多多指教!