python简单实现k-means聚类算法

我对聚类算法的理解:将一堆,无划分的数据,通过它们之间的相似度进行划分。(简单粗暴^。^)

根据上面的理解,K-means算法就能知名晓意了:就是将一堆无划分的样本数据,定义需要划分为K堆,然后通过每个样本数据点与中心点间的距离进行归簇。(在k-means中需要在划分前需指定中心点,这是它的缺点)

下面是官方一点的说法:

K-Means算法是最为经典的基于划分的聚簇方法,是十大经典数据挖掘算法之一。简单的说K-Means就是在没有任何监督信号的情况下将数据分为K份的一种方法。聚类算法就是无监督学习中最常见的一种,给定一组数据,需要聚类算法去挖掘数据中的隐含信息。聚类算法的应用很广:顾客行为聚类,google新闻聚类等。K值是聚类结果中类别的数量。

其实k-means算法真的简单,先不说代码,就说原理,你只要仔细理解一遍,用自己的语言总结一下,慢慢就能摸索出代码。我刚学时参考了一篇博客,找不到连接了,很感谢他写的很简单,理解的很快,过了一段时间用自己的思路将代码摸索了出来。直接看完整代码:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

global K
global N
global spot
global unite
global mean
global count

def cluster():  #划分每个点一个到簇
    global unite
    unite = []      #这里千万注意,需要每次都定义,否则会重复添加长度。当下一次运行cluster函数时才会重置unite列表
    for i in range(K):
        unite.append([])
    for i in range(len(spot)):
        max_max = np.iinfo(np.int32).max  #预定义一个极大值,用于比较
        flag = -1
        for j in range(K):
            n = (spot[i][0]-mean[j][0])**2+(spot[i][1]-mean[j][1])**2
            if(n<max_max):
                max_max = n
                flag = j
        unite[flag].append(spot[i])
    for i in range(K):
        print("第{}簇:{}".format(i,unite[i]))

def square_d():
    global count
    sum_E = 0
    for i in range(len(unite)):
        for j in range(len(unite[i])):
            sum_E += ((unite[i][j][0]-mean[i][0])**2 + (unite[i][j][1]-mean[i][1])**2)
    count +=1
    return sum_E

def remean():
    for i in range(len(unite)):
        sum_x = 0
        sum_y = 0
        for j in range(len(unite[i])):
            sum_x += unite[i][j][0]
            sum_y += unite[i][j][1]
        # if(len(unite[i]) == 0):     #分母不为0,如果前面判断了中心值不一样可以省略
        #     mean[i] = (0,0)
        # else:
        #     mean[i] = (sum_x/len(unite[i]),sum_y/len(unite[i]))
        mean[i] = (sum_x/len(unite[i]),sum_y/len(unite[i]))    #根据均值,重新定义的中心值

def show():
     #样本数据横坐标列表
    xx = []
    for i in range(K):
        xx.append([])
    #样本数据纵坐标列表
    yy = []
    for i in range(K):
        yy.append([])
    #使用样本坐标,绘制散点图
    for i in range(K):
        for j in range(len(unite[i])):
            xx[i].append(unite[i][j][0])
        for j in range(len(unite[i])):
            yy[i].append(unite[i][j][1])
        plt.scatter(xx[i],yy[i],label=i)
        plt.scatter(mean[i][0],mean[i][1],label=i)
        plt.legend()
    #显示散点图
    plt.show()

if __name__=="__main__":
    K = 4   #聚类数    手动设置
    N = 30  #样本数    手动设置
    spot = []   #样本列表
    for i in range(N):
        spot.append((np.random.randint(50),np.random.randint(50)))  #50是我给它定义的范围

    unite = []  #每个点归属簇
    mean = []   #每个簇的中心值

    set_t = set()
    while(1):           #虽然两行代码就可以搞定这个选值,但是我建议使用集合,防止重复值
        d = np.random.randint(N)    #当然如果你有更简单的方法除外,就我觉得使用集合比较简单
        set_t.add(d)
        if(len(set_t)==K):
            for i in set_t:
                mean.append(spot[i])
            break

    count = 0
    m = []
    for i in range(2):      #首先两次循环,得出两个比较的平方误差
        cluster()       #聚类
        temp = square_d()   #计算平方误差
        m.append(temp)
        print("第{}次聚类平方的差值为:{}".format(count,temp))
        remean()    #重定义中心值

    while(m[0] != m[1]):    #就两次循环后,重复选中心值,直至相等
        m[0] = m[1]
        cluster()
        m[1] = square_d()
        print("第{}次聚类平方的差值为:{}".format(count,m[1]))
        remean()

    show()  #构散点图    构图与k-means无关,但是利用图表将数据展示出来更直观、更有说服力


这算是写的很简单的了,稍微复杂点的,定义每个部分的类于不同文件,在每个类中更细致的实现方法,再相互调用实例。

这里我根据代码总结了实现聚类的思想:

1:随机选择K个中心值

2:根据样本数据各点与中心点的距离来进行归簇

3:通过整个簇的均值,重置每个簇的中心值

4:重复2、3,直至达到最大的迭代次数(例如:我这里的条件设置为本次与上次的平方误差相等)


适用范围及缺陷:

K-Menas算法尝试找到使平方误差准则函数最小的簇。当潜在的簇形状是凸面的,簇与簇之间区别较明显,且簇大小相近时,其聚类结果较理想。对于处理大数据集合,该算法非常高效,且伸缩性较好。

但该算法除了要事先确定簇数K和对初始聚类中心敏感外,经常以局部最优结束,同时对“噪声”和孤立点敏感,并且该方法不适于发现非凸面形状的簇或大小差别很大的簇。

克服缺点的方法:使用尽量多的数据;使用中位数代替均值来克服outlier的问题。


优缺点是借鉴别人的,感觉比我总结的好那么一点点。所以记住上面的步骤,就算忘记了模板,也能自己慢慢摸索出来。

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值