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

本文介绍数据聚类概念,通过分析博客CSS订阅话题和社区意愿,利用聚类算法对博客用户和词汇进行分组。内容包括单词向量构建、分级聚类、距离度量和树状图可视化,展示了从复杂数据集中提取群组结构的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

发现群组(数据聚类)

对第二章的想法,加以拓展,引入“数据聚类”(data clustering)的概念。本章主要涉及以下内容

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

本文涉及两个例子:
1、对博客CSS订阅话题,根据涉及的词汇对博客分组;对词汇的用法,对词汇分组;
2、对社区网站考察,获取人们已拥有或者希望拥有的物品,对人们的意愿进行分组。


一、单词向量

1、 对博客用户进行分组

目的:一组指定的词汇在每个博客订阅源中出现的次数。根据单词出现的频度进行聚类,尝试分析出具有相似主题或者写作风格的博客用户。

代码块
..生成generatefeedvector.py


import feedparser  # 解析RSS的一个包
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 = getword(e.title+' '+summary)  # getword(题目+空格+文章)
        for word in words:
            wc.setdefault(word,0)  # 如果键在字典wc中,返回这个键对应的值,如果不在字典中,则添加键到字典中,并将键对应的值默认为0
            wc[word] += 1  # 用于统计单词出现的次数
    return d.feed.title,wc

   # 函数getwordcounts 将摘要传给函数getwords,后者会将其中所有的HTML标记剥离
   # 并以非字母字符作为分隔符拆分出单词,再将结果以列表的形式返回


def getword(html):
    # 去除所有HTML标记
    txt = re.compile(r'<[^>]>').sub('',html)
    # 利用所有非字母字符拆分出单词,split()通过指定分隔对字符串进行切片
    words = re.compile(r'[^A-Z^a-z]+').split(txt)
    # 转化成小写的形式
    return [word.lower() for word in words if word!='']


# 代码的第一部分  遍历文件中的每一行url地址,然后生成针对每个博客的单词统计,
# 以及出现这些单词的博客数目(apcount)

apcount = {}
wordcounts = {}
feedlist = [line for line in open('feedlist_china.txt')]  # 注意路径原为file,改为open
for feedurl in feedlist:
    try:
        title, wc = getwordcounts(feedurl)  # title,wc 类似Google blogoscoped {u'limited':1, u'all':5, u'research':6}
        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.item=[(词汇,计数),(词汇,计数)]
            apcount.setdefault(word, 0)  # 此时 apcount={word, 0}
            if count > 1:
               apcount[word] += 1
    except:
        print('Failed to parse feed %s' % feedurl)


# 建立一个单词列表,将其实际用于针对每个博客的单词计数
# 将10% 定为下界,50%定为上界
wordlist = []
for w, bc in apcount.items():  # apcount.items()类似于[(u'limited', 0), (u'all', 1), (u'searchable', 0)]
    frac = float(bc)/len(feedlist)  # 变成浮点算法,不然结果不准确
    if frac>0.1 and frac<0.5: wordlist.append(w)  # wordlist = ['limited', 'all', 'searchable']

# 最后我们利用上述单词列表和博客列表来建立一个文本文件,包含对每个博客所有单词的统计情况
out = open('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])
    out.write('\n')

以上代码生成如下格式数据:

blog word1 word2
blog1 1 2
blog2 3 3

2、分级聚类

分级聚类是通过连续不断地将最为相似的群组两两合并,来构造一个群组的层级结构。
层级聚类过程


以下代码实现对博客数据集进行聚类,以构造博客的层级结构。

# clusters.py
def readfile(filename):
    lines = [line for line in open(filename)]
#lines example
# [blogname, word1, word2, ...
#  A,        5,     6,     ...
#  B,        3,     1,     ...
#  C,        2,     0,     ...]

# 第一行是列标题
# 加载blogdata.txt的话,lines=['blog\w1\w2\w3\...','blogname\w1词频\w2词频\w3词频',...]
    colnames=lines[0].strip().split('\t')[1:]  # 从第二列始 获取博客总的词条
    # strip() 移除字符串头尾指定的字符

# columns 是按照\t进行切割
    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

#blognames, words, data=readfile('blogdata.txt')

rownames,colnames,data=readfile('blogdata.txt')

'''
定义紧密度:由于不同博客包含的文章条目不一,用皮尔逊相关系数可以修正这个问题,
    因为它判断的是两组数据与某条直线的拟合程度。'''

创建pearson函数,用于计算两列数组的相关系数

# 以下计算代码接受两个数字列表作为参数,返回这两个列表的相关度分值
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)))
    if den==0: 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值