一、基本概念
词频(TF,Term Frequency):一篇文章中某个词的出现次数。TF=某个词在文章中出现的次数/文章的总词数,或者TF=某个词在文章中出现的次数/该文章出现次数最多的词的次数。
T
F
=
某
个
词
在
文
章
中
出
现
的
次
数
文
章
的
总
词
数
=
某
个
词
在
文
章
中
出
现
的
次
数
该
文
章
出
现
次
数
最
多
的
词
的
次
数
TF=\frac{某个词在文章中出现的次数}{文章的总词数}=\frac{某个词在文章中出现的次数}{该文章出现次数最多的词的次数}
TF=文章的总词数某个词在文章中出现的次数=该文章出现次数最多的词的次数某个词在文章中出现的次数
反文档频率IDF:在词频的基础上,赋予每个词的权重,进一步体现该词的重要性。IDF=log([语料库的文档总数]/[包含该词的文档数+1])。
I
D
F
=
l
o
g
语
料
库
的
文
档
总
数
包
含
该
词
的
文
档
数
+
1
IDF=log\frac{语料库的文档总数}{包含该词的文档数+1}
IDF=log包含该词的文档数+1语料库的文档总数
TF-IDF=词频(TF)* 反文档频率(IDF),可以看出TF-IDF与一个词在文档中出现的次数成正比,与包含该词的文档数成反比。某个词对文章的重要性越高,该值越大,于是排在前面的几个词,就是这篇文章的关键词。
TF-IDF可运用于计算两篇文章的相似度:
- 使用TF-IDF算法,找出两篇文章的关键词;
- 每篇文章各取出若干个关键词(比如20个),合并成一个集合(不重复),计算每篇文章对于这个集合中的词的词频;
- 生成两篇文章各自的词频向量(两个向量每个维的顺序要保持一致,可与集合顺序一致);
- 计算两个向量的余弦相似度,值越大就表示越相似。
二、IDF实践
实践中选取已进行中文分词后的508篇文章,其内容如下:
- convert.py:由于508篇文章需要频繁的读取每一篇,增加了磁盘IO操作,故先将其内容合并为一个文件中,合并代码如下。
import os
import sys
file_path_dir = sys.argv[1] #获取存放508篇文章的目录
def read_file_handler(f):
fd = open(f, 'r')
return fd
file_name = 0
for fd in os.listdir(file_path_dir):
file_path = file_path_dir + '/' + fd #获取每篇文章的路径
content_list = [] % 存放每篇文章的内容
file_fd = read_file_handler(file_path)
for line in file_fd:
content_list.append(line.strip()) #将每篇文章里的所有行内容去首位空格和换行符后添加进List集合
print '\t'.join([str(file_name), ' '.join(content_list)]) # 格式:文章id + ‘\t’ + 该文章第一行 + ' ' + 该文章第二行...
file_name += 1
然后执行以下命令,将所有文章合并到idf_input.data文件中,该文件的每一行为每一篇文章的内容,共508行
[root@master tfidf_python]# python convert.py input_tfidf_dir > idf_input.data
- map.py:对所有文章的单词作map处理,每篇文章的单词需作去重处理,这样再对每个单词后添置1,表示包含该单词的文章数。
import sys
for line in sys.stdin:
ss = line.strip().split('\t')
if len(ss) != 2:
continue
file_name, file_content = ss
word_list = file_content.strip().split(' ')
word_set = set(word_list) #去重
for word in word_set:
print '\t'.join([word, '1'])
map的输出结果部分如下:
- reduce.py:对map输出的结果,合并相同的单词key的value值,统计包含该词的文档总数,计算idf值,reduce的前提是map的输出结果已按key(word)进行了排序。
import sys
import math
current_word = None
sum = 0
docs_cnt = 508
for line in sys.stdin:
ss = line.strip().split('\t')
if len(ss) != 2:
continue
word, val = ss
if current_word == None: # 遍历第一行map结果
current_word = word
if current_word != word: # 遍历到不同的word,统计当前word的IDF值
idf = math.log(float(docs_cnt) / (float(sum) + 1.0))
print '\t'.join([current_word, str(idf)])
current_word = word
sum = 0 # 记录当前word被多少篇文章包含
sum += int(val)
idf = math.log(float(docs_cnt) / (float(sum) + 1.0))
print '\t'.join([current_word, str(idf)]) %打印最后一个单词的idf值
通过以下命令测试mapreduce:加入了人工排序,模拟MapReduce的shuffle里的排序
[root@master tfidf_python]# cat idf_input.data | python map.py | sort -k1 | python red.py > red.tmp
- 结果: