0x01 TF-IDF介绍

 

  TF-IDF(Term Frequency–Inverse Document Frequency)是一种用于资讯检索与文本挖掘的常用加权技术。TF-IDF是一种统计方法,用以评估一个字词对于一个文件集或一个语料库中的其中一份文件的重要程度。字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降。TF-IDF加权的各种形式常被搜索引擎应用,作为文件与用户查询之间相关程度的度量或评级

 

如果某个词或短语在一篇文章中出现的频率TF高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类。TF-IDF实际上是:TF * IDF

 

  (1)词频(Term Frequency,TF)指的是某一个给定的词语在该文件中出现的频率。即词w在文档d中出现的次数count(w, d)和文档d中总词数size(d)的比值。

tf(w,d) = count(w, d) / size(d)

同一个词语在长文件里可能会比短文件有更高的词数,而不管该词语重要与否。)

 

  (2)逆向文件频率(Inverse Document Frequency,IDF)是一个词语普遍重要性的度量。某一特定词语的IDF,可以由总文件数目除以包含该词语之文件的数目,再将得到的商取对数得到。即文档总数n与词w所出现文件数docs(w, D)比值的对数。  

idf = log(n / docs(w, D))

  TF-IDF根据 tf 和 idf 为每一个文档d和由关键词w[1]...w[k]组成的查询串q计算一个权值,用于表示查询串q与文档d的匹配度:

tf-idf(q, d)
= sum { i = 1..k | tf-idf(w[i], d) }
= sum { i = 1..k | tf(w[i], d) * idf(w[i]) }

  某一特定文件内的高词语频率,以及该词语在整个文件集合中的低文件频率,可以产生出高权重的TF-IDF。因此,TF-IDF倾向于过滤掉常见的词语,保留重要的词语。

  关于TF-IDF的详细介绍和例子,有兴趣的同学可以看这一篇博客。  下面主要分享TF-IDF在Python的如何使用。

 

0x02 Python中计算TF-IDF

 

  在Python中,scikit-learn包下有计算TF-IDF的api,其效果也很不错。首先得安装Scikit-clearn。不同系统安装请看:https://scikit-learn.org/stable/install.html。

  1. 安装scikit-learn包(先安装依赖包,再安装sklearn)

  2. 安装jieba分词包

  由于计算TF-IDF是对分词结果进行计算,所以这里需要使用jieba中文分词。

  3. 计算TF-IDF

  scikit-learn包进行TF-IDF分词权重计算主要用到了两个类:CountVectorizerTfidfTransformer。其中

  CountVectorizer是通过fit_transform函数将文本中的词语转换为词频矩阵,矩阵元素a[i][j] 表示j词在第i个文本下的词频。即各个词语出现的次数,通过get_feature_names()可看到所有文本的关键字,通过toarray()可看到词频矩阵的结果。简例如下:

>>> from sklearn.feature_extraction.text import CountVectorizer
>>> vectorizer = CountVectorizer()
>>> corpus = [
...     'This is the first document.',
...     'This is the second second document.',
...     'And the third one.',
...     'Is this the first document?',
... ]
>>> X = vectorizer.fit_transform(corpus)
>>> X.toarray()           
array([[0, 1, 1, 1, 0, 0, 1, 0, 1],
       [0, 1, 0, 1, 0, 2, 1, 0, 1],
       [1, 0, 0, 0, 1, 0, 1, 1, 0],
       [0, 1, 1, 1, 0, 0, 1, 0, 1]]...)
>>> vectorizer.get_feature_names()
(['and', 'document', 'first', 'is', 'one', 'second', 'the', 'third', 'this'])

 

  TfidfTransformer是统计vectorizer中每个词语的tf-idf权值,用法如下:

>>> from sklearn.feature_extraction.text import CountVectorizer
>>> transformer = TfidfTransformer()
>>> counts = [[3, 0, 1],
...           [2, 0, 0],
...           [3, 0, 0],
...           [4, 0, 0],
...           [3, 2, 0],
...           [3, 0, 2]]
>>> tfidf = transformer.fit_transform(counts)
>>> tfidf.toarray()                        
array([[ 0.85...,  0.  ...,  0.52...],
       [ 1.  ...,  0.  ...,  0.  ...],
       [ 1.  ...,  0.  ...,  0.  ...],
       [ 1.  ...,  0.  ...,  0.  ...],
       [ 0.55...,  0.83...,  0.  ...],
       [ 0.63...,  0.  ...,  0.77...]])

  关于函数的具体说明,请看官方说明文档:scikit-learn common-vectorizer-usage

 

  这里我处理的是对100份文档进行分词,然后进行TF-IDF的计算,其效果相当好。

import os import jieba import jieba.posseg as pseg import sys import string from sklearn import feature_extraction from sklearn.feature_extraction.text import TfidfTransformer from sklearn.feature_extraction.text import CountVectorizer reload(sys) sys.setdefaultencoding('utf8') #获取文件列表(该目录下放着100份文档) def getFilelist(argv) :     path = argv[1]     filelist = []     files = os.listdir(path)     for f in files :         if(f[0] == '.') :             pass         else :             filelist.append(f)     return filelist,path #对文档进行分词处理 def fenci(argv,path) :     #保存分词结果的目录     sFilePath = './segfile'     if not os.path.exists(sFilePath) :          os.mkdir(sFilePath)     #读取文档     filename = argv     f = open(path+filename,'r+')     file_list = f.read()     f.close()          #对文档进行分词处理,采用默认模式     seg_list = jieba.cut(file_list,cut_all=True)     #对空格,换行符进行处理     result = []     for seg in seg_list :      seg = ''.join(seg.split())         if (seg != '' and seg != "\n" and seg != "\n\n") :             result.append(seg)     #将分词后的结果用空格隔开,保存至本地。比如"我来到北京清华大学",分词结果写入为:"我 来到 北京 清华大学"     f = open(sFilePath+"/"+filename+"-seg.txt","w+")     f.write(' '.join(result))     f.close() #读取100份已分词好的文档,进行TF-IDF计算 def Tfidf(filelist) :   path = './segfile/'     corpus = []  #存取100份文档的分词结果     for ff in filelist :         fname = path + ff         f = open(fname,'r+')         content = f.read()         f.close()         corpus.append(content)         vectorizer = CountVectorizer()         transformer = TfidfTransformer()     tfidf = transformer.fit_transform(vectorizer.fit_transform(corpus))          word = vectorizer.get_feature_names() #所有文本的关键字     weight = tfidf.toarray()              #对应的tfidf矩阵          sFilePath = './tfidffile'     if not os.path.exists(sFilePath) :          os.mkdir(sFilePath)     # 这里将每份文档词语的TF-IDF写入tfidffile文件夹中保存     for i in range(len(weight)) :      print u"--------Writing all the tf-idf in the",i,u" file into ",sFilePath+'/'+string.zfill(i,5)+'.txt',"--------"         f = open(sFilePath+'/'+string.zfill(i,5)+'.txt','w+')         for j in range(len(word)) :             f.write(word[j]+"    "+str(weight[i][j])+"\n")         f.close()              if __name__ == "__main__" :      (allfile,path) = getFilelist(sys.argv)   for ff in allfile :         print "Using jieba on "+ff         fenci(ff,path)     Tfidf(allfile)