标签:
啦啦啦聚类算法~
我刚看到这一章的时候内心是崩溃的,许多傻瓜软件点一下鼠标就能完成的事儿,到书里这一章需要许多行代码来完成,也说明了,学数据挖掘,算法real重要。。
本章需要安装:
feedparser(第二章安装pydelicious已经安装过了,pip install即可)
BeautifulSoup,我也不记得自己是怎么装上的了,据说可以easy_install,大家试一试。
PIL,下载地址:http://pythonware.com/products/pil/
还涉及到一些正则表达式的知识,非常非常强烈推荐下面这个教程,写得很好:
www.cnblogs.com/huxi/archive/2010/07/04/1771073.html
一.监督学习和无监督学习
https://www.zhihu.com/question/23194489
二.单词向量
(一)对博客用户进行分类
(二)对订阅源中的单词进行计数
#基础导入 import feedparser #用来解析RSS订阅源(XML文档),可以就从RSS或Atom订阅源中得到标题链接和文章的条目了 import re #正则表达式
# 返回一个RSS订阅源的标题和包含单词计数情况的字典 def getwordcounts(url): # 解析订阅源 d=feedparser.parse(url) #传入的是博客的rss地址,这时候rss的全部内容就都在d里面了 wc={} # 遍历所有文章条目 for e in d.entries: #d.entries:文章条目 if ‘summary‘ in e: summary=e.summary else: summary=e.description #summary=文章内容 # 提取一个单词列表 words=getwords(e.title+‘ ‘+summary) #getwords(题目+空格+文章) for word in words: wc.setdefault(word,0)#如果键在字典中,返回这个键所对应的值。如果键不在字典中,向字典中插入这个键,并且以default为这个键的值,并返回 default。default的默认值为None wc[word]+=1 #得到字典wc类似{u‘limited‘: 1, u‘all‘: 5, u‘searchable‘: 1, u‘results‘: 1, u‘browsers‘: 2} return d.feed.title,wc #返回 博客订阅源,字典wc
def getwords(html): #去除所有HTML标记:<XXXXXXX> txt=re.compile(r‘<[^>]+>‘).sub(‘‘,html) #re.compile(pattern[, flags])作用:把正则表达式语法转化成正则表达式对象。r是raw(原始)的意思。因为在表示字符串中有一些转义符,如表示回车‘\n‘。如果要表示\表需要写为‘\\‘。但如果我就是需要表示一个‘\‘+‘n‘,不用r方式要写为:‘\\n‘。但使用r方式则为r‘\n‘这样清晰多了。 #re.sub(pattern, repl, string, count=0, flags=0) # 利用非字母字符拆分出单词 split()通过指定分隔符对字符串进行切片 words=re.compile(r‘[^A-Z^a-z]+‘).split(txt) # 转换成小写模式 return [word.lower() for word in words if word!=‘‘]
apcount={} #出现某单词的博客数目 wordcounts={} feedlist=[line for line in file(‘feedlist.txt‘)] #建立一个包含feedlist.txt中每一个url的列表 for feedurl in feedlist: try: title,wc=getwordcounts(feedurl) #title,wc类似Google Blogoscoped {u‘limited‘: 1, u‘all‘: 5, u‘searchable‘: 1, u‘results‘: 1, u‘browsers‘: 2} wordcounts[title]=wc #得到wordcounts类似{u‘Google Blogoscoped‘: {u‘limited‘: 1, u‘all‘: 5, u‘searchable‘: 1, u‘results‘: 1, u‘browsers‘: 2} for word,count in wc.items(): #items()方法返回字典的(键,值)元组对的列表;wc.items=[(词汇,计数),(词汇,计数)] ‘‘‘得到: 词汇 计数 词汇 计数‘‘‘ apcount.setdefault(word,0) #此时 apcount={word:0} if count>1: apcount[word]+=1 #得到apcount类似{u‘limited‘: 0, u‘all‘: 1, u‘searchable‘: 0, u‘results‘: 0} except: print ‘Failed to parse feed %s‘ % feedurl
wordlist=[] for w,bc in apcount.items(): #apcount.items()类似[(u‘limited‘, 0), (u‘all‘, 1), (u‘searchable‘, 0), (u‘results‘, 0)] frac=float(bc)/len(feedlist) #变成浮点数算除法不然结果不精确 if frac>0.1 and frac<0.5: wordlist.append(w) #wordlist=[‘limited‘,‘all‘,‘searchable‘]
out=file(‘blogdata1.txt‘,‘w‘) out.write(‘Blog‘) for word in wordlist: out.write(‘\t%s‘ % word) #‘\t‘是tab out.write(‘\n‘) for blog,wc in wordcounts.items(): print blog out.write(blog) for word in wordlist: if word in wc: out.write(‘\t%d‘ % wc[word]) else: out.write(‘\t0‘) out.write(‘\n‘)
ps.最后会得到blogdata.txt文件,效果如下图(我节选了一部分),不想进行这一步的同学可以直接找我要数据23333
用excel打开的效果
三.分级聚类
分级聚类的概念在P34,写得很清楚啦。
本节我们将示范如何对博客数据集进行聚类,以构造博客的层级结构;如果构造成功,我们将实现按主题对博客进行分组。
(一)加载数据文件
##加载数据文件 def readfile(filename): lines=[line for line in file(filename)] #加载的是blogdata.txt的话,lines=[‘blog\tword\tword...‘,‘blogname\t词频\t词频...‘,...] colnames=lines[0].strip().split(‘\t‘)[1:] #colnames列标题,按\t进行切分 #加载的是blogdata.txt的话,colnames=[‘blog‘,‘word‘,‘word‘,...] rownames=[] #即将填入行标题的空列表 data=[] #即将填入计数值的空列表 for line in lines[1:]: p=line.strip().split(‘\t‘) ‘‘‘加载的是blogdata.txt的话, p=[‘blogname‘,‘xx‘,‘xx‘,...] [‘blogname‘,‘xx‘,‘xx‘,...] ...‘‘‘ rownames.append(p[0]) ‘‘‘加载的是blogdata.txt的话, p[0]=blogname blogname ...‘‘‘ data.append([float(x) for x in p[1:]]) return rownames,colnames,data ‘‘‘上述函数将数据集中的头一行数据读入了一个代表列名的列表, 并将最左边的一列读入了一个代表行名的列表, 最后它又将剩下的所有数据都放入一个大列表,其中每一项对应于数据集中的一行数据。‘‘‘
(二)定义紧密度
第二章已经有讲到了,这儿直接把代码粘过来,用的是皮尔逊相关性度量。
from math import sqrt def pearson(v1,v2): # Simple sums sum1=sum(v1) sum2=sum(v2) # Sums of the squares sum1Sq=sum([pow(v,2) for v in v1]) sum2Sq=sum([pow(v,2) for v in v2]) # Sum of the products pSum=sum([v1[i]*v2[i] for i in range(len(v1))]) # Calculate r (Pearson score) num=pSum-(sum1*sum2/len(v1)) den=sqrt((sum1Sq-pow(sum1,2)/len(v1))*(sum2Sq-pow(sum2,2)/len(v1))) if den==0: return 0 return 1.0-num/den
(三)新建bicluster类,将所有属性村反给其中,并以此来描述层级树
class bicluster: #定义一个bicluster类,将每一篇博客看成是一个对象,为此定义一个类。 #分级聚类算法中的每一个聚类,可以是树中的枝节点,也可以是叶节点。每一个聚类还包含了只是其位置的信息,这一信息可以是来自叶节点的行数据,也可以是来自枝节点的经合并后的数据 #我们可以定义一个bicluster类,将所有这些属性存放其中,并以此来描述这颗层级树 def __init__(self,vec,left=None,right=None,distance=0.0,id=None): self.left=left self.right=right #每次聚类都是一堆数据,left保存其中一个,right保存另一个 self.vec=vec#代表该聚类的特征向量,保存两个数据聚类后形成新的中心 self.id=id#用来标志该节点是叶节点还是内部节点,如果是叶节点,则为正数,如果不是叶节点,则为负数。 self.distance=distance#表示合并左子树和右子树时,两个特征向量之间的距离。
(四)hcluster算法
书P35最下方有介绍:
分级聚类算法以一组对应于原始数据项的聚类开始。函数的主循环部分会尝试每一组可能的配对并计算它们的相关度,以此来找出最佳配对。最佳配对的两个聚类会被合并成一个新的聚类。新生成的聚类中所包含的数据,等于将两个旧聚类的数据求均值之后得到的结果。这一过程会一直重复下去,直到只剩下一个聚类为止。由于整个计算过程可能会非常耗时,所以不妨将每个配对的相关度计算结果保存起来,因为这样的计算会反复发生,直到配对中的某一项被合并到另一个聚类中为止。
####hcluster算法(hierarchical cluster) def hcluster(rows,distance=pearson): distances={} currentclustid=-1 #最开始的聚类就是数据集中的行 clust=[bicluster(rows[i],id=i) for i in range(len(rows))] #此时 clust=[bcluster(rows[1],id=1),bcluster(rows[2],id=2),...] while len(clust)>1: ‘‘‘while 判断条件: 执行语句……‘‘‘ #Python 编程中 while 语句用于循环执行程序,即在某条件下,循环执行某段程序,以处理需要重复处理的相同任务。 lowestpair=(0,1) #lowestpair为距离最近的两个id closest=distance(clust[0].vec,clust[1].vec) #先计算第一第二行的相关度,赋值给closest,此时lowestpair=(0,1) # 遍历每一个配对,寻找最小距离 for i in range(len(clust)): for j in range(i+1,len(clust)): #遍历,使得i不等于j # 用distances来缓存距离的计算值 if (clust[i].id,clust[j].id) not in distances: distances[(clust[i].id,clust[j].id)]=distance(clust[i].vec,clust[j].vec) d=distances[(clust[i].id,clust[j].id)] if d<closest: closest=d lowestpair=(i,j) # 计算两个聚类的平均值 # 将找到的距离最小的簇对合并为新簇,新簇的vec为原来两个簇vec的平均值 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) # 不在原始集合中的聚类,其id为负数 #id:如果是叶节点,则为正数,如果不是叶节点,则为负数。 currentclustid-=1 del clust[lowestpair[1]] del clust[lowestpair[0]] #删除聚在一起的两个数据 #del用于list列表操作,删除一个或连续几个元素 clust.append(newcluster) return clust[0] #返回最终的簇
(五)检视执行结果P37
为了检视执行结果,我们可以编写一个简单的函数,递归遍历聚类树,并将其以类似文件系统层级结构的形式打印出来。
def printclust(clust,labels=None,n=0): ‘‘‘参数解释:本例中,labels=blognames clust:层次遍历最后输出的一个簇 n:在本例中代表树的层数‘‘‘ # 利用缩进来建立层级布局 for i in range(n): print ‘ ‘, #n代表当前遍历的层数,层数越多,前面的空格越多 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)
成果在书上P37最下方
(六)绘制树状图
基础导入
from PIL import Image,ImageDraw
首先,需要利用一个函数来返回给定聚类的总体高度。
如果聚类是一个叶节点,其高度为1,;否则,高度为所有分支高度之和。
标签:
原文地址:http://www.cnblogs.com/zzhzhao/p/5278492.html