对英文文档中的单词与词组进行频率统计

您所在的位置:网站首页 怎么判断一个词组的性质 对英文文档中的单词与词组进行频率统计

对英文文档中的单词与词组进行频率统计

2024-05-28 16:32| 来源: 网络整理| 查看: 265

一、程序分析

 

1、以只读模式读取文件到字符串

def process_file(path): try: with open(path, 'r') as file: text = file.read() except IOError: print("Read File Error!") return None return text

 

2、对字符串进行数据清洗,返回一个字典

import re word_list = re.sub('[^a-zA-Z0-9n]', ' ', textString).lower().split()

使用正则表达式过滤掉文档中的特殊字符,把它们全部替换为空格,方便后续的分隔操作。(忽略大小写,所以全部使用小写字母)

  

  2.1、只考虑单词频率统计

for word in word_list: if word in word_freq: word_freq[word] += 1 else: word_freq[word] = 1

  判断单词列表中的单词是否在单词频率字典中。

  如果这个单词在字典中,则该单词的个数加1;

  如果这个单词不在字典中,则以这个单词为键,赋值为1,表示这个单词第一次出现。

 

  2.2、考虑单词和词组的频率

    2.2.1、数据结构

    词组是由单词连接构成的,一个单词既可以与前面的单词构成词组,也可以与后面的单词构成词组。

    同时,一个单词可能在文章中多次出现,并且和前后的单词构成多种不同的词组。

    这也就表示几乎每一个单词可以一前一后发散出去,这与图状结构颇为类似。(适用于两个单词构成的词组)

    选择图状结构与当前的问题颇为契合。但是,目前python没有一种已知的图类来供我们操作。(自定义类暂不作考虑) 

    于是我想到了树状结构(两个以上单词构成的词组也适用),砍去了单词向前的发散,只保留向后的。至此,问题就变成了构建森林。

    而在python中,森林是比较容易表示的。用一个字典放置每一棵树的根节点,字典套字典,就形成了森林。

    可能你们要问单词和词组的统计个数放在哪。我使用“Value”作键,键值number这样一种结构放在该单词的字典中。

    具体的字典示例样本参见sample.json

 

    2.2.2、具体流程

  

    以索引停在50,词组限制单词个数为3为例:

    (1)在树的根节点中寻找mother节点,找不到就创建mother节点。

"mother":{ "Value":1 }

    (2)在树的根节点中寻找your节点,找到了继续寻找mother节点,找不到就创建mother节点。(不可能找不到,因为索引经过49之后肯定存在your根节点)

    (3)以此类推,在树的根节点中寻找did节点,找到了继续寻找your节点,,找到了继续寻找mother节点,找不到就创建mother节点......

 

    具体实现:

count = len(word_list)i = 0while i < count: # 因为需要用到索引遍历列表,只能使用while来遍历列表 finish = i start = i - num + 1 # num表示词组的单词个数限制,start表示以该单词作为词组结尾的第一个单词的索引 if start < 0: start = 0 # 处理开始时索引前面没有单词的特殊情况 index = i while index >= start: # 做num次建立节点 if word_list[i] in get_dict_value(word_freq, word_list[index: finish]).keys(): get_dict_value(word_freq, word_list[index: finish])[word_list[i]]['Value'] += 1 else: get_dict_value(word_freq, word_list[index: finish]).update({word_list[i]: {'Value': 1}}) index -= 1 i += 1     get_dict_value函数 def get_dict_value(word_freq={}, keys=[]): """如果keys为字符串,返回word_freq字典中以keys为键的值。 如果keys为列表,则使用eval()函数进行字符串拼接,深度查找word_freq字典中以keys为键的值。""" if type(keys).__name__ == 'str': return word_freq[keys] else: count = len(keys) if count == 0: return word_freq elif count == 1: return word_freq[keys[0]] elif count == 2: return word_freq[keys[0]][keys[1]] elif count == 3: return word_freq[keys[0]][keys[1]][keys[2]] #对寻找三个单词以下的词组进行特化 else: string = "word_freq['" string += "']['".join(keys) string += "']" return eval(string) #动态寻找字典的值的一般版本

    2.2.3、字典格式化

    数据已经存储到了森林中,接下来就是如何把森林格式化成普通的字典。

    相比深度优先遍历,我选择的是更易理解与实现的广度优先遍历。

    广度优先遍历的原理我不再赘述。

