python輸出最大詞頻_TF-IDF詞頻逆文件頻次演算法
技術標籤:python輸出最大詞頻
TF-IDF演算法
一、TF-IDF演算法簡介
TF-IDF (詞頻-逆文件頻次) 演算法包含兩部分:TF演算法和IDF演算法。
頻次:一個單詞在某篇文件中出現的次數。
- TF演算法
TF(Term Frequency)演算法是統計一個詞在一篇文件中出現的頻次。
基本思想:一個詞在文件中出現的次數越多,其對文件的表達能力就越強。
特點:TF僅衡量詞的出現頻次,而沒有考慮到詞對文件的區分能力。
計算:tf(word)=(word在文件中出現的次數)/(文件總詞數)
tf計算時,僅用頻次來表示的話,長文字中的詞出現頻次高的概率會更大,這一點會影響到不同文件之間關鍵詞權值的比較。計算過程中,一般會對詞頻做歸一化處理,即分母一般為文件總詞數。分母也可以為該篇文件中詞出現最多的次數,程式碼中會判斷是sum或max。
2. IDF演算法
IDF(Inverse Document Frequency)演算法是統計一個詞在文件集的多少個文件中出現。
基本思想:一個詞在越少的文件中出現,則其對文件的區分能力也就越強。
特點:IDF強調詞的區分能力,但一個詞既然能夠在一篇文件中頻繁出現,表明這個詞能夠很好地表現該篇文件的特徵,忽略這一點顯然是不合理的。
計算:idf(word)=log{(文件集中文件總數量)/(word出現過的文件數量 + 1)}
idf計算時,分母加1是採用拉普拉斯平滑,避免有部分新的詞沒有在語料庫中出現過而導致分母為0的情況,增強演算法的健壯性。
3. TF-IDF演算法
TF-IDF演算法從詞頻、逆文件頻次兩個角度對詞的重要性進行度量。
基本思想:TF-IDF值越大,越適合為文件的關鍵詞。
特點:TF-IDF即考慮詞的出現頻次,也考慮詞對文件的區分能力。
計算:tf-idf(word)= tf(word)* idf(word)
說明:1) tf和idf是相加還是相乘,idf的計算是否取對數,經過大量的理論推導和試驗研究後,上述方式是較為有效的計算方式之一。
2) TF-IDF演算法可以用來進行關鍵詞提取。關鍵詞可以根據tf-idf值由大到小排序取TopN。
二、python實現TF-IDF演算法
1. 硬體系統: win10+anaconda37+pycharm
2. 資料準備
連結:https://pan.baidu.com/s/1X5FtrhhhCzlYC1-Y1jIPfQ
提取碼:a9oh
隨便下載2-5個txt檔案即可,為了測試看資料方便,自己可以將檔案中的內容刪除些,方便跑程式碼過程研究邏輯。資料已經用空格分割處理好的資料。
3. 程式碼邏輯
3.1) load_data函式:讀取資料,並對文件集中所有詞進行編碼,同時進行詞頻統計(預設sum);
3.2) idf函式:計算每篇文章中每個單詞的idf值;
3.3) doc_tf_idf函式:計算每篇文章中每個單詞的tf-idf值;
3.4) 對應某篇文件根據tf-idf值倒序取topN。
4. python實現
4.1) 變數簡介
doc_list:列表,[文件名列表],文件不重複
word_encoding:字典,單層,{word詞:word編碼},編碼不重複
word_idf:字典,單層,{ word編碼:idf值}},word編碼不重複
doc_word_freq_dict:字典,雙層,{文件名:{word編碼:word計數}},每篇文件中的每個word編碼不重複
doc_word_tfidf:字典,雙層,{文件名:{word編碼:tfidf值}}
4.2) 程式碼實現
# 測試資料路徑
file_path = './test_newsdata'
# word編碼字典 {單詞:單詞編碼}
word_encoding = dict()
def load_data(tf_denominator_type="sum"):
"""
載入新聞資料,並做詞頻統計(word count)
:return: doc_list [文件名列表]
doc_dict 每篇文件對應每個單詞編碼的詞頻
{文件名:word_freq} => {文件名:{單詞編碼:單詞計數}}
"""
doc_list = []
doc_word_freq_dict = dict()
i = 0 # 迴圈初始變數
for doc_names in os.listdir(file_path):
doc_name = doc_names.split('.')[0]
doc_list.append(doc_name)
# 用i來展示當前檔案讀取到哪裡了,類似於每讀取第10整數倍的文章數時,進行列印輸出(進度條的感覺)
if i % 10 == 0:
print("The {0} file had been loaded!".format(i), 'file loaded ...')
# 以可讀模式逐個開啟目錄下的檔案,並逐行讀取檔案中的word轉化成列表,以每篇文章為單位
with open(file_path + '/' + doc_names, 'r', encoding='utf-8') as f:
# word_freq 記錄每篇文章中的單詞編碼及出現次數 {word編碼: word計數}
word_freq = dict()
words_count_sum = 0 # 文件中單詞總數統計
words_count_max = 0 # 文件中單詞出現次數最大值
# 每行讀取並去除空格
for line in f.readlines():
words = line.strip().split(' ')
print('The line corresponds to words: {0}'.format(words))
for word in words:
# 空字串,此處也可以跳過停用詞(stop_list為停用詞集合,可百度得到,亦可自行增加)
# if len(word.strip()) < 1 or word in stop_list:
if len(word.strip()) < 1:
continue
# word編碼的邏輯:
# 1. 若word未曾出現在word_encoding編碼表中,則剛好以word_encoding長度向上增加,從0開始;
# 2. 否則直接進行下一步詞頻統計
if word_encoding.get(word, -1) == -1:
word_encoding[word] = len(word_encoding)
# word_encoding_id 記錄word對應的編碼id
word_encoding_id = word_encoding[word]
print('doc_name: {2}, word: {0}, word_encoding_id: {1} '.format(word, word_encoding_id, doc_name))
# 統計每篇文章中的word編碼所對應的詞頻
# 第一次出現的word編碼計1次,否則計數就+1
if word_freq.get(word_encoding_id, -1) == -1:
word_freq[word_encoding_id] = 1
else:
word_freq[word_encoding_id] += 1
print('doc_name: {0}, word_freq: {1}'.format(doc_name, word_freq))
# 文件中單詞出現次數的最大值
if word_freq[word_encoding_id] > words_count_max:
words_count_max = word_freq[word_encoding_id]
# 文件中單詞出現次數加和
words_count_sum += 1
# 計算tf值(計算佔比: 1. 分母為總單詞數;2、分母為單詞最大詞頻)
for word in word_freq.keys():
if tf_denominator_type == "sum":
word_freq[word] /= words_count_sum
if tf_denominator_type == "max":
word_freq[word] /= words_count_max
doc_word_freq_dict[doc_name] = word_freq
i += 1
return doc_word_freq_dict, doc_list
def idf(doc_word_freq_dict):
"""
idf: 統計每個單詞的逆文件詞頻,
計算idf值(idf = log(文件集的總數量/(文件出現次數+1)))
(當新單詞未出現在word編碼時,拉普拉斯平滑消除異常報錯)
:param: doc_word_freq_dict {文件名:{word編碼:word計數}}
:return: word_idf {word編碼: idf值}
"""
word_idf = {}
docs_num = len(doc_word_freq_dict) # 文件集總數量
# step1: 統計對應每個單詞在文件集中出現的文件數量
for doc in doc_word_freq_dict.keys():
# print("doc: {0}, doc_dict: {1}".format(doc, doc_dict[doc]))
for word_id in doc_word_freq_dict[doc].keys():
# 統計當前文件是否出現word編碼,第一次出現計1,否則+1
if word_idf.get(word_id, -1) == -1:
word_idf[word_id] = 1
else:
word_idf[word_id] += 1
# step2: 計算idf
for word_id in word_idf.keys():
word_idf[word_id] = math.log(docs_num/(word_idf[word_id] + 1))
return word_idf
def doc_tf_idf():
"""
實現tf*idf,計算每篇文章中對應每個單詞的tf-idf值
:return: doc_word_freq_dict {文件名:{單詞: tf-idf值}}
doc_list 文件名列表
"""
doc_word_freq_dict, doc_list = load_data()
word_idf = idf(doc_word_freq_dict)
doc_word_tfidf = dict()
for doc in doc_list:
word_tfidf = dict()
for word_id in doc_word_freq_dict[doc].keys():
word_tfidf[word_id] = doc_word_freq_dict[doc][word_id] * word_idf[word_id]
print('doc: {0}, word: {1}, tf: {2}, idf: {3}, tfidf: {4} '.format(doc,
word_id,
doc_word_freq_dict[doc][word_id],
word_idf[word_id],
word_tfidf[word_id]))
# print(word_tfidf)
doc_word_tfidf[doc] = word_tfidf
# print(len(doc_word_tfidf))
print('-----------------------------n我是優雅的分割線!n----------------------------')
print('doc_list: {0}'.format(doc_list))
print('word_encoding: {0}'.format(word_encoding))
print('word_idf: {0}'.format(word_idf))
print('doc_word_freq_dict: {0}'.format(doc_word_freq_dict))
print('doc_word_tfidf: {0}'.format(doc_word_tfidf))
return doc_word_tfidf, doc_list
doc_word_tfidf, doc_list = doc_tf_idf()
# 提取文件的關鍵詞Top10
# sorted(doc_word_tfidf['1000business'].items(), key=operator.itemgetter(1), reverse=True)[0:10]
sorted(doc_word_tfidf['1000business'].items(), key=lambda x: x[1], reverse=True)[0:10]
三、TF-IDF優缺點及改進方向
優點:僅考慮了詞的兩個統計資訊(頻次和在多少個文件中出現),衡量詞對文件的重要性。
缺點:對文字的資訊利用程度較低,比如詞的詞性、出現的位置等資訊。
改進方向:在關鍵詞提取過程中,結合場景,可以考慮以下方面:
1) 文件中的名詞,作為一種定義現實實體的詞,帶有更多的關鍵資訊,可對名詞賦予更高的權重;
2) 文中的起始段落和末尾段落比起其他部分的文字更重要,對出現在這些位置的詞賦予更高的權重。
備註:
無監督關鍵詞提取演算法,主要包括TF-IDF演算法、TextRank演算法和主題模型演算法(包括LSA、LSI、LDA等),以後有機會再繼續介紹。