社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

【福利】机器学习:Python实现聚类算法(一)之K-Means

36大数据 • 8 年前 • 1168 次点击  

1.简介


K-means算法是最为经典的基于划分的聚类方法,是十大经典数据挖掘算法之一。K-means算法的基本思想是:以空间中k个点为中心进行聚类,对最靠近他们的对象归类。通过迭代的方法,逐次更新各聚类中心的值,直至得到最好的聚类结果。

2. 算法大致流程为:


1)随机选取k个点作为种子点(这k个点不一定属于数据集)

2)分别计算每个数据点到k个种子点的距离,离哪个种子点最近,就属于哪类

3)重新计算k个种子点的坐标(简单常用的方法是求坐标值的平均值作为新的坐标值)

4)重复2、3步,直到种子点坐标不变或者循环次数完成

3.完整计算过程


1)设置实验数据

运行之后,效果如下图所示:

在图中,ABCDE五个点是待分类点,k1、k2是两个种子点。

2)计算ABCDE五个点到k1、k2的距离,离哪个点近,就属于哪个点,进行初步分类。

结果如图:

A、B属于k1,C、D、E属于k2

3)重新计算k1、k2的坐标。这里使用简单的坐标的平均值,使用其他算法也可以(例如以下三个公式)

a)Minkowski Distance公式——λ可以随意取值,可以是负数,也可以是正数,或是无穷大。

b)Euclidean Distance公式——也就是第一个公式λ=2的情况

c)CityBlock Distance公式——也就是第一个公式λ=1的情况

采用坐标平均值算法的结果如图:

4)重复2、3步,直到最终分类完毕。下面是完整的示例代码:

import numpy as np
import matplotlib.pyplot as plt

##样本数据(Xi,Yi),需要转换成数组(列表)形式
Xn=np.array([2,3,1.9,2.5,4])
Yn=np.array([5,4.8,4,1.8,2.2])

#标识符号
sign_n = ['A','B','C','D','E']
sign_k = ['k1','k2']

def start_class(Xk,Yk):
    ##数据点分类
    cls_dict = {}
    ##离哪个分类点最近,属于哪个分类
    for i in range(len(Xn)):
        temp = []
        for j in range(len(Xk)):
            d1 = np.sqrt((Xn[i]-Xk[j])*(Xn[i]-Xk[j])+(Yn[i]-Yk[j])*(Yn[i]-Yk[j]))
            temp.append(d1)
        min_dis=np.min(temp)
        min_inx = temp.index(min_dis)
        cls_dict[sign_n[i]]=sign_k[min_inx]
    #print(cls_dict)
    return cls_dict
    
##重新计算分类的坐标点
def recal_class_point(Xk,Yk,cls_dict):  
    num_k1 = 0  #属于k1的数据点的个数
    num_k2 = 0  #属于k2的数据点的个数
    x1 =0       #属于k1的x坐标和
    y1 =0       #属于k1的y坐标和
    x2 =0       #属于k2的x坐标和
    y2 =0       #属于k2的y坐标和

    ##循环读取已经分类的数据
    for d in cls_dict:
        ##读取d的类别
        kk = cls_dict[d]
        if kk == 'k1':
            #读取d在数据集中的索引
            idx = sign_n.index(d)
            ##累加x值
            x1 += Xn[idx]
            ##累加y值
            y1 += Yn[idx]
            ##累加分类个数
            num_k1 += 1
        else :
            #读取d在数据集中的索引
            idx = sign_n.index(d)
            ##累加x值
            x2 += Xn[idx]
            ##累加y值
            y2 += Yn[idx]
            ##累加分类个数
            num_k2 += 1
    ##求平均值获取新的分类坐标点
    k1_new_x = x1/num_k1 #新的k1的x坐标
    k1_new_y = y1/num_k1 #新的k1的y坐标

    k2_new_x = x2/num_k2 #新的k2的x坐标
    k2_new_y = y2/num_k2 #新的k2的y坐标

    ##新的分类数组
    Xk=np.array([k1_new_x,k2_new_x])
    Yk=np.array([k1_new_y,k2_new_y])
    return Xk,Yk

