作業 4:詞頻統計——基本功能
一、基本信息
1.本次作業的地址: https://edu.cnblogs.com/campus/ntu/Embedded_Application/homework/2088
2.項目Git的地址:https://gitee.com/ntucs/PairProg/tree/SE016_017
3.開發環境:Pycharm2018、Python3.6
4.結對成員:1613072016 高耀、1613072017 錢金
二、項目分析
1.程序運行模塊(方法、函數)介紹
1.1Task1:
(1)讀文件到緩沖區,統計文本行數(process_file(dst))
def process_file(dst): # 讀文件到緩沖區,統計文本行數try: # 打開 file = open(dst, ‘r‘) # dst為文本的目錄路徑 except IOError as e: print(e) return None try: # 讀文件到緩沖區,統計文本行數 lines = len(file.readlines()) # 關閉文件,重新打開 file.close() file = open(dst, "r") bvffer = file.read() except: print("Read File Error!") return None file.close() return bvffer, lines
(2)處理緩沖區,返回存放每個單詞頻率的字典word_freq,單詞總數(process_buffer(bvffer))
def process_buffer(bvffer): # 處理緩沖區,返回存放每個單詞頻率的字典word_freq,單詞總數 if bvffer: word_freq = {} # 將文本內容都小寫 bvffer = bvffer.lower() # 用空格消除文本中標點符號 words= bvffer.replace(punctuation, ‘ ‘).split(‘ ‘) # 正則匹配至少以4個英文字母開頭,跟上字母數字符號,單詞以分隔符分割,不區分大小寫 regex_word = "^[a-z]{4}(\w)*" for word in words: if re.match(regex_word, word): # 數據字典已經存在該單詞,數量+1 if word in word_freq.keys(): word_freq[word] = word_freq[word] + 1 # 不存在,把單詞存入字典,數量置為1 else: word_freq[word] = 1 return word_freq, len(words)
(3) 按照單詞的頻數排序,返回前十的單詞組(output_result(word_freq))
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]: # 輸出 Top 10 的單詞 print(‘<‘ + str(item[0]) + ‘>:‘ + str(item[1])) return sorted_word_freq[:10]
(4)保存結果到文件result.txt(save_result(lines, words, items))
def save_result(lines, words, items): # 保存結果到文件(result.txt) try: result = open("result.txt", "w") # 以寫模式打開,並清空文件內容 except Exception as e: result = open("result.txt", "x") # 文件不存在,創建文件並打開 # 寫入文件result.txt result.write("lines:" + lines + "\n") result.write("words:" + words + "\n") for item in items: item = ‘<‘ + str(item[0]) + ‘>:‘ + str(item[1]) + ‘\n‘ result.write(item) print(‘寫入result.txt已完成‘) result.close()
(5) 主函數入口
if __name__ == "__main__": # 命令行傳遞參數 parser = argparse.ArgumentParser() parser.add_argument(‘dst‘) args = parser.parse_args() dst = args.dst bvffer, lines = process_file(dst) word_freq, words = process_buffer(bvffer) items = output_result(word_freq) # 把lines、words類型強制轉化為str lines = str(lines) words = str(words) save_result(lines, words, items)
1.2Task2:
(1)停詞表模塊(在Task1的基礎上,在process_buffer(buffer)函數內增加語句讀取stopwords.txt內容並實現跳過這些單詞輸出)
def process_buffer(bvffer): # 處理緩沖區,返回存放每個單詞頻率的字典word_freq,單詞總數 if bvffer: word_freq = {} # 將文本內容都小寫 bvffer = bvffer.lower() # 用空格消除文本中標點符號 words = bvffer.replace(punctuation, ‘ ‘).split(‘ ‘) # 正則匹配至少以4個英文字母開頭,跟上字母數字符號,單詞以分隔符分割,不區分大小寫 regex_word = "^[a-z]{4}(\w)*" # 停詞表模塊 txtWords = open("stopwords.txt", ‘r‘).readlines() # 讀取停詞表文件 stopWords = [] # 存放停詞表的list # 讀取文本是readlines所以寫入list要將換行符取代 for i in range(len(txtWords)): txtWords[i] = txtWords[i].replace(‘\n‘, ‘‘) stopWords.append(txtWords[i]) for word in words: if word not in stopWords: # 當單詞不在停詞表中時,使用正則表達式匹配 if re.match(regex_word, word): # 數據字典已經存在該單詞,數量+1 if word in word_freq.keys(): word_freq[word] = word_freq[word] + 1 # 不存在,把單詞存入字典,數量置為1 else: word_freq[word] = 1 return word_freq, len(words)
(2)高頻詞組模塊(nltk的安裝就不再贅述,使用nltk.collocations下的兩個類BigramCollocationFinder、 TrigramCollocationFinder,在Task1的基礎上增加
def word_group(dst)函數實現對二元詞組和三元詞組的統計)【參考博客:http://www.cnblogs.com/no-tears-girl/p/7096519.html】
def word_group(dst): # 統計高頻的二元詞組、三元詞組BigramCollocationFinder、 TrigramCollocationFinder str = open(dst, ‘r‘).read() # 用空格消除文本中標點符號 wordGroup = str.lower() tokens = nltk.wordpunct_tokenize(wordGroup) # 二元詞組 finder = nltk.collocations.BigramCollocationFinder.from_words(tokens) # 過濾掉符合條件fn的詞組 finder.apply_word_filter(lambda x: x in [‘,‘, ‘.‘, ‘’‘, ‘“‘, ‘”‘, ‘\‘‘, ‘"‘, ‘,"‘, ‘,”‘]) print("頻率前五的二元詞組") # 這裏的key是排序依據,就是說先按t[1](詞頻)排序,-表示從大到小;再按照詞組(t[0])排序,默認從a-z. print(sorted(finder.ngram_fd.items(), key=lambda t: (-t[1], t[0]))[:5]) # 三元詞組 finder = nltk.collocations.TrigramCollocationFinder.from_words(tokens) # 過濾掉符合條件fn的詞組 finder.apply_word_filter(lambda x: x in [‘,‘, ‘.‘, ‘’‘, ‘“‘, ‘”‘, ‘\‘‘, ‘"‘, ‘,"‘, ‘,”‘]) print("頻率前五的三元詞組") print(sorted(finder.ngram_fd.items(), key=lambda t: (-t[1], t[0]))[:5])
2.程序算法的時間、空間復雜度分析
(1)以下面的代碼為例進行分析
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]: # 輸出 Top 10 的單詞 print(‘<‘ + str(item[0]) + ‘>:‘ + str(item[1])) return sorted_word_freq[:10]
時間復雜度:sort函數使用的Timsort算法,其最Best Case下時間復雜度為O(n),Average Case下時間復雜度為O(nlog2n),Worst Case下時間復雜度為O(nlog2n);
for循環的時間復雜度為O(n);所以該段代碼的時間復雜度:Best Case:O(n), Average Case:O(nlog2n) ,Worst Case:O(nlog2n)
空間復雜度:sort函數使用的Timsort算法空間復雜度為O(n),for循環的空間復雜度也為O(n),所以該段代碼的空間復雜度為O(n)
3.程序運行案例截圖
(1)Task1 基本任務:result.txt截圖
(2)Task2.1 停詞功能:stopwords.txt截圖、result.txt截圖(自定義停用詞,該處為與Task1結果對比,挑選上圖結果的一部分單詞作為停用詞)
(3)Task2.2 高頻詞組
三、性能分析(把主函數的語句用main()函數封裝,在主函數運行性能測試代碼)
1.Task1
(1)運行時間
(2)性能圖表(result.out->result.png)
2.Task2
(1)運行時間
(2)性能圖表(result1.out->result1.png)
四、其他
1.結對編程時間開銷
PSP | 任務內容 | 預估耗時(min) | 實際耗時(min) |
Planning | 計劃 | 40 | 50 |
Coding Standard | 代碼規範 | 10 | 10 |
Coding | 具體編碼 | 180 | 200 |
Code Review | 代碼復審 | 30 | 25 |
Test And Improve | 性能測試及改進 | 40 | 50 |
Reporting | 撰寫博客以及push碼雲 | 100 | 100 |
Postmortem | 總結 | 10 | 15 |
合計 | 410 | 450 |
2.結對編程照片
五、事後分析與總結
1.簡述關於停詞表部分的決策過程
一開始準備使用nltk中的停詞表,但是考慮到時間開銷以及停詞效果顯示方面的原因,我們選擇創建stopwords.txt,自定義停詞內容,然後通過代碼讀取txt文檔中停用詞,在輸出詞頻前十的過程中進行剔除停用詞。
2.評價
(1)錢金評價高耀:①高耀同學學習能力強、對於不熟悉的方法上手很快。表現在:Task2中要求實現統計高頻短語,高耀同學快速掌握了 nltk(Natural LanguageToolkit,自然語言處理工具包)的使用方法。②高耀同學做事認真且有條理、但是嚴謹性需加強。表現在:編寫博客層次清晰有條理,但是對於代碼的容錯機制欠缺。
(2)高耀評價錢金:①錢金同學思維活躍、常常想出好的方法。表現在:Task2中要求實現統計高頻短語,錢金同學想出使用正則表達式的方法來匹配短語。②錢金同學的基礎代碼能力欠缺,需要繼續努力。
3.關於結對過程的建議
(1)可以以自由結對編程的形式開展,同學自主尋找的結對夥伴彼此比較熟悉能夠更有效率地開展結對編程工作;當然,不太熟悉的人結對編程也有一定好處,如可以鍛煉個人的與他人交流溝通、相處之道。
4.其他
(1)大多數人偏好 “單打獨鬥”,只願一個人完成整個功能的實現,而忽視了這種做法存在的致命缺陷——主觀意願過強;而結對作業不僅提供了一個能從客觀角度幫助你分析的隊友,還提供了一種結對編程形式的模板——其中一人主攻編碼模塊,另一人則更客觀提出編碼意見促進編碼實現,這也算是一種不可多得的編程體驗吧。
作業 4:詞頻統計——基本功能