统计PDF的词频
可视化领域,有一本非常经典的书籍《The Grammar of Graphics》,一直都想仔细读一下。但是这本书似乎没有中译本,只有英文版。就我目前这么烂的英文阅读水平,啃起来绝对很吃力。因此我想先统计下这本书里面的单词词频,找到高频的生词,我先通过比如扇贝单词等APP将这些生词背下来,这样看书的时候效率肯定就上去了。
大致实现的思路就是:解析PDF文件->拆分单词->统计词频->过滤认识的单词->将剩下的高频词导入扇贝生词本。
解析PDF文件
我看英文版的PDF,一般都是用的pdfjs这个工具,方便点击查词。因此本来这次也准备通过这个工具进行PDF的解析。不过我查找后,发现有一个更方便的工具pdf2json,可以解析成json格式,那这样我处理起来就更容易了。因此最终选择了这个pdf2json。github地址在这里。
解析的代码直接使用官方示例即可:
1 | |
拆分单词
单词的拆分需要注意如下几点:
1、不能只根据空格拆分,还需要注意对一些特殊字符也进行拆分;
2、单词一定要转为小写,否则同一个单词会因为大小写问题,被多次统计到;
3、换行导致一个单词被拆分为2个的情况也需要考虑下(我这次暂时没有处理换行的问题)。
代码如下:
1 | |
统计词频
这一步稍微麻烦一些,因为要考虑单词形变的问题,比如复数形式、过去时、现在进行时等等,需要将其转换为单词原型来统计(像objects就应该转为object),否则一个单词就会因为形变,在统计结果中出现多次。
这一步靠我自己写逻辑明显是不行的,得引入第三方支持了。一开始我本来是想用一些在线词典API,然后尝试了有道词典的API,发现并不能获取到单词原型;后来找到了一个做自然语言解析的stanford-nlp,发现这个是可以取到原型的,因此最终采用了该工具。
这个工具是Java写的,需要安装Java环境,然后下载官方的包,解压后即可使用。
由于这个解析很耗费性能,文本长度不能太大,因此需要先对现有的单词做去重处理。我是先对当前解析PDF后获取到的单词做了去重,然后写入了一个文本中,每个单词一行;然后再通过命令行调用stanford-nlp去做解析。解析命令大致如下:
1 | |
这里需要注意下,尽量把Java的堆内存设置大一些(-Xmx参数),否则可能出现内存溢出的报错。
单词形变处理好之后,后续统计词频就比较简单了,不再赘述。
注意,必须加-cp '*',否则会报错:找不到或无法加载主类 edu.stanford.nlp.pipeline.StanfordCoreNLP
(必看)关于性能
前面我用的是默认的参数,结果把所有的功能都用上了,巨慢无比。后来看了这个:
Understanding memory and time usage
才发现可以通过-annotators指定自己需要的功能。我需要的是lemma,应该这样设置-annotators "tokenize,pos,lemma":
1 | |
这样3000+词基本也是秒级搞定。
关于词性
一开始我以为一个单词一行是不行的,因为无法获取单词在句子中的词性:
但是实际测试,发现是OK的。
关于词量
截取6000个单词,这样频率最低的出现18次。
经过柯林斯3-5星和我自己的easy词库处理后为3562个,上传识别到2772个,加速787个,剩余2000个左右,还不错。
晚上优化了下,去掉了认识的单词,同样截取6000个单词,这样频率最低的出现14次。经过柯林斯3-5星和我自己的easy词库处理后为3311个,上传识别到2464个,加速598个,剩余1900个左右。
过滤认识的单词
因为我也没有一个词库记录所有我认识的单词,因此只能通过一些通用的单词列表将常见单词过滤掉。我选择了柯林斯的单词列表。这个词典对单词词频做了1-5星的评分,我大致看了下,发现3-5星的单词我大部分都认识,因此提取了这部分单词,作为过滤的词表。
1 | |
另外这里还需要再次对单词做一下过滤,包括:
1、筛选掉包含数字的”单词”
2、筛选掉长度小于等于2的”单词”(比如单个字母、被换行阶段而形成的2个字符的字符串等等)
过滤完成后,需要再次进行词频统计。
导入扇贝生词本
扇贝的生词本设置了限制,每次只能导入100个单词。本来我想通过抓包、修改页面HTML元素、通过JS控制页面交互等方式,自动将几千个单词导入进去,结果发现他们的前端、数据接口加密做得挺好的,没让我得逞,因此只能作罢,老老实实手动导入进去了。
现在可以导入单词书了。