一 什么是聚类
聚类是针对给定的样本,依据它们的特征的相似度或距离,将其归并到若干"类" 或者 "簇"的数据分析问题,直观上讲,聚类是将相似的样本归到一个类。
根据介绍我们明白,聚类的核心是如何来对两个样本的相似度来进行度量。聚类方法有多种度量方法,下面一起来看看吧。
二 相似度的度量方法
在聚类中,可以将样本集合看作是向量空间中点的集合,利用点和点之间的距离来代表样本与样本之间的相似度。点与点之间距离越近,则样本与样本的相似度越高。
下面来介绍几种距离度量
闵可夫斯基距离
给定样本集合X, X是m维实数向量空间Rm中点的集合,其中xi,xj ∈X,xi=(x1i,x2i,…,xmi)T,xj=(x1j,x2j,…,xmj)T,样本xi与样本xj的闵可夫斯基距离定义为
其中当p=2时称为欧氏距离
当p=1时称为曼哈顿距离
当 p= ∞时称为切比雪夫距离, 取各个坐标数值差的绝对值的最大值,即
马哈拉诺比斯距离
马哈拉诺比斯距离简称马氏距离,它考虑各个分量(也就是样本的特征)之间的相关性,并且与各个分量的尺度无关。
给定一个样本集合 X,X = (xij)m*n,,其协方差矩阵记作 S。样本xi 与样本xj之间的马哈拉诺比斯距离 dij 定义为
X的协方差矩阵是这样定义的:
设X=(X1,X2,…XN)T 则矩阵
为n维随机变量X的协方差矩阵,其中cij=Cov(Xi,Xj) i,j=1,2,…n 为 X的分量Xi,Xj的协方差。
除了距离度量,还可以用相关系数和夹角余弦来度量样本间的相似度
相关系数
样本 xi与样本 xj之间的相关系数定义为
其中
夹角余弦
样本与样本之间的夹角越小,即夹角余弦越趋于1,两个样本的相似度越高。 样本与样本之间的夹角越大,即夹角余弦越趋于0,两个样本的相似度越低。
样本 xi 与样本 xj之间的夹角余弦定义为
在上图中,A与C之间的夹角相比于B与C之间的夹角更小,所以A相对与B来讲与C更相似。
三 聚类方法
下面介绍两个聚类方法。
层次聚类
主要介绍层次聚类中的聚合聚类。
聚合聚类开始将每个样本各自分到一个类:之后将相距最近的两类合并,建立一 个新的类,重复此操作,每次减少一个类,直到满足停止条件。
聚合聚类算法如下:
代码实现
class Hierarchical_Clutering:
def __init__(self,Train):
self.Train=Train
self.M=self.Train.shape[1] #样本的维数
self.N=self.Train.shape[0] #样本的个数
self.D=self.cal_D()
self.classes,self.class_index=self.generating_classes() #类集合
def Minkowski_distance(self,X,Y,p):
#闵可夫斯基距离
max=0
d=0
if p>sys.maxsize: #如果p是无穷 即切比雪夫距离
for i in range(self.M):
if math.fabs(X[i]-Y[i]) > max:
max=math.fabs(X[i]-Y[i])
d=max
else:
sum=0
for i in range(self.M):
sum+=math.pow(math.fabs(X[i]-Y[i]),p)
d=math.pow(sum,1/p)
return d
def cal_D(self):
#计算n个样本两两之间的欧氏距离
D=[[0]*self.N]*self.N
for i in range(self.N):
for j in range(i+1,self.N):
d=self.Minkowski_distance(self.Train[i],self.Train[j],2)
D[i][j]=d
D[j][i]=d
return D
def generating_classes(self):
#构造n个类,使每个类只包含一个样本
classes={}
class_index=[]
for i in range(self.N):
classes[i]=[]
classes[i].append((i,self.Train[i])) #i 表示第几个样本 self.Train[i] 是样本的内容
class_index.append(i) #类别
return classes,class_index
def fit(self):
lens=len(self.classes)
min_c1=0
min_c2=0
while len(self.classes)>1: #类的总数大于等于2 就可以继续合并
print(self.classes)
l=len(self.class_index)
print(self.class_index)
min_dis = sys.maxsize
for i in range(l):
for j in range(i+1,l):
c1_num=self.class_index[i]
c2_num=self.class_index[j]
class1=self.classes[c1_num]
class2=self.classes[c2_num]
for c1 in range(len(class1)):
for c2 in range(len(class2)):
d=self.D[class1[c1][0]][class2[c2][0]]
if d <min_dis:
min_dis=d
min_c1= c1_num
min_c2= c2_num
for c in self.classes[min_c2]:
#print(c)
self.classes[min_c1].append(c)
del self.classes[min_c2]
self.class_index.remove(min_c2)
train=np.array([[0,2],
[0,0],
[1,0],
[5,0],
[5,2]])
model=Hierarchical_Clutering(train)
model.fit()
K均值聚类
K均值聚类是将样本集合划分为K个子集,也就是构成K个类,每一个类都有一个中心点。K均值聚类划分的原则是将每一个样本点划分到离它最近的那个中心点所代表的类。
1 距离度量
K均值聚类算法采用欧氏距离的平方作为样本之间的距离 即
2 损失函数
我们知道两个样本的欧氏距离越小则其相似度越高。所以当确定了K个类的中心之后,我们将其他样本进行聚类的方法就是将某个样本聚到离它最近的中心点所代表的类,也就是让所有样本到其所在类的中心点的距离之和最小,基于此建立损失函数。
3 K均值聚类算法
(1) 对于给定训练集,首先要随机选择K个样本作为中心点。
(2) 对样本进行聚类: 对于每一个样本,计算它到所有中心点的距离,选取距离最近的中心点,将该样本点划分到该中心点所在的类。
(3) 计算新的类的中心: 聚类完毕后,我们要计算每一类的均值,以此均值作为每一类的新的中心
重复以上 2,3 过程直到满足终止条件
终止条件可以是迭代次数到达某一个阈值,或者划分结果不再改变。
4 代码实现
class Kmeans:
def __init__(self,Train,K):
self.Train=Train
self.K=K #K个中心
#self.M=M #进行M次迭代
self.center=self.generate_initial_center()
self.classes={}
def Minkowski_distance(self, X, Y, p):
# 闵可夫斯基距离
max = 0
d = 0
if p > sys.maxsize: # 如果p是无穷 即切比雪夫距离
for i in range(self.train.shape[1]):
if math.fbs(X[i] - Y[i]) > max:
max = math.fabs(X[i] - Y[i])
d = max
else:
sum = 0
for i in range(self.Train.shape[1]):
sum += math.pow(math.fabs(X[i] - Y[i]), p)
d = math.pow(sum, 1 / p)
return d
def generate_initial_center(self):
#随机选择k个样本点作为初始样本点
i=0
c=[]
center={}
while i< self.K:
a=random.randint(0,self.Train.shape[0]-1) #随机选择一个样本序号
if a not in c: #为避免重复选择某一个顶点
center[i]=self.Train[a]
c.append(a)
i+=1
return center
def fit(self):
for k in range(self.K):
self.classes[k]=[]
isContinue=True
#for m in range(self.M):
while isContinue:
isContinue=False
for i in range(self.Train.shape[0]): #为每个样本找离它最近的中心点以此分类
min_dis=sys.maxsize
min_k=0
for k in range(self.K):
d=(self.Minkowski_distance(self.Train[i],self.center[k],2))**2
if d < min_dis:
min_dis=d
min_k=k
self.classes[min_k].append(self.Train[i])
for k in range(self.K):
print("k= "+str(k))
print("更新前")
print(self.center[k])
list=self.classes[k]
lens=len(list) #这个类一共有多少个样本
array=np.array(list) #转换为矩阵
means=np.sum(array, axis=0)/lens #更新中心点
if any(means!=self.center[k]):
self.center[k]=means
isContinue=True
print("更新后")
print(self.center[k])
self.classes[k]=[]
print("*******************")