起初想找一份英文单词文件,用于数据分析。没找到,所以就想,找一本英文小说,将重复的单词去掉,几乎可以得到一份单词文件。本文以《傲慢与偏见》英文小说为例,统计小说中各个单词出现的次数。
1. 下载英文小说
我恰巧搜到《Pride and Prejudice》,将文件头尾无关部分删去。
2. 编写shell脚本
在博文《Linux Shell高级技巧(五)》中的统计英文文章中每个单词出现的频率,给出了SHELL源代码,稍加修改,本例SHELL源代码如下:
#!/usr/bin/env bash # 1. 通过当前脚本的pid,生成awk脚本的临时文件名。 # 2. 捕捉信号,在脚本退出时删除该临时文件,以免造成大量的垃圾临时文件。 awk_script_file="/tmp/scf_tmp.$$" count_words_file="/tmp/cwf_tmp.$$" trap "rm -f $awk_script_file $count_words_file" EXIT # 3. while循环将以当前目录下的testfile作为输入并逐行读取,在读取到末尾时退出循环。 # 4. getline读取到每一行将作为awk的正常输入。在内层的for循环中,i要从1开始,因为$0表示整行。NF表示域字段的数量。 # 5. 使$i作为数组的键,如果$i的值匹配正则表达式"^[a-zA-Z]+$",我们将其视为单词而不是标点。每次遇到单词时,其键值都会递增。 # 6. 最后通过awk脚本提供的特殊for循环,遍历数组的键值数据。 cat < < 'EOF' > $awk_script_file BEGIN { while (getline < "Pride_and_Prejudice_count_words.txt" > 0) { for (i = 1; i < = NF; ++i) { if (match($i,"^[a-zA-Z]+$") != 0) arr[$i]++ } } for (word in arr) { printf "%-*s\t%s\n", 20, word, arr[word] } } EOF # 运行程序 awk -f $awk_script_file > $count_words_file sort -k1,1n $count_words_file > count_words.txt
正规表达式^[a-zA-Z]+$
,^[a-zA-Z]
表示以小写或大写字母开头,+
表示前面的字符至少重复一次,$
表示结束(即也以小写或大写字母结尾)。细心的话,会发现,这样不够准确,因为少考虑了句子中最后一个单词的情况,如“me.”。 事实上,上述的代码很粗糙,很多情况没有考虑到,比如单复数、动词时态等。
3. 一些有趣的事
(1)共有多少单词
总行数 13044 总单词数 121598 总字符数 698013
不同的单词数只有5493(包括一个词的多种形式。如在本统计中look, looked, looking, looks算4个不同的词),如果把一个词的多种形式只算一个的话(如将look, looked, looking, looks算1个),那就更少了。但同时也应该注意到,一些常见单词并没有出现在小说中,如elephant。
(2)出现最多的词
词 出现次数 to 3767 the 3655 of 3289 and 2992 a 1746 was 1673 her 1672 I 1650 in 1634 that 1293
可见,出现次数最多的词几乎是虚词,这也难怪,搜索引擎在切词时,要将虚词过滤。
(3)只出现i次的单词数
#出现次数 单词数 1 2243 2 841 3 488 4 313 5 219 6 158 7 116 8 122 9 62 10 67
很自然就想,单词分布会不会符合某种概率分布(如泊松分布、幂律分布)。下图的上半部分是单词出现次数,下半部分是单词出现次数为n的概述(如出现1次的单词有2243个,总单词数5493,所以概率是0.408338),右侧部分是左侧部分的放大。
从图形上看,蛮像服从幂律分布的图,但我也不确定。注:已将本文相关资料上传到GitHub,在这里.
2016/02/01更新:后来某天看到一个YouTube视频The Zipf Mystery,谈到了单词出现次数服从幂律故事。
赞赏微信赞赏
支付宝赞赏