python红楼梦人物词频统计_用 Python 分析《红楼梦》

1 前言

两个月以来,我通过互联网自学了一些文本处理的知识,用自然语言处理和机器学习算法对《红楼梦》进行了一些分析。这个过程中我找到了一些有趣的发现,所以我想写一篇文章,既㲌与大家分享和讨论实验结果,也顺便做一个整理和总结。(其实虽说是两个月,但是中间停顿了一段时间,真正在做的时间大概是两周左右)

我开始做这件事情是因为之前看到了一篇挺好玩的文章,大概内容是,作者用“结巴分词”这个开源软件统计了红楼梦中各词汇的出现次数(也就是词频),然后用词频作为每个章回的特征,最终用“主成份分析”算法把每个章回映射到三维空间中,从而比较各个章回的用词有多么相似。(文章地址:用机器学习判定红楼梦后40回是否曹雪芹所写)作者的结论是后四十回的用词和前八十回有明显的差距。

看完文章之后,我觉得有两个小问题:首先,作者用的结巴分词里的词典是根据现代文的语料获得的(参见“结巴分词”开发者之前对网友的回复:模型的数据是如何生成的? · Issue #7 · fxsjy/jieba),而《红楼梦》的文字风格是半文半白的,这样的分词方法准确性存疑;其次,虽然作者用《三国演义》做了对比,但是依然没有有力地证明用词差异没有受到情节变化的影响。于是我决定自己做一遍实验,用无字典分词的方法来分词,并且尝试剔除情节对分析的影响,看看结果会不会有所不同。

本来开始写的时候觉得 5000 字就差不多了,结果最后成文的时候竟然达到了 1.3 万字。即使这样,我也只能解释一下算法的大致工作过程,至于详细的原理,如果感兴趣的话可以找其他资料去学习,我也会附上一些资料链接。不然如果我写的面面俱到的话感觉可以出书了……至于结果如何?先卖个关子。(诶,不要直接滑到底啊!)

程序已在 GitHub 上开源,使用方法参见 README 文件:LouYu2015/analysis_on_the_story_of_a_stone。考虑到版权问题,我决定不提供《红楼梦》原文。如果想复现实验结果的话,可以去找小说网站下载。(更新:根据网友提醒,《红楼梦》因为作者去世远远超过 100 年而进入公有领域,不受版权限制。因此我把原文也补充了上去,现在按照说明运行程序即可复现结果。也可在这里获取《红楼梦》全文:紅樓夢 - 维基文库,自由的图书馆。)

2 文本预处理

这一步很基础,就不赘述了。简单来说,就是要根据标点符号,把每一个分句都切开,然后用统一的符号(这里我用的是井号)来标记切分点。这样对于后面的程序来说就好处理一些了。

虽然目标很简单,然而,有些细节还是需要额外处理一下的。比如,我找到的文本里,所有“性”啊,“露”啊之类的字都被用 『』 框了起来(可能为了过滤少儿不宜的内容?我怎么觉得框起来以后更奇怪了……),所以这种标点需要被删掉,不能当作分割符号。另外,每章开头的回目编号也需要去掉,因为这不算小说的内容。最后,文本中出现了一些电脑中没有的罕见字,不过好在文本中这些罕见字都在括号内用拆分字型的方法标了出来(比如“(左王右扁)”),所以理论上我可以把这些内容替换成一些原文中没有的字符(比如特殊符号),最后再替换回去。不过我太懒了,所以没有做这样的替换。理论上罕见字对后面的分析也不会有很大,因为后面涉及到的都是出现频率比较高的单词。

处理后的效果是这个样子:#甄士隐梦幻识通灵#贾雨村风尘怀闺秀#此开卷第一回也#作者自云#因曾历过一番梦幻之后#故将真事隐去#而借#通灵#之说#撰此石头记一书也#故曰#甄士隐#云云#但书中所记何事何人#自又云#今风尘碌碌一事无成#忽念及当日所有之女子#一一细考较去#觉其行止见识皆出于我之上#何我堂堂须眉诚不若彼裙钗哉#实愧则有馀#悔又无益之大无可如何之日也……

3 构建全文索引

得到处理后的文本之后,我需要建立一个全文索引。这样是为了快速地查找原文内容,加速后面的计算。我使用了后缀树这个结构作为索引。这个数据结构比较复杂,所以我们可以先谈谈更简单的字典树。

3.1 字典树

首先,我们看看字典树的样子:

Free Image on Pixabay - Landscape, Tree, Flowers, Book

啊错了,这个才是字典树……

Trie - Wikipedia

上图中,每个圆圈是一个结点,代表着一个字符串(就是圆圈内的内容);结点之间的连线是边,代表着一个字母。最上面的结点,也就是空着的那个结点,是根结点。如果我们从根结点不断向下走到某个结点,那么把经过的每一条边上的字母拼起来,就是这个结点代表的字符串了。这就是字典树的特点。

那么字典树是干什么用的呢?举个例子来说,假如我们想在这棵字典树里查找 “to” 这个单词,就可以先从根结点下面的边里找到第一个字母,也就是 “t” 这条边,从而找到 “t” 这个结点。然后我们再从 “t” 结点下面的边里找到第二个字母,也就是 “o” 这条边,就找到 “to” 这个结点了。假如 “to” 这个结点里储存了 “to” 的中文解释,那么我们只通过两次操作就找到了 to 的中文意思。这样比一个词一个词地找的方法快多了。这很像我们查字典的时候,先看第一个字母在字典中的位置,然后再看第二个字母……最终找到单词,因此被称为字典树。

3.2 后缀树

说完字典树,我们再说说后缀树的前身:后缀字典树。后缀字典树其实就是字典树,只不过里面的内容不是单词,而是一个字符串的所有后缀:从第一个字母到最后一个字母的内容,从第二个字母到最后一个字母的内容……以此类推。比如说,"banana" 的所有后缀就是 banana, anana, nana, ana, na 和 a。把这些内容都加到字典树里,就构成了后缀字典树。下面左图就是 banana 的后缀字典树:

https://www.slideshare.net/farseerfc/ukks-algorithm-of-suffix-tree

而后缀树和后缀字典树的区别就是,在后缀树中,我们要把下面只有一条边的结点去掉,然后把这个结点连接的两条边压缩成一条。比如,左图后缀字典树中的 b-a-n-a-n-a,在右图的后缀树中被压缩成了 banana 这一条边。此外,后缀树还使用了一个技巧,就是不储存边的内容,而是储存这些内容在原文中的位置。因为后缀树中的很多内容都是重复的,所以这个小技巧可以大大减少索引的大小(用专业的语言描述,它的空间复杂度是 O(n))。

后缀树又有什么用呢?它最大的用途就是检索字符串中间的内容。比如,假如我想查找 an 在 banana 中哪里出现过,只需要查找代表 an 的结点,就找到了所有以 an 开头的结点: anana 和 ana。由于每次出现 an 的地方都一定会产生一个以 an 开头的后缀,而所有的后缀都在后缀树中,所以这样一定能够找到所有 an 出现的位置。后缀树的强大之处在于,即使我们把 banana 换成一篇很长很长的文章,我们也能很快地进行这样的检索。

最后,我使用了 Ukkonen 算法快速地创建了整篇《红楼梦》的后缀树(用专业的语言描述 Ukkonen 算法的速度:它的时间复杂度是 O(n))。Ukkonen 算法比较复杂,所以这里我不会讲解 Ukkonen 算法,感兴趣的同学可以看看这些资料:

Ukkonen's suffix tree algorithm in plain English

后缀树的构造方法-Ukkonen详解 - 懒人小何的日志 - 网易博客

Ukkonen's Suffix Tree Construction - Part 6 - GeeksforGeeks

有了全文索引以后,后面的程序就好做了。

4 制作字典

等等,我们不是要无字典分词吗,为什么还要制作字典?其实无字典分词并不是完全不用字典,只是说字典是根据原文生成的,而不是提前制作的。为了进行分词,我们还是需要先找出文章中哪些内容像是单词,才能确定如何进行切分。

那么怎么确定哪些内容像单词呢?最容易想到的方法就是:把所有出现次数高的片段都当成单词。听上去很有道理,所以我们可以试一试,用后缀树查询红楼梦中的所有重复的片段,然后按出现次数排个序:宝玉(3983)、笑道(2458)、太太(1982)、什么(1836)、凤姐(1741)、了一(1697)、贾母(1675)、一个(1520)、也不(1448)、夫人(1437)、黛玉(13

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值