作业 5:词频统计——增强功能
一、基本信息
(一)本次作业的地址:https://edu.cnblogs.com/campus/ntu/Embedded_Application/homework/2300
(二)项目Git的地址:https://gitee.com/ntucs/PairProg/tree/SE016_017
(三)开发环境:Pycharm2018、Python3.6
(四)结对成员:1613072016 高耀、1613072017 钱金
二、项目分析
(一)程序运行模块(方法、函数)介绍
(1)封装功能的wClass.py模块
import re from string import punctuation # 把基本功能里的:统计单词数、统计最多的单词及其词频封装成一个独立的类 class wClass(object): # 构造函数 # input_dst:读入的文件路径 word_len:统计的词组长度 out_num 输出的单词数量 result_dst:生成文件的存储路径 def __init__(self, input_dst, word_len, out_num, result_dst): self.input_dst = input_dst self.word_len = word_len self.out_num = out_num self.result_dst = result_dst # 读文件到缓冲区,统计文本行数 def process_file(self): try: # 打开文件 file = open(self.input_dst, 'r') # dst为文本的目录路径 except IOError as e: print(e) # 读文件到缓冲区,统计文本行数 lines = len(file.readlines()) with open(self.input_dst) as f: bvf = f.read() f.close() return bvf, lines # 处理缓冲区,返回字典word_freq,单词总数 def process_buffer(self, bvf): if bvf: # 将文本内容都小写 bvf = bvf.lower() # 用空格消除文本中标点符号 words = bvf.replace(punctuation, ' ').split(' ') # 停词表模块 stop_words = open("stopwords.txt", 'r').read() # 读取停词表文件,默认 # 正则匹配 regex = '' # 当统计的是单个单词时,正则匹配至少以4个英文字母开头,跟上字母数字符号,单词以分隔符分割,不区分大小写 for i in range(self.word_len): regex += '[A-Za-z]+' if i < self.word_len - 1: regex += '\s' result = re.findall(regex, bvf) # 正则查找词组 word_freq = {} for word in result: # 将正则匹配的结果进行统计 if word not in stop_words: # 当单词不在停词表中时,使用正则表达式匹配 word_freq[word] = word_freq.get(word, 0) + 1 return word_freq, len(words) # 返回统计最多的单词及其词频 def get_word_freq(self, word_freq): if word_freq: sorted_word_freq = sorted(word_freq.items(), key=lambda v: v[1], reverse=True) return sorted_word_freq[:self.out_num] # 控制台输出、写入文件 def print_result(self): # 在控制台输出 # 输出前out_num个长度为word_len的词组及其词频 print(self.input_dst + '词汇统计情况:') bvf, lines = wClass.process_file(self) word_freq, words_len = wClass.process_buffer(self, bvf) lines = 'lines:' + str(lines) words = 'words:' + str(words_len) print(lines) print(words) items = wClass.get_word_freq(self, word_freq) print('长度为' + str(self.word_len) + '的词组及前'+str(self.out_num)+'的词频:') for item in items: # 格式化 print('<' + str(item[0]) + '>:' + str(item[1])) # 写入文件 try: result = open(self.result_dst, "w") # 以写模式打开,并清空文件内容 except Exception as e: print(e) result = open(self.result_dst, "x") # 文件不存在,创建文件并打开 # 写入文件result.txt result.write(lines + "\n") result.write(words + "\n") for item in items: item = '<' + str(item[0]) + '>:' + str(item[1]) + '\n' result.write(item) print('写入'+self.result_dst+'文件已完成') result.close()
(2)调用模块和性能分析的wordCount.py文件
# 编译环境:Pycharm2018、Python3.6 # 项目名称:结对项目之词频统计——增强功能 # filename:WordCount.py # 作者:1613072016 高耀 1613072017 钱金 import wClass import argparse import cProfile import pstats # 读取输入,执行操作 def main(): parser = argparse.ArgumentParser() parser.add_argument('-i', type=str, default='input.txt') # 默认读入的文件路径 parser.add_argument('-m', type=int, default=1) # 默认统计的词组长度为1 parser.add_argument('-n', type=int, default=5) # 默认统计词频前5的词组 parser.add_argument('-o', type=str, default='output.txt') # 默认的生成文件的存储路径 args = parser.parse_args() obj = wClass.wClass(args.i, args.m, args.n, args.o) obj.print_result() # 性能分析 if __name__ == '__main__': # 使用cProfile进行性能分析 cProfile.run("main()", filename="result2.out") # 创建Stats对象 p = pstats.Stats('result2.out') p.strip_dirs().sort_stats("call").print_stats(10) # 按照调用的次数排序 p.strip_dirs().sort_stats("cumulative").print_stats(10) # 按执行时间次数排序
(二)程序算法的时间、空间复杂度分析
(1)时间复杂度:以耗时最长的process_buffer函数举例:
由性能分析得出re.findAll()方法耗时最长,Average Case下时间复杂度为O(nlog2n),其他代码段时间复杂度近似O(n),所以程序算法时间复杂度近似O(nlog2n)
附:re.findAll()函数:返回string中所有非重叠的匹配——字符串列表
(2)空间复杂度--主要是从input.txt中读取文章,近似O(n)
(三)程序每项新增功能运行案例截图【截图结果是使用停词表模块】
【在程序中默认指定-i,-m,-n,-o的值】
parser.add_argument('-i', type=str, default='input.txt') # 默认读入的文件路径 parser.add_argument('-m', type=int, default=1) # 默认统计的词组长度为1 parser.add_argument('-n', type=int, default=5) # 默认统计词频前5的词组 parser.add_argument('-o', type=str, default='output.txt') # 默认的生成文件的存储路径
(1)只指定读入的文件路径input.txt
(2)指定读入的文件路径input.txt、统计的词组长度2
(3)指定读入的文件路径input.txt、统计的词组长度2、统计词频前10的词组
(4)指定读入的文件路径input.txt、统计的词组长度3、统计词频前5的词组、生成文件的存储路径output.txt
(5)参数之间的顺序不固定,指定读入的文件路径input.txt、统计的词组长度3、统计词频前5的词组、生成文件的存储路径output.txt
三、性能分析
(一)性能图表
(1)使用cProfile进行性能分析(以上面截图的第一个为例)
1)按照调用次数排序
2)按照执行时间排序
(2)使用gprof2dot进行性能分析可视化(以上面截图的第一个为例)
python gprof2dot.py -f pstats result2.out | dot -Tpng -o result2.png
(3)使用pycharm自带profile工具进行性能分析可视化(以上面截图的第一个为例)
四、PSP 表格
(一)结对编程时间开销(单位:分钟)
五、事后分析与总结
(一)简述结对编程时,针对某个问题的讨论决策过程。
我们在讨论如何实现多元词组的统计时,本来高耀同学想要沿用作业4的NLTK提供的函数,但是发现没有现成的方法能够提供调用,所以我们选择钱金同学提出的使用正则表达式,同时使用停词表模块,减少介词、人称代词被统计到。
(二)评价对方:请评价一下你的合作伙伴,又哪些具体的优点和需要改进的地方。 这个部分两人都要提供自己的看法。
(1)高耀评价钱金:①钱金同学思维活跃、常常想出好的方法。表现在:钱金同学想出使用正则表达式的方法来实现指定长度词组统计。②钱金同学的基础代码能力和基础知识欠缺,需要继续努力。
(2)钱金评价高耀:①高耀同学学习能力强、对于不熟悉的方法上手很快。表现在:对于不熟悉的正则表达式能够快速掌握使用方法。②高耀同学做事认真且有条理、但是严谨性需加强。表现在:编写博客层次清晰有条理,但是对于代码的容错机制欠缺。
(三)评价整个过程:关于结对过程的建议
两人结对编程有利于同学间优势互补,能够较为高效的完成任务,很好地提升了自己的编程能力和交流能力。 两人结对编程是最小规模的团队合作,合理的分工,协力完成工作,这也让我们提前熟悉团队合作模式,相比较作业4时候的配合不太熟悉,现在已经轻车熟路。
(四)结对编程照片