def draw_point(Xk,Yk,cls_dict):
    #画样本点
    plt.figure(figsize=(5,4)) 
    plt.scatter(Xn,Yn,color="green",label="数据",linewidth=1)
    plt.scatter(Xk,Yk,color="red",label="分类",linewidth=1)
    plt.xticks(range(1,6))
    plt.xlim([1,5])
    plt.ylim([1,6])
    plt.legend()
    for i in range(len(Xn)):
        plt.text(Xn[i],Yn[i],sign_n[i]+":"+cls_dict[sign_n[i]])
        for i in range(len(Xk)):
            plt.text(Xk[i],Yk[i],sign_k[i])
    plt.show()

if __name__ == "__main__":
    ##种子
    Xk=np.array([3.3,3.0])
    Yk=np.array([5.7,3.2])
    for i in range(3):
        cls_dict =start_class(Xk,Yk)
        Xk_new,Yk_new =recal_class_point(Xk,Yk,cls_dict)
        Xk=Xk_new
        Yk=Yk_new
        draw_point(Xk,Yk,cls_dict)

最终分类结果:

由上图可以看出,C点最终是属于k1类,而不是开始的k2.

4.K-Means的不足


K-Means算法的不足,都是由初始值引起的:

1)初始分类数目k值很难估计,不确定应该分成多少类才最合适(ISODATA算法通过类的自动合并和分裂,得到较为合理的类型数目k。这里不讲这个算法)

2)不同的随机种子会得到完全不同的结果(K-Means++算法可以用来解决这个问题,其可以有效地选择初始点)

5.K-Means++算法


算法流程如下:

1)在数据集中随机挑选1个点作为种子点

##随机挑选一个数据点作为种子点
def select_seed(Xn):
    idx = np.random.choice(range(len(Xn)))
    return idx

2)计算剩数据点到这个点的距离d(x),并且加入到列表

##计算数据点到种子点的距离
def cal_dis(Xn,Yn,idx):
    dis_list = []
    for i in range(len(Xn)):       
        d = np.sqrt((Xn[i]-Xn[idx])**2+(Yn[i]-Yn[idx])**2)
        dis_list.append(d)
    return dis_list

 

3)再取一个随机值。这次的选择思路是:先取一个能落在上步计算的距离列表求和后(sum(dis_list))的随机值rom,然后用rom -= d(x),直到rom<=0,此时的点就是下一个“种子点”

##随机挑选另外的种子点
def select_seed_other(Xn,Yn,dis_list):
    d_sum = sum(dis_list)
    rom = d_sum * np.random.random()
    idx = 0
    for i in range(len(Xn)):
        rom -= dis_list[i]
        if rom > 0 :
            continue
        else :
            idx = i
    return idx

4)重复第2步和第3步,直到选出k个种子

5)进行标准的K-Means算法。下面完整代码

import numpy as np
import matplotlib.pyplot as plt

##样本数据(Xi,Yi),需要转换成数组(列表)形式
Xn=np.array([2,3,1.9,2.5,4])
Yn=np.array([5,4.8,4,1.8,2.2])

#标识符号
sign_n = ['A','B','C','D','E']
sign_k = ['k1','k2']

##随机挑选一个数据点作为种子点
def select_seed(Xn):
    idx = np.random.choice(range(len(Xn)))
    return idx
    
##计算数据点到种子点的距离
def cal_dis(Xn,Yn,idx):
    dis_list = []
    for i in range(len(Xn)):       
        d = np.sqrt((Xn[i]-Xn[idx])**2+(Yn[i]-Yn[idx])**2)
        dis_list.append(d)
    return dis_list

##随机挑选另外的种子点
def select_seed_other(Xn,Yn,dis_list):
    d_sum = sum(dis_list)
    rom = d_sum * np.random.random()
    idx = 0
    for i in range(len(Xn)):
        rom -= dis_list[i]
        if rom > 0 :
            continue
        else :
            idx = i
    return idx

