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次迭代即可达到指定预设值的收敛阈值