def format_dict(word_freq={}): """对统计短语的情况生成的复杂字典进行格式化,格式化后的形式为""" formated_word_freq = {} phrases = [] for word in word_freq.keys(): #将所有根节点放入队列中 phrases.append(word) while len(phrases) > 0: #只要队列还有元素,就表明还没有遍历结束 phrase = phrases[0] if len(get_dict_value(word_freq, phrase)) == 1 and type(phrase).__name__ == 'list': formated_word_freq[' '.join(phrase)] = get_dict_value(word_freq, phrase)['Value'] #搜索到叶子节点了,这个节点存入格式化好的字典中 else: for next_word in get_dict_value(word_freq, phrase): #除"Value"键值对以外的键都入队 temp = [] if type(phrase).__name__ == 'str': temp.append(phrase) else: temp.extend(phrase) if next_word != 'Value': temp.append(next_word) phrases.append(temp) phrases.pop(0) #搜索完队列的第一个节点的子节点,这个节点出队 # print(formated_word_freq) return formated_word_freq

 

3、输出字典(没有改动)

def output_result(word_freq): if word_freq: sorted_word_freq = sorted(word_freq.items(), key=lambda v: v[1], reverse=True) for item in sorted_word_freq[:10]: print(item)

 

4、主函数

if __name__ == "__main__": import argparse parser = argparse.ArgumentParser() parser.add_argument('path') #路径 parser.add_argument('num') #词组单词个数 args = parser.parse_args() path = args.path num = int(args.num) buffer = process_file(path) if buffer: word_freq = process_buffer(buffer, num) if num != 1: word_freq = format_dict(word_freq) output_result(word_freq)

命令行接受两个参数,一个是英文文档的路径,还有一个是构成一个词组的最大单词个数(为1时即单词统计)。没有这个限制,森林里就会出现一棵从第一个单词到最后一个单词的树。

 

二、代码风格说明

  基本遵从PEP8,PEP8 涵盖了诸如空格、函数/类/方法之间的换行、import、对已弃用功能的警告之类的寻常东西,是一个不错的准则。

def get_dict_value(word_freq={}, keys=[]): """如果keys为字符串,返回word_freq字典中以keys为键的值。 如果keys为列表,则使用eval()函数进行字符串拼接,深度查找word_freq字典中以keys为键的值。""" if type(keys).__name__ == 'str': return word_freq[keys] else: count = len(keys) if count == 0: return word_freq elif count == 1: return word_freq[keys[0]] elif count == 2: return word_freq[keys[0]][keys[1]] elif count == 3: return word_freq[keys[0]][keys[1]][keys[2]] #对寻找三个单词以下的词组进行特化 else: string = "word_freq['" string += "']['".join(keys) string += "']" return eval(string) #动态寻找字典的值的一般版本

    注释基本也只在关键地方才有,不必面面俱到。

 

三、程序运行命令、运行结果截图 

    运行命令:

python statistic.py Gone_with_the_wind.txt 1 python statistic.py Gone_with_the_wind.txt 2

 

四、简单性能分析 

  运行命令:

python -m cProfile -o result.out -s cumulative statistic.py Gone_with_the_wind.txt 2 python gprof2dot.py -f pstats result.out | dot -Tpng -o result.png

 

  单词统计性能分析:

  词组统计性能分析:

  在进行单词统计时,时间有五成花费在过滤特殊字符上,有三成花费在创建字典上。

  而在词组统计中,过滤特殊字符只占了一成,两层时间在创建森林,六成时间在格式化森林。其中,尤其以list的pop()函数占用时间最多,将近一半的时间用来出队。

  

  程序优化:尝试在不pop()得情况下修改广度优先遍历。尝试另一种数据结构,森林比图占的内存多。

  修改中...



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3