使用 TF-IDF 算法将文本向量化

理解 TF-IDF 算法

TF-IDF 算法

TF-IDF 算法通过分配权重来反映每个词的重要程度,根据权重对一篇文章中的所有词语从高到低进行排序,权重越高说明重要性越高,排在前几位的词就可以作为这篇文章的关键词。所以 TF-IDF 算法可以用来提取关键词。

TF-IDF 全称为 term frequency–inverse document frequency
算法分为两部分: 词频(TF) 和 逆文档频率(IDF)

词频(TF) = 某个词在文章中的出现次数 / 文章总词数
逆文档频率(IDF) = log( 文章总数 / (包含该词的文章数+1) )

分成两部分理解的话就是,一个词的词频越高说明它越重要,逆文档频率越高说明它越普遍,越普遍则代表性越差。所以,词频与最终的权重呈正比,逆文档频率与最终的权重呈反比。

在使用 TF-IDF 算法之前,需要先对文本进行预处理,如分词、去除停用词。在运算之前,需要先统计每篇文章中的每个词语出现的次数。

统计词语出现次数

假设有一份包含三篇文章的数据,现在对三篇文章中的词语进行次数统计。下面使用的数据已经完成分词和去除停用词的步骤。

>>> docList = []
>>> for item in doc:
...   wordDic = {}
...   wordList = item.strip().split()
...   for word in wordList:
...     wordDic[word] = wordDic.setdefault(word, 0)+1
...   docList.append(wordDic)
... 
>>> len(docList)
3
>>> docList[0]
{'图书': 34, '评论': 12, '重视': 2, '书籍装帧': 1, '艺术': 26, '评价正文': 1, '近代': 1, '报刊': 2, '业': 1, '兴起': 1, '世界': 1, '各国': 1, '长足发展': 1, '一种': 4, '新型': 1, '体裁': 1, '不论是': 1, '书评': 17, '理论': 1, '实践': 1, '不小': 1, '疏漏': 1, '忽视': 6, '形式': 11, '因素': 7, '内容': 13, '综合体': 2, '这一': 3, '导致': 2, '活动': 4, '中': 8, '出版': 5, '品评'}
>>> 

docList 是一个列表,包含整份数据(包含多篇文章)的信息;其中,列表的元素是字典类型,即列表包含多个字典元素,其中字典的结构为 词语:出现次数 ,所以,每个列表储存着一篇文章中词语出现次数的信息

def countWord(doc):
    '''
    依次对所有文章进行统计,统计每篇文章中每个词的出现次数
    doc: list  列表中一个元素为一篇文章的文本数据,str类型,空格间隔,含换行符
    '''
    docList = []
    for item in doc:
        wordDic = {}
        wordList = item.strip().split()  #将字符串转换成列表,一个元素一个词
        for word in wordList:
            wordDic[word] = wordDic.setdefault(word, 0)+1
        docList.append(wordDic)
    return docList

计算词频(TF)

词频(TF) = 某个词在文章中的出现次数 / 文章总词数

例如:
文章一:'图书': 34, '评论': 12, '重视': 2
文章二:'评论': 7,  '活动': 4,  '出版': 5
文章三:'导致': 2,  '图书': 12, '评论': 9

则 词频(TF) 为:
文章一:'图书': 34/(34+12+2), '评论': 12/(34+12+2), '重视': 2/(34+12+2)
文章二:'评论': 7/(7+4+5),  '活动': 4/(7+4+5),  '出版': 5/(7+4+5)
文章三:'导致': 2/(2+12+9),  '图书': 12/(2+12+9), '评论': 9/(2+12+9)
def computeTF(wordDic):
    '''
    计算一篇文章中每个词的词频
    wordDic: dict 为 docList 的元素
    '''
    #计算 total
    total = sum(wordDic.values())
    #计算词频
    tfDic = {}
    for word, value in wordDic.items():
        tfDic[word] = value / total

    return tfDic

计算逆文档频率(IDF)

逆文档频率(IDF) = log( 文章总数 / (包含该词的文章数+1) )

例如:
文章一:'图书': 34, '评论': 12, '重视': 2
文章二:'评论': 7,  '活动': 4,  '出版': 5
文章三:'导致': 2,  '图书': 12, '评论': 9

则 逆文档频率(IDF) 为:
'图书': log(3/(2+1)), '评论': log(3/(3+1)), '重视': log(3/(1+1)),  '活动': log(3/(1+1)),  '出版': log(3/(1+1)), '导致': log(3/(1+1))

针对 '评论': log(3/(3+1)) 这个数据:
分子 3 是一共有三篇文章;分母 3 是其中有三篇文章包含了词语 '评论';分母 1 是防止分母为 0 的一种做法,如果能保证分母不为 0,此处不加一也无妨。
def computeIDF(docList):
    '''
    计算每个词的逆文档频率
    docList: list
    '''
    #计算 total
    total = len(docList)
    #计算逆文档频率
    idfDic = {}
    for wordDic in docList:
        for word, value in wordDic.items():
            if value > 0:
                idfDic[word] = idfDic.setdefault(word, 0)+1
    for word, value in idfDic.items():
        idfDic[word] = math.log(total/value+1)  #要先引入 math 库

    return idfDic

计算TF-IDF

TF-IDF = 词频(TF) * 逆文档频率(IDF)

对于每篇文章,将文章中的每个词对应的词频和逆文档频率相乘,结果就是 TF-IDF 的值

def computeTFIDF(doc):
    '''
    计算 TF-IDF 值
    doc: list
    '''
    docList = countWord(doc)
    idfDic = computeIDF(docList)
    tfidf = []
    for wordDic in docList:
        tfDic = computeTF(wordDic)
        for word, value in tfDic.items():
            tfDic[word] = tfDic.setdefault(word) * idfDic.setdefault(word)
        tfidf.append(tfDic)
    return tfidf

比如得到像下面这样的结果:

{'图书': 0.2325942849792562, '评论': 0.06181160435625407, '重视': 0.005153575532783829, '书籍装帧': 0.013093886622905858, '艺术': 0.09805658420000751, '评价正文': 0.014042067245293792, '近代': 0.004887464652957998, '报刊': 0.012632288317729841, '业': 0.005769296438219885, '兴起': 0.0043842616102319565, '世界': 0.0021243970875383877, '各国': 0.00329842781709159}