文本词频统计是字典吗_TF-IDF词频逆文档频次算法

本文介绍了TF-IDF算法的基本概念、计算方法以及在关键词提取中的应用。TF-IDF结合词频和逆文档频率衡量词的重要性。文章提供了一个Python实现的简单流程,包括数据读取、词频统计、IDF计算和TF-IDF值的求解。最后讨论了TF-IDF的优缺点及可能的改进方向。
摘要由CSDN通过智能技术生成

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等),以后有机会再继续介绍。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值