聚类算法之k-均值,k-中心点

k-means和k-中心点算法是属于简单的迭代型聚类算法,它将一个给定的数据集分为用户指定的k个聚簇。实现和运行该算法都很简单,它的速度比较快,同时又易于修改,所以在实际应用中使用非常广泛。

K-means算法

k-means算法是硬聚类算法,是典型的基于原型的目标函数聚类算法的代表。它是数据点到原型的某种距离作为相似性的评价指标,即两个对象的距离越接近,其相似度就越大。算法采用误差平方和准侧函数作为聚类准则函数。

算法实现

    输入:

       k:簇的数目

       D:包含n个对象的数据集

   输出:k个簇的集合

   方法:

    (1):从D中任意选择k个对象作为初始簇中心

    (2):repeat

    (3):    根据簇中的对象的均值,将每个对象分配到最相似的簇

    (4):    更新簇均值,即重新计算每个簇中对象的均值

    (5):util不再发生变化

k-means方法是不保证收敛于全局最优解,并且它通常终止于一个局部最优解。结果可能依赖于初始簇中心的随机选取。

k-means算法的优点:

1:是聚类算法中的一种经典,快速,简单的算法

2:对处理大数据集,该算法保持可伸缩性和可扩展性

3:当簇接近高斯分布时,结果较好

k-means算法的缺点:

1:在簇的平均值可被定义的情况下才能使用,可能不适用于某些应用,比如说标量数据

2:在 K-means 算法中 K 是事先给定的,这个 K 值的选定是非常难以估计的。很多时候,事先并不知道给定的数据集应该分成多少个类别才最合适。

3:在 K-means 算法中,首先需要根据初始聚类中心来确定一个初始划分,然后对初始划分进行优化。这个初始聚类中心的选择对聚类结果有较大的影响,一旦初始值选择的不好,可能无法得到有效的聚类结果。

4:该算法需要不断地进行样本分类调整,不断地计算调整后的新的聚类中心,因此当数据量非常大时,算法的时间开销是非常大的。

5:若簇中含有异常点,将导致均值偏离严重(即:对噪声和孤立点数据敏感)。

6:不适用于发现非凸形状的簇或者大小差别很大的簇。

k-means算法的改进

1、很多时候,事先并不知道给定的数据集应该分成多少个类别才最合适。通过类的自动合并和分裂,得到较为合理的类型数目 K,例如 ISODATA 算法。

2、针对上述3,可选用二分K-均值聚类;或者多设置一些不同的初值,对比最后的运算结果,一直到结果趋于稳定结束。

3、针对上述第5点,改成求点的中位数,这种聚类方式即K-Mediods聚类(K中值)

k-means算法的实现

def readfile(filename):
    lines=[line for line in open(filename)]

    #第一行是列标题
    colnames=lines[0].strip().split('\t')[1:]
    rownames=[]
    data=[]
    for line in lines[1:]:
        p=line.strip().split('\t')
        #每一行的第一列是行名
        rownames.append(p[0])
        #剩余部分就是行对应的数据
        onerow = [float(x) for x in p[1:]]
        data.append(onerow)
    return rownames,colnames,data

#计算两行的皮尔逊相似度
def pearson(v1,v2):
    #简单求和
    sum1=sum(v1)
    sum2=sum(v2)

    #求平方和
    sum1Sq=sum([pow(v,2) for v in v1])
    sum2Sq=sum([pow(v,2) for v in v2])

    #求乘积之和
    Psum=sum([v1[i]*v2[i] for i in range(len(v1))])

    #计算r
    num=Psum-(sum1*sum2/len(v1))
    den=sqrt((sum1Sq-pow(sum1,2)/len(v1))*(sum2Sq-pow(sum2,2)/len(v1)))
    if den == 0:
        return 0
    return 1.0-num/den

#每个点表示一行,每个聚类点表示一类。rows数据集,distance距离计算方法,k聚类的数目
def kcluster(rows,distance=pearson,k=4):
    #确定每个点的特征的最小值和最大值--->每一列的最大值和最小值
    ranges = [(min([row[i] for row in rows]), max([row[i] for row in rows])) for i in range(len(rows[0]))]

    #为每一列生成一个随机聚类点
    clusters = [[random.random() * (ranges[i][1] - ranges[i][0]) + ranges[i][0]
                 for i in range(len(rows[0]))] for j in range(k)]

    lastmatches = None
    for t in range(100): #默认迭代一百次
        print("迭代 %d" % t)
        # 生成k个空数组,用于存储k个聚类点包含的成员
        bestmatches = [[] for i in range(k)]

        #在每一行中寻找距离最近的点
        for j in range(len(rows)):
            row = rows[j]
            bestmatchIndex = 0
            for i in range(k):
                d = distance(clusters[i], row)
                if d < distance(clusters[bestmatchIndex], row):
                    bestmatchIndex = i
            bestmatches[bestmatchIndex].append(j) #每个聚类点记录它包含的成员

        #如果结果与上次一样,则整个过程结束
        if bestmatches == lastmatches:
            break
        lastmatches = bestmatches

        #把中心点移到成员的平均位置处
        for i in range(k):
            avgs = [0.0]*len(rows[0])
            if len(bestmatches[i]) > 0:
                for rowid in bestmatches[i]:
                    for m in range(len(rows[rowid])):
                        avgs[m] += rows[rowid][m]
                for j in range(len(avgs)):
                    avgs[j] /= len(bestmatches[i])
                clusters[i] = avgs
    return bestmatches

