无监督学习—聚类 k-means和k-medoids
无监督学习—聚类 k-means和k-medoids
运行环境
python3.6
jupyter notebook
一、基本思想
k-means
k均值聚类算法(k-meansclusteringalgorithm)是一种基于划分迭代求解的聚类分析技术,k-means算法的基本思想如下所示:
(1)随机选择一个k值,用以确定簇的总数;
(2)在数据集中任意选择k个实例,将它们作为初始的簇中心;
(3)计算这k个簇中心与其他剩余实例的简单欧氏距离(EuclideanDistance),用这个距离作为实例之间相似性的度量,将与某个簇相似度高的实例划分到该簇中,成为其成员之一;
(4)使用每个簇中的实例来计算该簇新的簇中心:对应类簇中所有数据对
象的均值,即为更新后该类簇的类簇中心;
(5)如果簇中心变化较小或者达到指定迭代次数,终止算法过程;否则,用新的簇中心作为簇中心并重复步骤(3)~(5)
k-medoids
K 中心聚类算法(k-medoids clustering algorithm)是一种基于划分迭代求解
的聚类分析技术,k-medoids 算法的基本思想如下所示:
(1)随机选择一个 k 值,用以确定簇的总数;
(2)在数据集中任意选择 k 个实例,将它们作为初始的簇中心;
(3)计算这 k 个簇中心与其他剩余实例的简单欧氏距离(Euclidean
Distance),用这个距离作为实例之间相似性的度量,将与某个簇相似度高的实
例划分到该簇中,成为其成员之一;
(4)在每个簇中按照顺序依次选取点,计算该点到当前簇中所有点距离之
和,最终距离之和最小的点,则视为新的中心点;
(5)如果簇中心变化较小或者达到指定迭代次数,终止算法过程;否则,
用新的簇中心作为簇中心并重复步骤(3)~(5)
参数调优
使用最优聚类确定基于划分的聚类分析技术的参数 k:簇中所有实例与簇中心的误差平方和最小的聚类
二、步骤
将之前降维的数据分别保存为df1.csv,df2.csv,df3.csv,df4.csv。分别代表1,2,3,4维数据。
k-means
1.导入数据
import pandas as pd
def load_csv(filename):#获取数据
data = pd.read_csv(filename)
del data["Unnamed: 0"]#因为上次实验保存数据时,将序列也保存了,现在将它删掉
return data
2.选取随机数作为K,并选取k个点作为初始簇中心
使用random中的sample方法可以生成随机的整数。
import random
def get_k(data,k):
b = random.sample([i for i in range(0,150)],k)#生成k个随机数
k_point=[]#用来保存k个簇中心
for i in b:#遍历这个随机数
a=[]
for j in data.iloc[i]:#把数据中第k行数据取出来,加入到
a.append(j)
k_point.append(a)
return k_point,len(k_point)#初始簇中心和k值
3.将点分到不同的簇中
计算每个点到簇中心的距离,将点放入距离最小的簇中。
def cluster(data,k_point,k):
all_point=[]
features = [[] for _ in range(k)]#几个簇建立几个空列表
for i in range(data.shape[0]):
point=[]#样本中每一个点
for j in data.iloc[i]:
point.append(j)
a=[]#存放距离
for m in range(len(k_point)):#对于上面的簇中心,一共三个点,计算三次
sum = 0
for j in range(len(k_point[m])):#每个点有几个坐标就计算几次
sum = (k_point[m][j]-point[j])**2 + sum #每个点的其中第一个元素相减平方再相加
a.append(sum)#a列表用来存储点到这三个簇中心的距离
index = a.index(min(a))#取出a当中最小的点,并取出它的索引
features[index].append(point)#将点加入到对应的簇中
return features#返回分完后的结果
4.更新簇中心
计算簇内所有对象的均值,作为新的簇中心。
#更新簇中心 计算簇内所有对象的均值,作为新的簇中心
def new_k_point(result):
new_point=[]#存放新的簇中心
for j in range(len(result)):#len(result)的值为3
result1 = pd.DataFrame(result[j])#取出result里的每个簇
avg =[]
for i in result1.columns:#遍历簇内所有实例
a = result1[i].values.tolist()#将每个簇中实例的第一个坐标取出为列表
sum = 0#求和
for m in range(0,len(a)):#求出列表a的平均值,即所有实例对应坐标的平均值
sum = sum + float(a[m])
avg.append(sum/len(a))
new_point.append(avg)#加入到大列表中
return new_point
5.计算误差平方和
此处需要先调用前面的函数,计算出所有的中心点和簇。
point_result=[]#簇中心,[[2簇中心],[3簇中心],[4簇中心],。。。]
all_point=[]
for x in range(2,20):#k从2-20
k_point,k = get_k(data3,x)
for j in range(1,100):#每个簇计算1-100次,满足条件就结束遍历
old_point = k_point
result = cluster(data3,k_point,k)
k_point=new_k_point(result)
if old_point==k_point:#判断新的簇中心和上一个簇中心是否相同,相同就结束判断
point_result.append(k_point)#所有的中心点
all_point.append(result)#所有的簇的点
break
else:
continue
利用上面计算出的结果,point_result和all_point来计算每个k对应的误差平方和。
def wuchapingfanghe(a,b):#a是所有的簇中心,b是所有的簇
for i in range(len(point_result)):#遍历point_result每一个点,即簇中心
list=[]
for m in range(len(a)):#计算簇内每个实例与簇中心的简单欧式距离
for j in range(len(b[m])):
sum=0
for i in range(len(b[m][j])):
sum = (b[m][j][i]-a[m][i])**2+sum
list.append(sum**0.5)
sum=0
for i in list:
sum = sum + i
return sum
因为k从2-20遍历,所以point_result的第一个元素是k为2的结果,第二个元素是k为3的结果。all_point也对应如此。
6.选取最优的k值
for i in range(len(point_result)-1):#误差平方和越来越小
d = wuchapingfanghe(point_result[i],all_point[i])
c = wuchapingfanghe(point_result[i+1],all_point[i+1])
cha = d-c
if cha<5:#计算每个k之间的差,小于就结束判断,认为其是最优k
print("簇中心为{}".format(point_result[i]))
print("k值为{}".format(i+2))#因为我的k是从2开始的
break
7.整合函数,遍历所有文件
suoyou_cluster_point=[]#保存最后最优的簇中心
suoyou_cluster=[]#保存所有的簇
for i in range(1,5):#读取四个文件
filename='df'+str(i)+'.csv'
data=load_csv(filename)
print("--------{}维数据--------".format(i))
point_result=[]#簇中心,[[2簇中心],[3簇中心],[4簇中心],。。。]
all_point=[]
for x in range(2,20):
k_point,k = get_k(data,x)
for j in range(1,100):
old_point = k_point
result = cluster(data,k_point,k)
k_point=new_k_point(result)
if old_point==k_point:#判断前一个中心点和后一个中心点是否相等
point_result.append(k_point)#所有的中心点
all_point.append(result)#所有的簇的点
break
else:
continue
for m in range(len(point_result)-1):#误差平方和越来越小
d = wuchapingfanghe(point_result[m],all_point[m])
c = wuchapingfanghe(point_result[m+1],all_point[m+1])
cha = d-c
if cha<5:
suoyou_cluster_point.append(point_result[m])
suoyou_cluster.append(all_point[m])
print("簇中心为{}".format(point_result[m]))
print("k值为{}".format(m+2))#因为我的k是从2开始的
break
运行每次输出的结果都不相同,原因可能是每次选取的初始簇中心不同。
8.可视化
#一维数据的点 第一个簇
import numpy as np
import matplotlib.pyplot as plt
import warnings
import time
df = pd.DataFrame()
for j in range(len(suoyou_cluster[0])):
df1 = pd.DataFrame(suoyou_cluster[0][j])
df1["label"]=[j]*df1.shape[0]
df = df.append(df1,ignore_index=True)
df['extra']=[0]*df.shape[0]#此处因为1维数据只有一列,所以加入一个全为0的列
plt.scatter(df.iloc[:,0].tolist(),df.iloc[:,2].tolist(),c=df['label'].tolist())
plt.show()
df = pd.DataFrame()
for j in range(len(suoyou_cluster[1])):
df1 = pd.DataFrame(suoyou_cluster[1][j])
df1["label"]=[j]*df1.shape[0]
df = df.append(df1,ignore_index=True)
df['extra']=[0]*df.shape[0]
plt.scatter(df.iloc[:,0].tolist(),df.iloc[:,1].tolist(),c=df['label'].tolist())
plt.show()
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
%matplotlib inline
fig = plt.figure()
ax = Axes3D(fig)
df = pd.DataFrame()
for j in range(len(suoyou_cluster[2])):
df1 = pd.DataFrame(suoyou_cluster[2][j])
df1["label"]=[j]*df1.shape[0]
df = df.append(df1,ignore_index=True)
df['extra']=[0]*df.shape[0]
ax.scatter(df.iloc[:,0].tolist(), df.iloc[:,1].tolist(), df.iloc[:,2].tolist(), c=df['label'].tolist())
plt.show()
df = pd.DataFrame()
for j in range(len(suoyou_cluster[3])):
df1 = pd.DataFrame(suoyou_cluster[3][j])
df1["label"]=[j]*df1.shape[0]
df = df.append(df1,ignore_index=True)
df['extra']=[0]*df.shape[0]
plt.scatter(df.iloc[:,0].tolist(),df.iloc[:,3].tolist(),c=df['label'].tolist())
plt.show()
k-medoids
前面的代码都和k-means的相同,只需修改更新簇中心的代码。
更新簇中心
#按照顺序选取簇中每个点,计算它到簇中其他点的距离并相加,选取距离之和最小的点作为新的簇中心。
def new_k_point(result):
new_k_point=[]
for i in range(len(result)):
sum_list=[]
for j in range(len(result[i])):
sum = 0
for m in range(len(result[i][j])):
sum = (result[i][j][m]-k_point[i][m])**2 + sum
sum_list.append(sum)
new_k_point.append(result[i][sum_list.index(min(sum_list))])
return new_k_point
整合结果
suoyou_cluster_point=[]
suoyou_cluster=[]
for i in range(1,5):
filename='df'+str(i)+'.csv'
data=load_csv(filename)
print("--------{}维数据--------".format(i))
point_result=[]#簇中心,[[2簇中心],[3簇中心],[4簇中心],。。。]
all_point=[]
for x in range(2,20):
k_point,k = get_k(data,x)
for j in range(1,100):
old_point = k_point
result = cluster(data,k_point,k)
k_point=new_k_point(result)
if old_point==k_point:
point_result.append(k_point)#所有的中心点
all_point.append(result)#所有的簇的点
break
else:
continue
for m in range(len(point_result)-1):#误差平方和越来越小
d = wuchapingfanghe(point_result[m],all_point[m])
c = wuchapingfanghe(point_result[m+1],all_point[m+1])
cha = d-c
#cha_list.append(d)
#print(i)
if cha<5:
suoyou_cluster_point.append(point_result[m])
suoyou_cluster.append(all_point[m])
print("簇中心为{}".format(point_result[m]))
print("k值为{}".format(m+2))#因为我的k是从2开始的
break
总结
通过本次实验让我对聚类有了很深的认识,它属于无监督学习,能够将给定的一组数据点分成不同的组,理论上,同一组的数据点应该具有相似的属性或特征。本次实验使我了解了k-means和k-mediods聚类算法的使用,也可以编写简单的函数实现聚类算法的应用。对于复杂的代码编写,可能有时写完代码之后自己再看起来都有点难以理解,所以代码一定要加入注释,方便后面修改和理解。我们也可以通过定义函数来增强代码的复用性。本次实验让我对最好的聚类数选取有了不同的了解,可以使用手肘法观察手肘位置的点作为最优k值,此外还可以计算簇中所有实例与簇中心的误差平方和最小的聚类作为最优k,方法有很多。
数据和源码:
链接:https://pan.baidu.com/s/1Br5RqikpeUthk4L7q-Tp0w
提取码:1025