1. 程式人生 > 其它 >python輸出最大詞頻_TF-IDF詞頻逆文件頻次演算法

python輸出最大詞頻_TF-IDF詞頻逆文件頻次演算法

技術標籤:python輸出最大詞頻

TF-IDF演算法

一、TF-IDF演算法簡介

TF-IDF (詞頻-逆文件頻次) 演算法包含兩部分:TF演算法和IDF演算法。

頻次:一個單詞在某篇文件中出現的次數。

  1. 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等),以後有機會再繼續介紹。