您的位置:首页 > 其它

发现群组(二)分级聚类

2016-03-21 21:44 155 查看

python基础

字符串处理方法:

string.lstrip() 截掉string 左边的空格

string.rstrip() 删除string 字符串末尾的空格

string.split(str="", num=string.count(str)) 以str 为分隔符切片string,如果num有指定值,则仅分隔num 个子字符串

序列操作符

(1)切片( [ ] 和 [ : ] )

对任何范围[start:end],我们可以访问到包括start 在内到end(不包括end)的所有字符

>>> aString = 'abcd'
>>> aString[1:3]
'bc'
>>> aString[2:4]
'cd'


反向索引

>>> aString[-1]
'd'
>>> aString[-3:-1]
'bc'
如果开始索引或者结束索引没有被指定,则分别以字符串的第一个和最后一个索引值为默认值。

>>> aString[2:]
'cd'
>>> aString[1:]
'bcd'
>>> aString[:-1]
'abc'
(2)总述

序列操作符 作用

seq[ind] 获得下标为ind 的元素

seq[ind1:ind2] 获得下标从ind1 到ind2 间的元素集合

seq * expr 序列重复expr 次

seq1 + seq2 连接序列seq1 和seq2

obj in seq 判断obj 元素是否包含在seq 中

obj not in seq 判断obj 元素是否不包含在seq 中

(3) 序列的操作方法

len(seq) 返回seq 的长度

reversed(seq) 接受一个序列作为参数,返回一个以逆序访问的迭代器(PEP 322)

sum(seq, init=0) 返回seq 和可选参数init 的总和, 其效果等同于reduce(operator.add,seq,init)

特别注意上述接口的参数类型都为序列 (列表,数组、字典等),和我们平时用的C语言等函数参数的概念是有所差异的。

class 类

构造实例的方法:

在创建类时,可以通过重构__new__方法来构造对象的一个实例等,此方法和C++等语言中的new函数构造对象的方法是一致的。

通过重构__init__方法,实现初始化。

__new__(cls, *args, **kwargs) 创建对象时调用,返回当前对象的一个实例;注意:这里的第一个参数是cls即class本身

__init__(self, *args, **kwargs) 创建完对象后调用,对当前对象的实例的一些初始化,无返回值,即在调用__new__之后,根据返回的实例初始化;注意,这里的第一个参数是self即对象本身【注意和new的区别】

分级聚类

算法

将每一行的数据视为一个对象,构建描述此对象的类(bicluster)。而后使用皮尔逊相关度(当然也可以用其他相关度的算法)算法,计算每两个对象之间的亲密度(计算量大),先找出最亲密的两个节点,作为叶子节点,而后以此两个节点构造他们的父节点(父节点的值为两个子节点中各个数据的平均值),以此循环,直到所有的行数据对象被处理完毕,最终形成一个二叉树的结构。

实际上即是由一个链表(行数据读入后存在在一个rows列表中),构造一个二叉树的过程。构造的控制过程由具体算法控制。

代码实现

最终的代码如下:

# coding=utf-8
from math import sqrt
import codecs
#解析blogdata数据
def readfile(filename):
file=codecs.open('blogdata.txt','r','utf-8')
lines=[line for line in file.readlines()]
#将文件的第一行(单词)拆分成单词列表,存放到colnames中
colnames=lines[0].strip().split('\t')[1:]
rownames=[]
data=[]
for line in lines[1:]:   #对于除第一行之外,剩余的行进行处理
p=line.strip().split('\t')
rownames.append(p[0])  #每行对应的列表的第一个元素为博客title,单独存放,作为行名称
#本行中剩余的为单词 出现次数的统计
data.append([float(x) for x in p[1:]])
# print data
file.close()
return rownames,colnames,data  #所有单词出现的次数都存在data中,这个怎么知道是哪个单词在哪篇里面出现的次数了??

#皮尔逊相关度的实现
def pearson(v1,v2):
#简单求和
sum1= sum(v1)  #参数v1和v2是一个数据列表
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(Pearson score)
print "sum1 :%f" % sum1Sq
print "sum2 :%f" % sum2Sq
print "pow1 :%f" % pow(sum1,2)
print "pow2 :%f" % pow(sum2,2)

num = pSum-(sum1*sum2)/len(v1)
den =sqrt((sum1Sq-pow(sum1,2)/len(v1))*(sum2Sq-pow(sum2,2)/len(v2)))

if den == 0: return 0
return 1.0-num/den     #相似值越大,表示距离越小