##选取所有种子点
def select_seed_all(seed_count):
     ##种子点
    Xk = []  ##种子点x轴列表
    Yk = []  ##种子点y轴列表
    
    idx = 0  ##选取的种子点的索引
    dis_list = [] ##距离列表
    
    
    ##选取种子点
    #因为实验数据少,有一定的几率选到同一个数据,所以加一个判断
    idx_list = []
    flag = True
    for i in range(seed_count):
        if i == 0:
             idx = select_seed(Xn)
             dis_list = cal_dis(Xn,Yn,idx)
             Xk.append(Xn[idx])
             Yk.append(Yn[idx])
             idx_list.append(idx)
        else :
            while flag:
                idx = select_seed_other(Xn,Yn,dis_list)
                if idx not in idx_list:
                    flag = False
                else :
                    continue
            dis_list = cal_dis(Xn,Yn,idx)
            Xk.append(Xn[idx])
            Yk.append(Yn[idx])
            idx_list.append(idx)
                
    ##列表转成数组       
    Xk=np.array(Xk)
    Yk=np.array(Yk)

    return Xk,Yk
    

def start_class(Xk,Yk):
    ##数据点分类
    cls_dict = {}
    ##离哪个分类点最近,属于哪个分类
    for i in range(len(Xn)):
        temp = []
        for j in range(len(Xk)):
            d1 = np.sqrt((Xn[i]-Xk[j])*(Xn[i]-Xk[j])+(Yn[i]-Yk[j])*(Yn[i]-Yk[j]))
            temp.append(d1)
        min_dis=np.min(temp)
        min_inx = temp.index(min_dis)
        cls_dict[sign_n[i]]=sign_k[min_inx]
    #print(cls_dict)
    return cls_dict
    
##重新计算分类的坐标点
def recal_class_point(Xk,Yk,cls_dict):  
    num_k1 = 0  #属于k1的数据点的个数
    num_k2 = 0  #属于k2的数据点的个数
    x1 =0       #属于k1的x坐标和
    y1 =0       #属于k1的y坐标和
    x2 =0       #属于k2的x坐标和
    y2 =0       #属于k2的y坐标和

    ##循环读取已经分类的数据
    for d in cls_dict:
        ##读取d的类别
        kk = cls_dict[d]
        if kk == 'k1':
            #读取d在数据集中的索引
            idx = sign_n.index(d)
            ##累加x值
            x1 += Xn[idx]
            ##累加y值
            y1 += Yn[idx]
            ##累加分类个数
            num_k1 += 1
        else :
            #读取d在数据集中的索引
            idx = sign_n.index(d)
            ##累加x值
            x2 += Xn[idx]
            ##累加y值
            y2 += Yn[idx]
            ##累加分类个数
            num_k2 += 1
    ##求平均值获取新的分类坐标点
    k1_new_x = x1/num_k1 #新的k1的x坐标
    k1_new_y = y1/num_k1 #新的k1的y坐标

    k2_new_x = x2/num_k2 #新的k2的x坐标
    k2_new_y = y2/num_k2 #新的k2的y坐标

    ##新的分类数组
    Xk=np.array([k1_new_x,k2_new_x])
    Yk=np.array([k1_new_y,k2_new_y])
    return Xk,Yk

def draw_point(Xk,Yk,cls_dict):
    #画样本点
    plt.figure(figsize=(5,4)) 
    plt.scatter(Xn,Yn,color="green",label="数据",linewidth=1)
    plt.scatter(Xk,Yk,color="red",label="分类",linewidth=1)
    plt.xticks(range(1,6))
    plt.xlim([1,5])
    plt.ylim([1,6])
    plt.legend()
    for i in range(len(Xn)):
        plt.text(Xn[i],Yn[i],sign_n[i]+":"+cls_dict[sign_n[i]])
        for i in range(len(Xk)):
            plt.text(Xk[i],Yk[i],sign_k[i])
    plt.show()

def draw_point_all_seed(Xk,Yk):
    #画样本点
    plt.figure(figsize=(5,4)) 
    plt.scatter(Xn,Yn,color="green",label="数据",linewidth=1)
    plt.scatter(Xk,Yk,color="red",label="分类",linewidth=1)
    plt.xticks(range(1,6))
    plt.xlim([1,5])
    plt.ylim([1,6])
    plt.legend()
    for i in range(len(Xn)):
        plt.text(Xn[i],Yn[i],sign_n[i])
    plt.show()

