Python实验——文本词频获取与统计、词云图生成

题目要求

自选小说,统计该小说中人物及其出场的次数,并生成对应的词云图。

注意事项:1、使用python及相关的库完成实验;2、统计次数中去掉非人物名字的词;3、统计次数中去掉单个字;4、同一人物有多个别称的合并为统一的词。


解题思路

这次我选取的文本为《三国演义》原著。三国我是读过几次的,对相关的文化内容也都比较感兴趣,因此遇到这个课题时,第一个预选方案就是它。不过后来也是遇到了些困难,因为它的文本量非常大,算法时间复杂度带来的影响也随之变得很严重,这在测试算法时耗费了我大量的时间。

最开始我的想法很简单,提取文中的人名,统计数目,生成词云图,想当然的事情。然而开工后发现并没有那么简单,可能是我的水平不足,一些很基础的问题我都忽略掉了。在设计程序时,遇到很多障碍,设计的思路也是改动了很多次。

这里我就简单介绍一下最终的程序设计思路:1、提取全部文本内容并进行分词处理,将文本变成由若干分词组成的列表;2、遍历所有分词检验每一个分词是否为人物名称或者人物别名,根据情况计算统计人物出场次数;3、根据人物及其出场次数的数据表生成词云图。


实验步骤

一、原文的获取与加工

在网上寻找资料,获取《三国演义》原文电子版,使用open方法读取文本,保存为字符串。

原文文本内容如下:

  • 通过re库的sub方法对文本进行初步处理,去除全部非文字字符,然后使用jieba库的cut方法对文本字符串进行分词处理,得到由若干分词(字词)组成的“文本分词”迭代生成器对象。

import re
import jieba

# 读取小说文本
with open('实验6/Sanguo.txt', 'r', encoding='utf-16') as file:
    novel = file.read()

# 去除文本中的标点符号和空白字符(只保留文字)
novel = re.sub(r'[^\w]', '', novel)

# 对文本进行分词后重新编排查看
tokens = jieba.cut(novel)
str1 = ",".join(tokens)
with open('python文件/实验6/tokens.txt', 'w', encoding='utf-8') as file:
    file.write(str1)

查看重新编排后的内容:

  • *通过collections库的Counter方法统计频次。该方法的作用是统计每一个分词出现的次数并按照出现的次数降序排序,以键值对的形式输出。这一步并非关键步骤,目的只是为了获得一个数据参考,因为仅使用如此方法会面临许多问题:非人名字词被错误归类、人名及其别称无法统一归类等等。

from collections import Counter

# 使用Counter方法统计频次
counter = Counter(str1.split(','))

with open('实验6/counter.txt', 'w', encoding='utf-8') as file:
    file.write(str(counter))

查看Counter统计的词频:

由此可以看出,单纯使用Counter方法计算得出的数据的确存在以上那些问题,需要其他的方法来完成题目要求。

二、创建原名列表及别称字典

  • 在网上寻找资料,获取“三国演义中全部出场人物”以及“三国演义中主要人物的别称(别称包括表字与约定俗成的一些称呼,以下相同)”的相关内容,获取所需的字段后使用正则表达式加工处理,获得“人名原名列表”以及“人物原名别称对照表”,分别以列表和json格式保存。

这一步的关键在于如何获取有价值的资料并进行处理,转换为程序容易识别的形式。我在网上找到的原始资料是这样的:

我们需要并且只需要的是所有人物原名的列表。观察原始资料,可以看出,出现人物原名的上下文形式为“数字+英文逗号+人物原名+英文逗号”,使用python的re库,通过正则表达式完成人名的提取:

import re

# 读取原始人物名称文件
with open('实验6/originalData.txt', 'r', encoding='utf-8') as file:
    originalData = file.read()

# 先将文件中的换行符去掉
pattern = re.compile(r'\n\n')
Data2 = pattern.sub(';', originalData)
originalData2 = ''.join(Data2)

# 通过正则匹配全文到对应字段然后取出需要的内容
pattern2 = re.compile(r'\d+,([\u4e00-\u9fff]+),?')
originalData3 = pattern2.findall(originalData2)

# 用“,”将全部人名连接起来输出到文件中
nameList = ','.join(originalData3)

with open('实验6/nameList.txt', 'w', encoding='utf-8') as file:
    file.write(nameList)

运行以上代码的输出结果如下:

对于“人物原名别称对照表”,我们的目的是建立一个字典,包含主要的人物别称原名的键值对,方法与上面获取人名列表基本一致,在此不再赘述,只展示最后结果:

获得以上两组数据并处理好规范后,下面就可以利用它们去更精确地统计频次了。

三、统计人物出场频次

  • 在主程序中引入以上内容“文本分词”、“人名原名列表”以及“人物原名别称对照表”,再自定义一个函数,用于将别称(如果当前分词是人物的别称)替换为人物原名,若在别称对照表中查到对应原名则返回对应原名,否则返回本值。

转换函数很简单,如下所示:

def nameTrans(str):
    try:
        str2 = otherNameDict[str]    #otherNameDict为“人物原名别称对照表”字典
        return str2
    except KeyError:
        return str

接下来是统计计数的主要算法。

  • 算法思路:首先建立一个人名字典,存储文章中出现的所有人物(包含原名和别称)及其出现次数。遍历每一个分词,在每一个分词下,分别遍历人物原名列表人名别称字典。1.对于每一个人物原名,若该名称包含于当前分词中,则令该人物在字典中对应的值加一(若当前人物不存在于人名字典中则在字典中创建一个以该人名为键的键值对);2.遍历结束后(没有跳出遍历说明当前分词不包含任何人物原名),开始遍历人名别称字典,首先通过处理人名别称的函数转化该分词,对于每一个人名别称,若该别称包含于当前分词中,则令该别称对应的人物在人名字典中对应的值加一(若当前人物不存在于人名字典中则在字典中创建一个以该人名为键的键值对)。所有分词遍历完毕之后,对人名字典进行排序处理,以对应值为键进行降序排序,可以得到三国人物及其出场次数从高到低排列的字典,再将其处理一下生成“三国演义人物出场排序表”文本文件。

代码如下:

# 创建人名字典
nameDict = {}

# 遍历所有分词
for token in tokens:
    for name in nameList:
        if name in token and name in nameDict:
            nameDict[name] += 1
            break
        elif name in token and name not in nameDict:
            nameDict[name] = 1
            break
    for name2 in otherNameDict:
        nameT = nameTrans(name2)
        if name2 in token and nameT in nameDict:
            nameDict[nameT] += 1
            break
        elif name2 in token and nameT not in nameDict:
            nameDict[nameT] = 1
            break

# 将统计结果进行降序排序
topNames = sorted(nameDict.items(), key=lambda x : x[1], reverse=True)

# 将最终结果输出到文本文件里
with open('实验6/topnames.txt', 'w', encoding='utf-8') as file:
    file.write(str(topNames))

运行结果如下:

四、生成词云图

  • 引入上面生成的“三国演义人物出场排序表”,经过处理将其转化为字典格式的数据,导入wordcloud库,通过WordCloud的generate_from_frequencies方法以该字典为数据生成词云图,修改WordCloud的font_path参数使生成的词云图适配中文字体,修改width和height调整生成的词云图图像大小使其更清晰。导入matplotlib.pyplot库生成图像。


存在问题

首先是词库不完整。我只通过人工检查的方式添加了出现频次较高的一些其他称谓,疏漏在所难免。此外部分有歧义的字词我也没有细分,如“操”可能指代“曹操”也可能指代“凌操”,虽然很明显前者可能性更大一些,但类似的情况仍有许多。还有就是人物重名的情况,虽热比较少见但是并没有相应的解决方案。


总结反思

做这个实验还是花了我挺长时间的,主要是一开始没想到会这么复杂。此外,我在完成这个课题时参考了许多AI的问答,这也是我第一次尝试运用AI帮助我完成某项任务(AI都火了这么久了,有点没跟上时代)。感觉还是蛮震惊的,没想到AI的效率与实用性已经这么高了。冠冕堂皇的话语就不说了,我实在配不上。但是还是想提一下“在问”网站,对我的帮助特别大(主要就是给不会翻墙的朋友提供一个方便使用GPT的方式)。贴一下在问的网页链接,各位有需要用到AI的朋友可以用一下在问的网站:在问 | 让知识无界,智能触手可及 (zaiwen.top)https://www.zaiwen.top/#/

本篇博文用到的相关资料与代码上传到了度盘,有需要的朋友可自取。

链接:https://pan.baidu.com/s/17PcNLa1hUb2qt3QcrTdDRA?pwd=auto 
提取码:auto 

本人刚开始学python,技术水平不足,一些问题也没能解决。对文章内容有疑惑的朋友可以在评论区或者私信与我交流,疏漏或错误也还请多多指教,在此不胜感激。

  • 16
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值