“滚滚长江东逝水,浪花淘尽英雄”。近来读《三国演义》,忽然想看看到底哪位英雄在书中提到的最多,于是就想用分词算法实现一下。
网上也确实有相关的案例,作为参考,自己又重写并优化了一遍。
思路
- 下载《三国演义》txt文档
- 使用jieba分词算法对文档进行分词处理
- 将分词结果踢除停用词、标点符合、非人名等
- 词频统计、并排序
- 可视化展示
问题
按照上面的思路进行简单实施时,查看结果会发现几个问题
- 名字
- 三国人物有名、字、号等,还有其他的一些别称,如“相父”、“曹阿瞒“、刘皇叔”,要想办法统一成一个人
- 词性
- 比如“曰”、“大胜”等非人名的词不是我们需要统计的
- 分词
- 一些如“孔明曰”、“玄德问”、“操大怒”之类的词没有被分割开
- 干扰词
- 分词后会出现像“诸公”、“齐声”、“班师回”等明显,但是算法无法判断的非人名词语,干扰排序结果
所以对这些情况要进行特殊处理。
优化
- 针对名、字、号的问题进行枚举判断。网上的案例中大多采用如
w.word == '玄德'
这种等号判断,但对于 “玄德遂”、“操乃”等分词不准确情况枚举不足。比如我们在文本里统计“操”字有2800多个,基本都是指代曹操,而用阿瞒、孟德、曹丞相等枚举等号判断时,最终统计的“曹操”词频只有1000多个,所以我采用的是包含判断,即'操' in w.word
、'孟德' in w.word
。 - 词性踢除。通过w.flag将nr(人名)外的其他词性踢除掉,这一步很多案例中是放在人物名字号的判断前,但是“玄德”会被当成x(未知词性),这样就会少算很多姓名,所以我放在了人物名字判断后,再踢除无关词性。
- 对于词性为nr但是非人名的词,则需要在停用词的基础上,增加一张踢除干扰词的自定义文件
结果
经过以上处理后,基本排除了干扰,最后就是激动人心的时刻,那么谁才是罗贯中心中的南波万呢?
当当当当,那就是这位“可爱的奸雄”——曹操。
曹操:3014
刘备:2689
诸葛亮:2103
关羽:1047
吕布:868
周瑜:660
司马懿:643
袁绍:544
张飞:505
鲁肃:465
赵子龙:433
马超:358
孙权:351
董卓:342
魏延:322
姜维:302
黄忠:190
刘表:156
庞德:122
张辽:117
再来一张拉风的三国词云
说明:该处理结果只对排名靠前的刘备、诸葛亮、曹操、关羽、张飞、吕布、司马懿等人物的分词结果进行了优化(见代码),其他诸如魏延、庞统、庞德等不影响第一排名的人物,名、字、号问题未进行详细处理。
附代码
import jieba.posseg as pseg
import jieba
import re
import matplotlib.pyplot as plt
import codecs
# 词云
import wordcloud
import imageio
# 定义主要人物的个数
keshihuaTop=10 # 可视化人物图人数
mainTop = 100 # 人物词云图人物数
peopleTop=10 # 人物关系图
# 获取图书数据
def get_book(file_path):
fn = open(file_path,encoding='utf-8')
stringdata = fn.read()
fn.close()
return stringdata
# 文本处理
def bookdata_process(bookdata):
pattern = re.compile(u'\t|\n|\.|-|:|;|\)|\(|\?|"')
book_str = re.sub(pattern,'',bookdata)
print('文本预处理完成')
return book_str
# 获取停用词
def stop_word_list(file_path):
stopwords = [line.strip() for line in open(file_path,'r',encoding='utf-8').readlines()]
return stopwords
# 获取词频
count = {
}
# 获取词频
def get