if __name__ == '__main__':
    blognames,words,data = readfile('blogdata.txt')
    kclust = kcluster(data,k=4)
    print([blognames[r] for r in kclust[0]])

k-中心点算法

k-means算法对离群点敏感,因为这种对象远离大多数数据,因此分配到一个簇时,他们可能严重地扭曲簇的均值。我们可以不采用簇中对象的均值作为参考点,而是挑选实际对象来代表簇。k-中心点使用了一个绝对误差标准。典型的算法为PAM

PAM实现过程

    输入:

     k:结果簇的个数

     D:包含n个对象的数据集合

    输出:k个簇的集合

    方法:

        1:从D中随即选择k个对象作为初始的代表对象或种子

        2:repeat

        3:    将每个剩余的对象分配到最近的代表对象所代表的簇

        4:    随即地算则一个非代表对象O

        5:    计算用O代替代表对象的总代价S

        6:    if S<0,then O代替代表对象,形成新的k个代表对象的集合

        7:util不再发生变化。

PAM这样的典型k-中心点算法在小型数据集上运行良好,但是不能很好地用于大数据集,为了处理大数据集,可以使用一种称为CLARA(Clustering LARge Applications,大型应用聚类)的基于抽样的方法。

PAM(partitioning around medoid,围绕中心点的划分)是具有代表性的k-medoids算法。
它最初随机选择k个对象作为中心点,该算法反复的用非代表对象(非中心点)代替代表对象,试图找出更好的中心点,以改进聚类的质量。 
例子: 
空间有5点{A,B,C,D,E}, 各点之间距离关系如表,根据pam算法进行聚类分析。

样本点 A B C D E
A 0 1 2 2 3
B 1 0 2 4 3
C 2 2 0 1 5
D 2 4 1 0 3
E 3 3 5 3 0

假设分为2类,以A,B为中心点,初始聚类为{A,C,D}和{B,E}。接下来进行交换(以非代表对象代替代表对象),我们需要计算TCac、TCad、TCae、TCbc、TCbd、TCbe。 
TCij表示用非中心点j替换中心点i所产生的代价。 
计算TCAC:当A被C替换后,设一指针p遍历所有对象,判断他们是否聚到别的类里。

1:先看A是否变化:C成为中心点后,A离B比A离C近,故A被划分到B簇里。所产生的代价为d(A,B)-d(A,A)=1(d(i,j)表示i划分到中心点j的距离;差值表示属于新的中心点-属于旧的中心点产生的代价。)
2:看B是否变化:C成为中心点后,B当然离自己是最近的,不变
3:看C是否变化:C成为中心点后,C划分到C簇里,代价为d(C,C)-d(C,A)=-2
4:看D是否变化:C成为中心点后,D离C最近,故划分到C里,代价为d(D,C)-d(D,A)=-1;
5:看E是否变化:C成为中心点后,E离B最近,为0 
    TCac就等于上述的代价之和,为1+0-2-1+0=-2。 
    同理需要计算TCAD=-2、TCAE=-1、TCBC=-2、TCBD=-2、TCBE=-2 
    然后选取代价最小的替换,这里有多个选择,随便选择一个就行。选C的话,新的簇为{C,D}和{A,B,E}。新的簇中心为C,B,继续迭代计算直到收敛。
为了判定一个非代表对象orandom是否是当前一个代表对象oi的好的替代,对于每一个非中心点p,需要考虑下面4中情况:

第一种情况:p当前隶属于代表对象oj(A类中心点),如果oj被orandom所代替作为代表对象,并且p离其他代表对象oi(B类的中心点)最近,则p重新分配给oi。(i!=j)

第二种情况:p当前隶属于代表对象oj(A类中心点),如果oj被orandom所代替作为代表对象,并且p离orandom(新的中心点)最近,则p重新分配给orandom。(i!=j)

第三种情况:p当前隶属于代表对象oi(B类中心点),如果oj被orandom所代替作为代表对象,并且p仍然离oi最近,则p不发生变化。(i!=j)

第四种情况:p当前隶属于代表对象oi(B类中心点),如果oj被orandom所代替作为代表对象,并且p离orandom最近,则p重新分配给orandom。(i!=j)

参考文献:

数据挖掘概念与技术

k-means算法的优缺点

https://blog.csdn.net/shuaishuai3409/article/details/50016013

猜你喜欢

转载自blog.csdn.net/Phoenix_tgd/article/details/83016899