TF-IDF(Term Frequency–Inverse Document Frequency)是一种用于资讯检索与文本挖掘的常用加权技术。TF-IDF是一种统计方法,用以评估一个字词对于一个文件集或一个语料库中的其中一份文件的重要程度。字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降。TF-IDF加权的各种形式常被搜索引擎应用,作为文件与用户查询之间相关程度的度量或评级。
TF-IDF的主要思想是:如果某个词或短语在一篇文章中出现的频率TF高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类。TF-IDF实际上是:TF * IDF。
(1)词频(Term Frequency,TF)指的是某一个给定的词语在该文件中出现的频率。即词w在文档d中出现的次数count(w, d)和文档d中总词数size(d)的比值。
这个数字是对词数(term count)的归一化,以防止它偏向长的文件。(同一个词语在长文件里可能会比短文件有更高的词数,而不管该词语重要与否。)
(2)逆向文件频率(Inverse Document Frequency,IDF)是一个词语普遍重要性的度量。某一特定词语的IDF,可以由总文件数目除以包含该词语之文件的数目,再将得到的商取对数得到。即文档总数n与词w所出现文件数docs(w, D)比值的对数。
某一特定文件内的高词语频率,以及该词语在整个文件集合中的低文件频率,可以产生出高权重的TF-IDF。因此,TF-IDF倾向于过滤掉常见的词语,保留重要的词语。
上述转自:TF-IDF算法介绍及实现_Asia-Lee-CSDN博客_tf-idf
抛去上述概念,本博客希望实现如下操作:
最终效果如下图:
1、加载文章集
文章集目前收集了20多篇300词左右的英文文档,其中一半200词左右的英语作文和阅读,一半英语期刊文章,并按篇分别存于txt文件中
在按篇读取时同时完成:去除非字母的字符、统一转为小写、按空格分词,再将文章的所有单词存入一个列表中,得到的结果如下:
import math
import os
import re
import matplotlib.pyplot as plt
def loadDataSet():
path="./text"
files=os.listdir(path)
print("text_lists")
text_lists=[]
for index,file in enumerate(files):
with open(os.path.join(path,file),"r") as f:
text=f.read()
text=re.sub('\W',' ',text).lower().split()
text_lists.append(text)
print("第{}篇文章{}词".format(index, len(text)))
print(text)
return text_lists
2、获取词符字典
整个文档集共用一个词符字典,字典键值为单词,值为一个列表,长度等于文章数,其中每个值记录对应文章中该词出现的次数,得到的字典结果如下:
def get_word_dictionary(text_lists):
print("word_dictionary")
word_dictionary={}
for index,text in enumerate(text_lists):
for word in text:
if word not in word_dictionary.keys():
word_num=[0]*len(text_lists)
word_num[index]=1
t={word:word_num}
word_dictionary.update(t)
else:
word_dictionary[word][index]+=1
print(word_dictionary)
return word_dictionary
3、计算tf和idf值
按照之前图中的要求计算每个单词的tf和idf值,仍然以字典的形式存放每个单词的tf和idf值,单词为键值,该词的tf和idf组成列表作为该键值对应的值,并在最后以tf值的大小对整个字典进行排序,方便后续输出,得到的字典如下:
print("计算tf,idf")
tf_idf_list={}
for word in word_dictionary.keys():
a=0
b=0
for i in word_dictionary[word]:
a+=i
tf=1+math.log2(a)
for i in word_dictionary[word]:
if i != 0:
b += 1
idf = math.log2(len(text_lists) / b)
t={word:[tf,idf]}
tf_idf_list.update(t)
print(tf_idf_list)
print("按tf排序")
tf_idf_list=dict(sorted(tf_idf_list.items(), key=lambda item: item[1][0], reverse=True))
print(tf_idf_list)
4、绘制点图
根据上述获得的文章数列、词符字典,以单词序号为横轴,tf、idf、tf*idf值为纵轴绘图
#绘图
x_index=[]
y_tf=[]
y_idf=[]
y_tf_idf=[]
for index,word in enumerate(tf_idf_list.keys()):
x_index.append(index)
y_tf.append(tf_idf_list[word][0])
y_idf.append(tf_idf_list[word][1])
y_tf_idf.append(tf_idf_list[word][0]*tf_idf_list[word][1])
plt.subplot(1, 2, 1)
g1 = plt.scatter(x_index, y_tf, c='red',s=1)
g2 = plt.scatter(x_index, y_idf, c='blue',s=1)
plt.legend(handles=[g1, g2], labels=['tf', 'idf'])
plt.subplot(1, 2, 2)
g3=plt.scatter(x_index, y_tf_idf, c='red',s=1)
plt.legend(handles=[g3], labels=['tf*idf'])
plt.show()
最终获得的图像如下,总体趋势与书本上相同,但仍有很大差别,主要应该是受文本集大小的影响,原作中包含将近10w篇文章,有1e+06个词符,分散比较均匀,而我的文本集目前只包含了25篇300词左右的英语文章,很大一部分词符只在一篇或极少数的文章中出现,分布极不均匀,比如从1000词符左右开始tf都等于1,因为该词只出现过一次,同时也导致idf值为最大,最终导致整体结果都相对集中。另外,因为大部分是在中文网上找的英语文章,很多基础词汇被反复利用,这应该也是tf值过分集中的原因。
完整代码见:
数据集见:
仅代表个人做法,有误欢迎指正