忽然想知道三国中到底谁是主角,简单使用 Python 分析一下看看。最后画一个词云展示结论。
首先导入必要的包:
import jieba
from collections import Counter
from wordcloud import WordCloud
然后导入需要的电子版三国,储存在 txt 对象中,但是如果直接这样调用的话十分占用内存,我们采用一个可迭代对象分次导入。
关于这方面的说明详见:https://www.jianshu.com/p/968550b63f34
# 直接导入
with open('threekingdom.txt' ,'r', encoding='utf-8') as f:
txt = f.read()
# 使用可迭代对象分次导入
def read_file(fpath):
BLOCK_SIZE = 1024
with open(fpath, 'r', encoding='utf-8') as f:
while True:
block = f.read(BLOCK_SIZE)
if block:
yield block
else:
return
txt = read_file('threekingdom.txt')
我先检查一下是否导入成功
txt.__next__()
这个输出还是很多截取了部分在这里:
'\n------------\n\n正文\n\n\n------------\n\n第一回 宴桃园豪杰三结义 斩黄巾英雄首立功\n\n 滚滚长江东逝水浪花淘尽英雄。是非成败转头空。\n\n 青山依旧在几度夕阳红。\u3000\u3000白渔樵江渚上惯\n\n 看秋月春风。一壶浊酒喜相逢。古今多少事都付\n\n 笑谈中。\n\n ——调寄《临江仙》\n\n 话说天下大势分久必合合久必分。周末七国分争并入于秦。及秦灭之后楚、汉分争又并入于汉。汉朝自高祖斩白蛇而起义一统天下后来光武中兴传至献帝遂分为三国。推其致乱之由殆始于桓、灵二帝。桓帝禁锢善类崇信宦官。及桓帝崩灵帝即位大将军窦武、太傅陈蕃共相辅佐。
导入没问题,接下来是使用jieba.lcut()
函数,将字符串分割成等量的中文词语。下面插播一条广告:
txt1 = '我来到北京清华大学'
setlist = jieba.lcut(txt1)
setlist
Building prefix dict from the default dictionary ...
Loading model from cache C:\Users\AppData\Local\Temp\jieba.cache
Loading model cost 1.719 seconds.
Prefix dict has been built succesfully.
['我', '来到', '北京', '清华大学']
从上面的例子可以看到输入一个字符串,返回了一个等量的中文词汇列表。
回到正题,知道了这个函数的用法,我们来看看怎么把我们的这本《三国》全部分词,并将全部词汇列表命名为: world
。
words = []
while True:
words += jieba.lcut(txt.__next__())
Building prefix dict from the default dictionary ...
Loading model from cache C:\Users\AppData\Local\Temp\jieba.cache
Loading model cost 1.654 seconds.
Prefix dict has been built succesfully.
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-3-9c577e6b04b4> in <module>
1 words = []
2 while True:
----> 3 words += jieba.lcut(txt.__next__())
StopIteration:
上面的StopIteration
并不是报错他是可迭代对象全部迭代完成的标志。接下来我们需要统计一下每个词出现的频率,因为是一个词对应一个频率,那么我们选择内置数据结构字典来储存。
但是分词十分多,因为我们要统计人名出现的次数,所以我们其实只需要统计有两个字或三个字的词语即可。
# 创建空字典
counts = {}
# 遍历字典
for word in words:
if len(word) == 1:
continue
else:
# 往字典里增加元素
counts[word] =counts.get(word, 0) + 1
# print(counts)
这个输出也有点多,大概类似于这种:
{'正文': 1, '第一回': 1, '桃园': 19, '豪杰': 22, '结义': 14, '黄巾': 40, '英雄': 82, '立功': 22, '滚滚': 5, '长江': 25, '逝水': 1, '浪花': 1, '淘尽': 1, '是非成败': 1, '转头': 2, '青山': 1, '依旧': 11, '几度': 1, '夕...
这里面的'向子典里增加元素'是很巧妙的一步,我来解释一下这步:
counts[word] =counts.get(word, 0) + 1
其实这个语句是比较复杂的counts['key'] = value
, 举一个栗子,如果此时的word
是“曹操”(曹操一定会是主角!!!)那么这一步就应该是:
counts['曹操'] = counts.get('曹操', 0) + 1
dic.get('key', 默认值)
是一个字典函数,它的作用是确定字典中是否存在某个 key 并获取其 value,如果没找到这个 key 就返回默认值。详见:https://www.jianshu.com/p/5d0bef1bc259
那么程序在执行这一步的时候会在字典 counts 中寻找 key 值为‘曹操’的键值对,如果找到了把 value 加一,并更新 value。
我们赶快看看到底是谁出现的次数比较多:
items = list(counts.items())
items.sort(key=lambda x: x[1], reverse=True)
items
输出大概类似于这种:[('曹操', 909),('孔明', 817),('将军', 740),('却说', 642),('玄德', 514),('关公', 507),('丞相', 483),('二人', 457),('不可', 427)('荆州', 419),('孔明曰', 385) ('不能', 380),('如此', 376),('玄德曰', 375),('商议', 341),('张飞', 339),('如何', 332),('主公', 326),('军士', 307),('左右', 288),('军马', 288),('刘备', 268),('次日', 267),('引兵', 265),('大喜', 263),('吕布', 258),('孙权', 258),...
哈哈哈哈果然是曹操老哥,曹操老哥牛X!等等先不要高兴太早,这里面其实还有像‘刘备’,‘玄德’,‘玄德曰’其实都是‘刘备’这个人,好的本着公平公正的原则就把他们都整合到一起吧。
counts['孔明'] = counts.get('孔明') + counts.get('孔明曰')
counts['玄德'] = counts.get('玄德曰') + counts.get('玄德')
counts['玄德'] = counts.get('玄德') + counts.get('刘备')
counts['关公'] = counts.get('关公') + counts.get('云长')
我们看一下现在的 top 20是啥样:
items[0:20]
输出为[('曹操', 909),('孔明', 817),('将军', 740),('却说', 642),('玄德', 514),('关公', 507),('丞相', 483),('二人', 457),('不可', 427),('荆州', 419),
('孔明曰', 385),('不能', 380),('如此', 376),('玄德曰', 375),('商议', 341),('张飞', 339),('如何', 332),('主公', 326),('军士', 307),('左右', 288)]
从上面的输出中可以看出有很多无关的词汇,真的我忍他们很久了!!是时候对他们下手了!!!
首先创建一个无关词字典,值得注意的是,我们整合后的词也成了无关词就没有必要再次计算了,还有就是我们的目标是统计前 10 的人物,所以不需要获得全部的无关词。
excludes = {"将军", "却说", "丞相", "二人", "不可", "荆州", "不能", "如此", "商议",
"如何", "主公", "军士", "军马", "左右", "次日", "引兵", "大喜", "天下",
"东吴", "于是", "今日", "不敢", "魏兵", "陛下", "都督", "人马", "不知",
'玄德曰', '孔明曰', '刘备', '关公'}
for word in excludes:
del counts[word]
我们重新排序来看看处理后的结果
items = list(counts.items()) # 将字典转化为 元组的字典
items.sort(key=lambda x: x[1], reverse=True)
print(items[0:10])
[('孔明', 1202), ('玄德', 1157), ('曹操', 909), ('张飞', 339), ('吕布', 258), ('孙权', 258), ('赵云', 254), ('云长', 239), ('司马懿', 221), ('周瑜', 215)]
items.sort(key=lambda x: x[1], reverse=True)
for i in range(10):
character, count= items[i] # 这是一个拆包操作
print(character, count)
孔明 1202
玄德 1157
曹操 909
张飞 339
吕布 258
孙权 258
赵云 254
云长 239
司马懿 221
周瑜 215
好吧,恭喜孔明,玄德君成功反超。
再次插播多条广告:
- 统计出现频次最高的前20个词方法2
roles = Counter(counts)
role = roles.most_common(10)
print(role)
[('孔明', 1202), ('玄德', 1157), ('曹操', 909), ('张飞', 339), ('吕布', 258), ('孙权', 258), ('赵云', 254), ('云长', 239), ('司马懿', 221), ('周瑜', 215)]
这是使用collections
中的Counter()
函数,这个我还没有仔细研究,详细资料见http://www.pythoner.com/205.html
- 注意
items.sort(key=lambda x: x[1], reverse=True)
中的 lambda 函数的使用
a = (1,2)
a[1]
2
广告插播结束,好啦到现在基本上我们就分析的差不多啦,但是这样的结果还是有点不直观,我们来试试画一个词云:
# 构造词云字符串
li = []
for i in range(10):
character, count = items[i]
for _ in range(count):
li.append(character)
# print(li)
cloud_txt = ",".join(li)
测试一下emmmmm这个的输出谁用谁知道,,,
# 构造词云字符串
wc = WordCloud(
background_color='white', # 改变底色 默认为黑色
font_path='msyh.ttc',
# 是否包含两个词的搭配默认是True
collocations=False
).generate(cloud_txt)
wc.to_file('三国中出现前十的人物.png')
词云图
网友评论