集体智慧编程----第三章发现群组


这章中的主要内容:

  • 从各种不同的来源中构造算法所需的数据
  • 两种不同的聚类算法;
  • 更多有关距离度量(distance metrics)的知识
  • 简单的图形可视化代码,用以观察所生成的群组
  • 如何将异常复制的数据集投影到二维空间中

监督学习和无监督学习

利用样本输入和期望输出来学习如何预测的技术被称为监督学习法(supervised learning methods)。例如神经网络、决策树、向量支持机、贝叶斯过滤。当我们想要利用这些方法中的任何一种来提取信息时,我们可以传入一组输入,然后期望应用程序能够根据其此前学到的知识来产生一个输出。

聚类是无监督学习。与神经网络或决策树不同,无监督学习算法不是利用带有正确答案的样本数据进行“训练”。它们的目的是要在一组数据中找寻某种结构,而这些数据本身不是我们要找的答案。

对博客用户进行分类

对订阅源中的单词进行计数

对wc={}的定义不解,原来是python中的字典类型的定义。这篇文章有讲解,关于feedparser的用法解释
RSS订阅源是一个包含博客及其所有文章条目信息的简单XML文档。为了给每个博客中的单词技术,首先第一步就是要解析这些订阅源

import feedparser
import re

#返回一个RSS订阅源的标题和包含单词统计情况的字典
def getwordcounts(url):
    #解析订阅源
    d=feedparser.parse(url)
    wc={
   }

    #循环遍历所有的文章条目
    for e in d.entries:
        if 'summary' in e: summary = e.summary
        else: summary=e.description

        #提取一个单词列表
        words=getwords(e.title+' '+summary)
        for word in words:
            wc.setdefault(word,0)
            wc[word]+=1
        return d.feed.title,wc

每个RSS和Atom订阅源都会包含一个标题和一组文章条目。通常,每个文章条目都有一个摘要,或者是包含了条目中世纪文本的描述性标签。函数getwordcounts将摘要传给函数getwords,后者会将其中所有的HTML标记剥离掉,并以非字母字符作为分隔符拆分出单词,再将结果以列表的形式加以返回。

generatefeedvector.py文件中的主体代码(这些代码不单独构成一个函数)循环遍历订阅源并生成数据集。代码的第一部分遍历feedlist.txt文件中的每一行,然后生成针对每个博客的单词统计,以及出现这些单词的博客数目(apcount)。

apcount={
   }
wordcounts={
   }
feedlist=[line for line in open('data/feedlist.txt')]
for feedurl in feedlist:
    title,wc=getwordcounts(feedurl)
    wordcounts[title]=wc
    for word,count in wc.items():
        apcount.setdefault(word,0)
        if count>1:
            apcount[word]+=1

下一步,建立一个单词列表,将其实际用于针对每个博客的单词计数。因为想“the"这样的单词几乎到处都是,而像”flim-flam"这样的单词则有可能只出现在个别博客中,所以通过只选择介于某个百分比范围内的单词,我们可以减少需要考查的单词总量。在本例中,我们可以将10%定为下届,将50%定为上届,不过假如你发现有过多常见或鲜见的单词出现,不妨尝试不同的边界值。

wordlist=[]
for w,bc in apcount.items():
    frac=float(bc)/len(feedlist)
    if frac>0.1 and frac<0.5: wordlist.append(w)

最后,我们利用上述单词列表和博客列表来建立一个文本文件,其中包含一个大矩阵,记录着针对每个博客的所有单词的统计情况:

out=open('data/blogdata.txt','w')
out.write('Blog')
for word in wordlist: out.write('\t%s' % word)
out.write('\n')
for blog,wc in wordcounts.items():
    out.write(blog)
    for word in wordlist:
        if word in wc: out.write('\t%d' % wc[word])
        else: out.write('\t0')
    out.write('\n')

网络问题feedparser的访问没有返回值,还好网上能找到对应的数据,之后会放到GitHub中。

大矩阵的大概样子就是这样的

分级聚类

分级聚类通过连续不断地将最为相似地群组两两合并,来构造一个群组地层级结构。其中地每个群组都是从单一元素开始地,在本章地例子中,这个单一元素就是博客。在每次迭代地过程中,分级聚类算法会计算每两个群组间地距离,并将距离最近的两个群组合并成一个新的群组。这一过程会一直重复下去,知道只剩一个群组为止。

待分级聚类完成之后,我们可以采用一种图形化的方式来展现所得的结果,这种图被称为树状图
在这里插入图片描述
我们将示范如何对博客数据集进行聚类,以构造博客的层级结构;如果构造成功,我们将实现按主题对博客进行分组。首先,我们需要一个方法来加载数据文件。新建一个名为clusters.py的文件

def readfile(filename):
    lines=[line for line in open(filename)]

    #第一行是列标题
    colnames=lines[0].strip().split('\t')[1:]
    rownames=[]
    data=[]
    for line in lines[1:]:
        p=line.strip().split('\t')
        #每行的第一列是行名
        rownames.append(p[0])
        #剩余部分就是该行对应的数据
        data.append([float(x) for x in p[1:]])
    return rownames,colnames,data

上述函数将数据集中的头一行数据读入一个代表列名的列表,并将最左边一列读入一个代表行名的列表,最后它又将剩下的所有数据都放入一个大列表,其中的每一项对应于数据集中的一行数据。数据集中任一单元格内的计数值,都可以由一个行号和列号来唯一定位,此行号和列号同时还对应于列表rownames和colnames中的索引。

在本章的例子中,一些博客比其他博客包含更多的文章条目,或者文章条目的长度比其他博客的更长,这样会导致这些博客在总体上比其他博客包含更多的词汇。皮尔逊相关度可以纠正这一问题,因为它判断的其实是两组数据与某条直线的拟合程度。此处,皮尔逊相关度的计算代码将接受两个数字列表作为参数,并返回这两个列表的相关度分值:

from math import sqrt
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(Pearson score)
    num=pSum-(sum1*sum2/len(v1))
    den=sqrt((sum1Sq-pow(sum1,2)/len(v1))*(sum2Sq-pow(sum2,2)/len(v1))
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值