作业 4:词频统计——基本功能
一、基本信息
1.1、编译环境、作者、项目名称
1 # 编译环境:Pycharm2017、Python3.7 2 # 项目名称:词频统计——基本功能(结对编程) 3 # 作者:1613072038:夏文杰 4 # 1613072040:高昶
1.2、其他
二、项目分析
2.1 程序运行模块(方法、函数)介绍(因为代码大部分和上次作业一样所以本文中只贴上不同的地方和重要的地方,源码可以在Git上查看)
-
Task1:基本任务(WordCount.py -(Git上的程序名))
主要需要解决二个问题:1、统计文件的有效行数;2、统计文件的单词总数(其中特殊定义了单词的定义);3、将数据存储在文本中
1、统计文件的有效行数
1 lines = len(open(dst, 'r').readlines()) # 借助readlines可以获得文本的行数
2、统计文件的单词总数(其中特殊定义了单词的定义),这里使用正则表达式。
(参考:http://www.runoob.com/python/python-reg-expressions.html )
1 def process_buffer(bvffer): 2 if bvffer: 3 for ch in ':,.-_': 4 bvffer = bvffer.lower().replace(ch, " ") 5 bvffer = bvffer.strip().split() 6 word_re = "^[a-z]{4}(\w)*" 7 # 正则匹配至少以4个英文字母开头,跟上字母数字符号,单词以分隔符分割,不区分大小写 8 words = [] 9 for i in range(len(bvffer)): 10 word = re.match(word_re, bvffer[i]) # 匹配list中的元素 11 if word: # 匹配成功,加入words 12 words.append(word.group()) 13 word_freq = {} 14 for word in words: # 对words进行统计 15 word_freq[word] = word_freq.get(word, 0) + 1 16 return word_freq, len(words)
3、将数据存储在文本中
1 def input_result(dst, lines, words, items): # 写入文件 2 with open(dst, 'w') as w: 3 w.write(lines) 4 w.write(words) 5 for item in items: # 格式化 6 item = '<' + str(item[0]) + '>:' + str(item[1]) + '\n' 7 w.write(item) 8 print('写入result文件已完成!') 9 w.close()
-
Task2.1:支持停词(stop_word.py)
这里我们需要建一个存放停词库的文本“stopwords.txt”
上图是停词库的部分,然后需要创建一个list存放文本里的停词,然后在统计词频是遍历list,如果元素存在list中跳过。
1 def process_buffer(bvffer, dst): 2 txt_words = open(dst, 'r').readlines() # 读取停词表文件 3 stop_words = [] # 存放停词表的list 4 for i in range(len(txt_words)): 5 txt_words[i] = txt_words[i].replace('\n', '') 6 # 因为读取文本是readlines所以写入list要将换行符取代 7 stop_words.append(txt_words[i]) 8 if bvffer: 9 word_freq = {} # 下面添加处理缓冲区 bvffer代码,统计每个单词的频率,存放在字典word_freq 10 bvffer = bvffer.lower() # 将文本内容都改为小写 11 for ch in '“‘!;,.?’”': # 除去文本中的中英文标点符号 12 bvffer = bvffer.replace(ch, " ") 13 words = bvffer.strip().split() 14 # strip()删除空白符(包括'/n', '/r','/t');split()以空格分割字符串 15 for word in words: 16 if word in stop_words: # 如果word在停词表里就跳过(本任务核心) 17 continue 18 else: 19 word_freq[word] = word_freq.get(word, 0) + 1 20 return word_freq
-
Task2.2:查看常用的短语(phrase.py)
此处我和搭档夏文杰,都有自己不同的想法。夏文杰的想法是:将以空格分割字符后的list为对象。创建新的字符串(list[i]+' '+list[i+1])组成我们想要的短语;我的想法是用正则表达式来匹配我们想要的短语。我们各自执行了自己的想法,因为夏同学的想法要多次遍历数组所以会很耗时,所以我们决定选用正则表达式匹配的方法。
1 # 正则表达式 2 two_phrase_re = '[a-z]+\s[a-z]+' # 匹配二个单词的短语 3 three_phrase_re = '[a-z]+\s[a-z]+\s[a-z]+' # 匹配三个单词的短语
但是我们发现结果并不让人满意,输出的结果都是:“There be”、“I‘m’”、“It was”、“She was”等没有达到我们想要的,所以我们想到结合停词来改善项目。
1 def create_stop_words(dst): # 创建停词表 2 txt_words = open(dst, encoding='utf-8').readlines() # 文本读取 3 phrase_words = [] # 存放停词表的list 4 for i in range(len(txt_words)): 5 txt_words[i] = txt_words[i].replace('\n', '') 6 # 因为读取文本是readlines所以写入list要将换行符取代 7 phrase_words.append(txt_words[i]) 8 return phrase_words
1 def process_buffer(dst, phrase_words, regex): 2 words = open(dst, 'r').read() # 文本读取 3 bvffer = words.lower() # 将文本内容都改为小写 4 # 将文本中的缩写都替换(进过程序发现以下是最常见的) 5 bvffer = bvffer.replace('’t', '') 6 bvffer = bvffer.replace('’m', '') 7 bvffer = bvffer.replace('’s', '') 8 bvffer = bvffer.replace('’ve', '') 9 # 其实上面的替换可以防在停词中但是涉及到空格等问题就放在前面直接替换了 10 for i in range(len(phrase_words)): # 将文本中的“停词”删除掉,这样会使结果很清晰更能接受 11 bvffer = bvffer.replace(' ' + phrase_words[i] + ' ', ' ') 12 result = re.findall(regex, bvffer) # 正则查找词组 13 word_freq = {} 14 for word in result: # 将正则匹配的结果进行统计 15 word_freq[word] = word_freq.get(word, 0) + 1 16 return word_freq
当然我们在学习的过程中还了解到了jieba、nltk。我们也尝试了用nltk库进行分词等处理。也写了个半成品,具体代码可以查看Git上的(NLTK.py)。
2.2、程序算法的时间、空间复杂度分析
此处我们以以下代码为例:
1 def process_buffer(dst, phrase_words, regex): 2 words = open(dst, 'r').read() # 文本读取 3 bvffer = words.lower() # 将文本内容都改为小写 4 # 将文本中的缩写都替换(进过程序发现以下是最常见的) 5 bvffer = bvffer.replace('’t', '') 6 bvffer = bvffer.replace('’m', '') 7 bvffer = bvffer.replace('’s', '') 8 bvffer = bvffer.replace('’ve', '') 9 # 其实上面的替换可以防在停词中但是涉及到空格等问题就放在前面直接替换了 10 for i in range(len(phrase_words)): # 将文本中的“停词”删除掉,这样会使结果很清晰更能接受 11 bvffer = bvffer.replace(' ' + phrase_words[i] + ' ', ' ') 12 result = re.findall(regex, bvffer) # 正则查找词组 13 word_freq = {} 14 for word in result: # 将正则匹配的结果进行统计 15 word_freq[word] = word_freq.get(word, 0) + 1 16 return word_freq
函数process_buffer中:时间复杂度关注二个for循环就行,假设文本中有N个单词,result中的元素个数为n,其中(N>n),所以时间复杂度为O(N+n),空间复杂度,即需要的辅助空间大小,即word_freq = {}的长度,所以空间复杂度为O(2n)。其他部分分析一样,就不多加赘述了。
2.3、程序运行案例截图
-
Task:基本任务(WordCount.py)
其中测试文件:‘test.txt’:
WordCount.py的运行结果:
-
Task2.1:支持停词(stop_word.py)
我们发现我们的词频统计里没有了我们停词表里的词。
-
Task2.2:查看常用的短语(phrase.py)
二个单词的短语:
三个单词的短语:
三、性能分析
我们大概花了一半的时间在提高程序性能上,大部分都花在改进算法上,比如前面提到的如何实现Task2.2。经过我们的改进,项目基本达到我们的预期。
附上:Task2.2的运行时间的截图和性能表。(整个程序运行时间在一秒左右)
四、其他
-
结对编程时间开销:二人也经过大概半个星期的研究、讨论,实际结对开发编程大概在5小时左右。
-
结对编程照片
四、事后分析与总结
-
简述结对编程时,针对某个问题的讨论决策过程。
如Task2.2中 ,我和搭档夏文杰,都有自己不同的想法。夏文杰的想法是:将以空格分割字符后的list为对象。创建新的字符串(list[i]+' '+list[i+1])组成我们想要的短语;我的想法是用正则表达式来匹配我们想要的短语。我们各自执行了自己的想法,因为夏同学的想法要多次遍历数组所以会很耗时,所以我们决定选用正则表达式匹配的方法。遇到有争议的地方我们就各种完成自己的想法,然后比较性能,谁好用谁的。
- 评价对方:请评价一下你的合作伙伴,又哪些具体的优点和需要改进的地方。 这个部分两人都要提供自己的看法。
高昶评价夏文杰:夏文杰很有悟性,我提出他不理解的东西,他能很快的理解,然后通过他的理解运用到程序中。需要改进的地方:知识面不是很广,平时还需要多阅读、了解计算机相关的知识。
夏文杰评价高昶:高昶有很丰富的编程经验,能很快的想到解决问题的办法,并能很好的实施。需要改进的地方:对知识掌握不牢固,很容易将知识混淆,所以希望能够将巩固一下自己的知识并与经验相结合。
- 评价整个过程:关于结对过程的建议
结对编程是一个相互学习、相互磨合的渐进过程,因为二人平时关系就很不错,二人都很认真的完成本次任务,所以本次的结对编程是很愉快的。通过本次结对编程我们也充分的认识到了合作的重要性,一个人编程不免要犯这样那样的错误,结对编程就很好的避免了这样的问题,而且二人会碰撞出更多的想法和灵感,二人相互学习,相互补充。而且结对编程能提供更好的设计质量和代码质量,两人合作能有更强的解决问题的能力。还有就是,结对编程让有些许枯燥的编程,变得有趣。
建议:结对编程是一件很不错的事情,但是结对编程的过程中难免发生争执,所以妥善处理好争议才能更好的将结对编程做好。
- 其它
建议下次的结对编程不需要给出固定的题目,让结对的人自由发挥!