#聚类节点,left riight相当于二叉树定义中的两个左右孩子指针
class bicluster:
def __init__(self,vec,left=None,right=None,distance=0.0,id=None):
self.left=left
self.right=right
self.vec=vec
self.id=id
self.distance=distance

#之前构造的数据文件,经过readfile转化后,每行代表一篇博客,每行看做一个聚类前的原始节点,然后
#开始计算每个节点之间的距离,并根据距离构造聚类关系的二叉树
def hcluster(rows,distance=pearson): #这里注意,函数的第二个参数为函数(理解为c语言中的函数指针)
distances={}  #定义一个字典,记录各个节点亲密度 。键为两个节点的ID,值为两个节点的距离
currentclustid=-1   #

#针对每一行rows[i],将其创建为一个bicuster的一个实例,其ID为行号。vec是个列表类型,代表一行的数据. clust为各行数据实例化后的集合
clust=[bicluster(rows[i],id=i) for i in range(len(rows))]

while len(clust)>1:   #逐步把clust这个列表 集合构造成 聚类的二叉树形式,最后只剩一个根节点
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)): #针对i后面的N行数据
if (clust[i].id,clust[j].id) not in distances:
distances[(clust[i].id,clust[j].id)]=distance(clust[i].vec,clust[j].vec)   #计算所有 节点(每行博客数据)的亲密度,并保存在字典distances中
d=distances[(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=currentclustid)

currentclustid -=1

del clust[lowestpair[1]]
del clust[lowestpair[0]]
clust.append(newcluster) # 将新节点追加到列表中
return clust[0]  #返回最后的根节点

#打印聚类后的数据
def printclust(clust,labels=None,n=0):
for i in range(n): print ' ',
if clust.id<0:
print '---'
else:
if labels == None: print clust.id
else: print labels[clust.id]
if clust.left != None: printclust(clust.left,labels=labels,n=n+1)
if clust.right != None: printclust(clust.right,labels=labels,n=n+1)

if __name__ =='__main__':
blogname,words,data=readfile("blogdata.txt")

clust=hcluster(data)
printclust(clust,blogname)


其中上述代码,在readfile接口中处理后,得到的data数据为:总共五个元素,每个元素为一行数据

[[4.0, 0.0, 6.0, 2.0, 0.0, 0.0, 2.0, 10.0, 2.0, 0.0, 0.0, 12.0, 0.0, 0.0, 15.0,

3.0, 0.0, 0.0, 2.0, 0.0, 16.0, 6.0, 0.0, 0.0, 0.0, 2.0, 0.0, 8.0, 0.0, 0.0], [0.

0, 3.0, 2.0, 0.0, 20.0, 0.0, 0.0, 1.0, 0.0, 0.0, 2.0, 0.0, 2.0, 4.0, 0.0, 0.0, 5

.0, 1.0, 0.0, 0.0, 0.0, 0.0, 3.0, 0.0, 4.0, 2.0, 3.0, 1.0, 3.0, 0.0], [2.0, 0.0,

0.0, 4.0, 144.0, 11.0, 4.0, 0.0, 11.0, 6.0, 2.0, 6.0, 2.0, 0.0, 3.0, 0.0, 0.0,

31.0, 9.0, 2.0, 13.0, 17.0, 1.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 8.0], [0.0, 0.0,

0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,

0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 2.0, 0.0, 0.0

, 0.0, 4.0, 0.0, 4.0, 0.0, 3.0, 1.0, 0.0, 0.0, 2.0, 0.0, 2.0, 4.0, 4.0, 0.0, 1.0

, 0.0, 0.0, 3.0, 4.0, 8.0, 0.0, 3.0, 2.0, 3.0, 2.0]]

程序运行结果

---

摇摆少年梦的技术博客

---

郭霖的专栏

---

yxyhack's blog

---

anzhsoft的技术专栏

雷霄骅(leixiaohua1020)的专栏

看起来最后两个相似度很高的,实际并不高。这就是因为在前一篇中所描述的,数据源构造出现的问题。毕竟咱们是中文的帖子。

总结

算法耗时。后面还有其他的聚类算法,诸如K-means等。聚类是无监督学习,主要是在数据中寻找某种结构(关联?)、将数据分成不同的群组。主要需要关注:相关度算法、聚类算法。后续会采用其他数据源,并采用不同的相关度算法计算数据之间的亲密度,最终用分级聚类展示,例如可以采取某些人关注的某些书,进而建立人和书的二维关系,而后聚类。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: