码迷,mamicode.com
首页 > 其他好文 > 详细

《集体智慧编程》 第三章 发现群组 笔记儿

时间:2016-03-19 10:03:59      阅读:1131      评论:0      收藏:0      [点我收藏+]

标签:

啦啦啦聚类算法~

我刚看到这一章的时候内心是崩溃的,许多傻瓜软件点一下鼠标就能完成的事儿,到书里这一章需要许多行代码来完成,也说明了,学数据挖掘,算法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

(1)
(5)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!