if __name__ == "__main__":

     ##选取2个种子点
     Xk,Yk = select_seed_all(2)
     ##查看种子点
     draw_point_all_seed(Xk,Yk)
     ##循环三次进行分类
     for i in range(3):
        cls_dict =start_class(Xk,Yk)
        Xk_new,Yk_new =recal_class_point(Xk,Yk,cls_dict)
        Xk=Xk_new
        Yk=Yk_new
        draw_point(Xk,Yk,cls_dict)

如图所示,选择了A、E两点作为种子点。

最终的结果。

补充说明:因为数据量太少,在选取所有种子函数的while阶段有可能陷入死循环,所以需要关闭代码重新运行才可以出结果。

6.sklearn包中的K-Means算法


1)函数: sklearn.cluster.KMeans

2)主要参数

n_clusters:要进行的分类的个数,即上文中k值,默认是8

max_iter  :最大迭代次数。默认300

min_iter   :最小迭代次数,默认10

init:有三个可选项

‘k-means ++’:使用k-means++算法,默认选项

‘random’:从初始质心数据中随机选择k个观察值

第三个是数组形式的参数

n_jobs: 设置并行量 (-1表示使用所有CPU)

3)主要属性:

cluster_centers_ :集群中心的坐标

labels_ : 每个点的标签

4)官网示例:

>>> from sklearn.cluster import KMeans
>>> import numpy as np
>>> X = np.array([[1, 2], [1, 4], [1, 0],
...               [4, 2], [4, 4], [4, 0]])
>>> kmeans = KMeans(n_clusters=2, random_state=0).fit(X)
>>> kmeans.labels_
array([0, 0, 0, 1, 1, 1], dtype=int32)
>>> kmeans.predict([[0, 0], [4, 4]])
array([0, 1], dtype=int32)
>>> kmeans.cluster_centers_
array([[ 1.,  2.],
       [ 4.,  2.]])

 End 



你投稿,我送书

为了让大家能有更多的好文章可以阅读,36大数据联合华章图书共同推出「祈文奖励计划」,该计划将奖励每个月对大数据行业贡献(翻译or投稿)最多的用户中选出最前面的10名小伙伴,统一送出华章图书邮递最新计算机图书一本。投稿邮箱:dashuju36@qq.com

点击查看:你投稿,我送书,「祈文奖励计划」活动详情>>>


阅读排行榜/精华推荐
1
入门学习

如果有人质疑大数据?不妨把这两个视频转给他 

视频:大数据到底是什么 都说干大数据挣钱 1分钟告诉你都在干什么

人人都需要知道 关于大数据最常见的10个问题

2
进阶修炼

从底层到应用,那些数据人的必备技能

如何高效地学好 R?

一个程序员怎样才算精通Python?

3
数据源爬取/收集

排名前50的开源Web爬虫用于数据挖掘

33款可用来抓数据的开源爬虫软件工具

在中国我们如何收集数据?全球数据收集大教程

4
干货教程

PPT:数据可视化,到底该用什么软件来展示数据?

干货|电信运营商数据价值跨行业运营的现状与思考

大数据分析的集中化之路 建设银行大数据应用实践PPT

【实战PPT】看工商银行如何利用大数据洞察客户心声?              

六步,让你用Excel做出强大漂亮的数据地图

 数据商业的崛起 解密中国大数据第一股——国双

双11剁手幕后的阿里“黑科技” OceanBase/金融云架构/ODPS/dataV

金融行业大数据用户画像实践


讲述大数据在金融、电信、工业、商业、电子商务、网络游戏、移动互联网等多个领域的应用,以中立、客观、专业、可信赖的态度,多层次、多维度地影响着最广泛的大数据人群

36大数据

长按识别二维码,关注36大数据



搜索「36大数据」或输入36dsj.com查看更多内容。


投稿/商务/合作:dashuju36@qq.com



点击下方“阅读原文”查看更多

↓↓↓



今天看啥 - 高品质阅读平台
本文地址:http://www.jintiankansha.me/weixin/xE5PFcjasA
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/2152
 
1168 次点击