作业 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时候的配合不太熟悉,现在已经轻车熟路。

(四)结对编程照片 

posted @ 2018-11-30 13:07  高耀  阅读(407)  评论(4编辑  收藏  举报