手写实现经典Kmeans及动态显示聚类过程

1.经典kmeans算法回顾
step1:随机抽取k个对象作为初始聚类中心
step2:计算各个样本到这k个对象的各个距离,并将样本点分配到距离最近的类中
step3:重新计算k个聚类的聚类中心
step4:刷新聚类中心,直至聚类中心达到预设的阈值或不再变化
step5:输出聚类中心及对应的聚类
2.动态展示过程
在每次刷新完成新的聚类后plot一次二维/三维图像即可,过程较为有趣,记录一下。
3.Code

import pandas as pd
import numpy as np
import random
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

plt.rcParams['font.sans-serif']=['KaiTi']
plt.rcParams['axes.unicode_minus'] = False

# 高效读取数据源的函数
def read_data(data,skip_row,level,sht_name=0):
    if data[-4:]=='.csv':
        try:
            d_u = pd.read_csv(data, header=0, chunksize=10000, encoding='gbk')  # encoding='gbk',
        except UnicodeDecodeError:
            d_u = pd.read_csv(data, header=0, chunksize=10000, encoding='utf-8')
        chunk_0 = [i for i in d_u]
        df = pd.concat(chunk_0)
    else:
        if level==0 or level==1:
            header=skip_row
        else:
            header=[skip_row + i for i in range(level)]
        df = pd.read_excel(data, sheet_name=sht_name,header=header)
    return df

#计算距离的函数--欧氏距离
def cal_distance(a,b):
    distance=0
    for i in range(len(a)):
        distance+=(a[i]-b[i])**2
    return distance**0.5

#获取归一化的数据
def get_data(df,dimensions):
    u=[]
    for i in dimensions:
        minValue=df[i].min()
        maxValue = df[i].max()
        dk=(df[i]-minValue)/(maxValue-minValue)#对数据进行归一化处理
        u.append(dk)
    ds=pd.concat(u,axis=1)
    return ds

#初始化参数的函数
def get_init_params(data,n):
    resultBox = {}  # 存放分类结果的容器
    oriPoints=[]
    for i in range(n):
        resultBox[i]=[]
    std_2=0
    while std_2<std_1:  #简单优化一下,避免随机选取的点过于集中
        oriPointIndex=random.sample(range(len(data)),n)
        oriPoints=data[oriPointIndex]
        std_2=np.std(oriPoints)
        print(std_2)
    return resultBox,oriPoints

def get_length(data):
    length=0
    for i in data:
        length+=i**2
    return length**0.5

#获取聚类具体数据的函数
def get_clusters(dk,data,clusterBox,center,n=1):
    colorBox=[''  for i in range(len(data))]
    for i in range(len(data)):
        distanceBox=[]
        for j in range(n):
            distanceBox.append(cal_distance(center[j],data[i]))
        clusterBox[distanceBox.index(min(distanceBox))].append(data[i])
        colorBox[i]=color_list[distanceBox.index(min(distanceBox))]
    plot_data(dk, n, dimensions, color_list, colorBox)
    return clusterBox

#更新中心点的函数
def update_center(data):
    ArrayData = [np.vstack(data[i]) for i in range(len(data))]
    center = [i.mean(axis=0) for i in ArrayData]
    return np.vstack(center)

#判断中心点偏移量的函数
def cal_center_change(a,b,err):
    chgBox = []
    for i in range(len(a)):
        chgPct = (cal_distance(list(a[i]),list(b[i]))/get_length(list(a[i])))
        chgBox.append(chgPct)
    IsWeaken=1 if sum([1 if i<=err else 0 for i in chgBox])==len(a) else 0#判定是否达到设定的阈值
    return chgBox,IsWeaken

#作图函数,间隔一秒重新展示,体现聚类过程。仅在2维和3维时进行作图
def plot_data(df,n,dimensions,color_list,colorBox):
    if len(dimensions)==2 and n<=len(color_list):
        plt.figure(figsize=(20,10),dpi=60)
        plt.scatter(df[dimensions[0]], df[dimensions[1]], c=colorBox,s=10)
        plt.title('二维图像')
        plt.xlabel(dimensions[0])
        plt.ylabel(dimensions[1])
        plt.show(block=False)
        plt.pause(1)
        plt.close()
    elif len(dimensions)==3 and n<=len(color_list):
        fig = plt.figure(figsize=(20,10),dpi=60)
        ax = Axes3D(fig)
        ax.scatter(df[dimensions[0]], df[dimensions[1]],df[dimensions[2]], c=colorBox,s=10)
        ax.set_xlabel(dimensions[0])
        ax.set_ylabel(dimensions[1])
        ax.set_zlabel(dimensions[2])
        ax.legend([],title='三维图像')
        plt.show(block=False)
        plt.pause(1)
        plt.close()

#主函数
def kMeans(df,list_data,interation,err,n):
    clusterBox, center = get_init_params(list_data, n=n)
    chgBox, IsWeaken=[],0
    i=1
    while IsWeaken==0 and i<interation:
        clusterBox=get_clusters(df,list_data, clusterBox, center=center, n=n)
        last_center=center
        center=update_center(clusterBox)
        chgBox, IsWeaken = cal_center_change(last_center, center, err)
        i+=1
    return center,clusterBox,chgBox,i


f_0='../data_source/clsTest.csv'  #数据自己构造一个即可
color_list=['pink','blue','green','purple','orange','red','grey','yellow','black','DodgerBlue']  #暂时写10个颜色
# dimensions=['xdata','ydata'] #存放进行聚类时使用的维度/字段
dimensions=['xdata','ydata','zdata'] #存放进行聚类时使用的维度/字段

k=7 #分类数量
interation=500  #迭代次数
err=0.005#设置阈值

if  __name__== "__main__":
    ds=read_data(f_0,0,0,0)
    dk=get_data(ds,dimensions)#用于绘图
    list_data = dk.values
    std_1=np.std(list_data)
    print(std_1)
    center,clusterBox,chgBox,interateNum=kMeans(dk,list_data,interation,err=err,n=k)
    print('归一化后聚类中心:\n{}'.format(center))
    print('迭代次数:{}'.format(interateNum))

4.效果展示
二维图像:
在这里插入图片描述
三维图像:
在这里插入图片描述
5.结果与评价
运行结果
归一化后聚类中心:
[[0.06568673 0.43479818 0.43537879]
[0.84744645 0.0676106 0.08559254]
[0.21654101 0.63332569 0.62638664]
[0.15594514 0.19334235 0.20561701]
[0.4406322 0.53107942 0.52858135]
[0.95099398 0.9540862 0.93101594]
[0.16843369 0.83702506 0.8172746 ]]
迭代次数:8

从聚类的动态展示过程可以很清楚的看到:
a.初始点的选择对聚类结果影响很大,如一开始的两个中心点被分配到非常边缘的位置,则这两个中心将始终分配在对应位置,即局部最优,图中三维图像的黄色和灰色部分看得出非常明显;
b.在某些位置,若该团簇未被初始分配一个聚类,则该团簇容易被附近的其它聚类势力均分,如位于上图三维图的蓝/红/绿中间的部分
c.该算法运行效率很高,8次迭代即可达到指定预设值的收敛阈值

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值