层次聚类
1.1 凝聚策略
按照聚类算法使用的两种不同的策略,可以将聚类算法分为两类:
1)层次或凝聚式算法
这类算法一开始将每个点都看成簇。簇与簇之间按照接近度(closeness)来组合,接近度可以按照“接近”的不同含义采用不同的定义。当进一步的组合导致多个原因之下的非期望结果时,上述组合过程结束。比如停止条件为:达到预先给定的簇数目,或者使用簇的紧密度测度方法,一旦两个小簇组合之后得到簇内的点分散的区域较大就停止簇的构建。
2)点分配过程算法
按照某个顺序依次考虑每个点,并将它分配到最适合的簇中。该过程通常有一个短暂的初始簇估计阶段。一些变形算法允许临时的簇合并或分裂的过程,或者当点为离群点时允许不将该点分配到任何簇中。
1.2层次凝聚算法的分类
层次聚类方法可以是凝聚或分裂的。取决于层次分解是以自底向上还是自顶向下方式形成。
1.2.1 凝聚的层次聚类方法
该方法使用自底向上的策略。该单个簇成为层次结构的根,在合并步骤,它找出两个最接近的簇(根据某种相似度量),并且合并他们,形成一个簇。因为每次迭代合并两个簇,其中每个簇至少包含一个对象,多以凝聚方法最多需要n次迭代
1.2.2 分裂的层次聚类方法
该方法使用自顶向下的策略。它从把所有对象置于一个簇中开始,该簇是层次结构的根。然后,它把根上的簇划分为多个较小的子簇,并且递归地把这些簇划分为更小的簇。
1.3算法方法的距离度量
无论是使用凝聚方法还是使用分裂方法,一个核心的问题是度量两个簇之间的距离,其中每个簇一般都是一个对象集。
4个广泛采用的簇间距离度量方法为:
最小距离:
最大距离:
均值距离:
平均距离:
当算法使用最小距离来衡量簇间距离时,有时称为最近邻聚类算法。如果当最近的两个簇之间的距离超过用户给定的阈值时聚类过程就会终止,则称其为单连接算法。
当算法使用最大距离来衡量簇间距离时,有时称为最远邻聚类算法。如果当最近的两个簇之间的最大距离超过用户给定的阈值时,聚类过程就会终止,则称其为全连接算法。
使用均值距离或平均距离是对最小和最大距离之间的一种折中算法,并且可以克服离群点敏感性问题。
1.4算法的实现
import math
#读取表格型数据,获取特征数据集
def readfile(filename):
lines=[line for line in open(filename)]
#第一行是标题
colnames=lines[0].strip().split('\t')[1:]
rowsname=[]
data=[]
for line in lines[1:]:
p=line.strip().split('\t')
#每行的第一列是列名
rowsname.append(p[0])
#剩余的部分就是该行的对应的数据
onerow=[float(x) for x in p[1:]]
data.append(onerow)
return rowsname,colnames,data
#定义一个聚类,包含左右自聚类
class bicluster:
def __init__(self,vec,left=None,right=None,distance=0.0,id=None):
self.vec=vec #聚类的中心点
self.left=left #左子聚类
self.right=right #右子聚类
self.id=id #聚类的id
self.distance=distance #左右子聚类间的距离
#计算两行的皮尔逊相似度
def pearson(v1,v2):
#简单求和
sum1=sum(v1)
sum2=sum(v2)
#求平方和
sum1Sq=sum([pow(v,2) for v in v1])
sum2Sq=sum([pow(v,2) for v in v2])
#求乘积之和
Psum=sum(v1[i] *v2[i] for i in range(len(v1)))
#计算r
num=Psum-(sum1*sum2/len(v1))
den=math.sqrt((sum1Sq-pow(sum1,2)/len(v1))*(sum2Sq-pow(sum2,2)/len(v1)))
if den==0:
return 0
return 1.0-num/den
#根据数据集形成聚类树
def hcluster(rows,distance=pearson):
distance_set={}
currentclusterid=-1
#最开始聚类就是数据集中的行,每行一个聚类
clust=[bicluster(rows[i],id=i) for i in range(len(rows))]
while len(clust)>1:
lowestpair=(0,1)
closest=distance(clust[0].vec,clust[1].vec)
#遍历每一对聚类,寻找距离最小的一对聚类
for i in range(len(clust)):
for j in range(i+1,len(clust)):
#用distance_set来缓存距离最小的计算值
if(clust[i].id,clust[j].id) not in distance_set:
distance_set[(clust[i].id,clust[j].id)]=distance(clust[i].vec,clust[j].vec)
d=distance_set[(clust[i].id),clust[j].id]
if d<closest:
closest=d
lowestpair=(i,j)
#计算距离最近的两个聚类的平均值作为代表新聚类的中心点
mergevec=[(clust[lowestpair[0]].vec[i]+clust[lowestpair[1]].vec[i])/2.0 for i in range(len(clust[0].vec))]
#将距离最近的两个聚类合并成新的聚类
newCluster=bicluster(mergevec,left=clust[lowestpair[0]],
right=clust[lowestpair[1]],
distance=closest,
id=currentclusterid)
#不在原始集合中聚类id设置为负数,为了标记这是一个枝节点
currentclusterid -=1
#删除旧的聚类(因为旧聚类已经添加为新的聚类的左右子聚类了)
del clust[lowestpair[1]]
del clust[lowestpair[0]]
clust.append(newCluster)
return clust[0] # 返回聚类树
if __name__ == '__main__':
blognames,words,data=readfile('blogdata.txt')
clust=hcluster(data)