corpus = [["我","a","e"],["我","a","c"],["我","a","b"]]
TFIDF全程叫做term frequency–inverse document frequency,翻译过来可以叫做文本频率与逆文档频率指数, TFIDF就是为了表征一个token(可以是一个字或者一个词)的重要程度,
所以如果这个token出现的频数很高,会更重要一点吧,这很好理解,那么出现的频数这个因素就叫做TF
但是问题来了,“的”,“了”,“吗”这种词,在文本中出现的频数也很高,那么是不是也很重要的,当然不是的,因为这些词在每一条文档中都会出现,所以就没那么重要了,在每个类别中都出现了“的”,我就没法把“的”作为我分类的一个重要指标,所以有了下一个概念,就是我这个词
尽可能的只在某几条文档中出现过,那样才会更有区分性对吧,所以逆文档频率指数应运而生,公式是:log(所有的文档条数/有这个词的文档条数),这个也比较好理解
分开解释:
文本频率,就是我统计的语料中词语出现的次数与所有词语的总数的比值,比如在上述例子中,一共有五个词(我,a,b,c,e)
所有词汇总数是5,其中“c”这个字出现了1次,所以“我”的文本频率是1/5,其他依次类推
逆文档频率,就是所有文档的条数与有某词的文档条数的比值的对数,比如上述例子中,文档总数是3条,出现“c”的是第二条,总共一条,所以“c”的
逆文档频率指数就是log(3/1),在实际操作中,我们会加平滑因子,防止统计数为0这种情况出现
我们知道了文本频率和逆文档频率指数那么我们把这两个结果相乘,就是这个词的权重,比如"c"的TFIDF值就是1/5 * log(3/1)
我们对每个词都做一下这样的计算,最后得到的是一个样品数量 * 唯一token总数维度的矩阵,在例子中样本数量为3,唯一token总数为5,那么我们会得到一个3*5的矩阵,如果这一条文档中没有这个词就直接赋值0就可以了。
下面是对上述词汇统计的矩阵:
()
统计个数:
{'我': 3, 'a': 3, 'e': 1, 'c': 1, 'b': 1}
token编号:
{'我': 0, 'a': 1, 'e': 2, 'c': 3, 'b': 4}
TFIDF矩阵
[[0.6 0.6 0.41972246 0. 0. ]
[0.6 0.6 0. 0.41972246 0. ]
[0.6 0.6 0. 0. 0.41972246]]
只说不练假把式,下面做了一个简单的实现:
完成代码
#!/usr/bin/env python
# encoding: utf-8
"""
@author: zkjiang
@site: https://www.github.com
@software: PyCharm
@file: TFIDF.py
@time: 2019/2/2 12:33
"""
import numpy as np
class TFIDF(object):
"""
手写一个TFIDF统计类,只写最简单的一个实现
"""
def __init__(self, corpus):
"""
初始化
self.vob:词汇个数统计,dict格式
self.word_id:词汇编码id,dict格式
self.smooth_idf:平滑系数,关于平滑不多解释了
:param corpus:输入的语料
"""
self.word_id = {}
self.vob = {}
self.corpus = corpus
self.smooth_idf = 0.01
def fit_transform(self, corpus):
pass
def get_vob_fre(self):
"""
计算文本特特征的出现次数,也就是文本频率term frequency,但是没有除token总数,因为后面bincount计算不支持float
:return: 修改self.vob也就是修改词频统计字典
"""
# 统计各词出现个数
id = 0
for single_corpus in self.corpus:
if isinstance(single_corpus, list):
pass
if isinstance(single_corpus, str):
single_corpus = single_corpus.strip("\n").split(" ")
for word in single_corpus:
if word not in self.vob:
self.vob[word] = 1
self.word_id[word] = id
id += 1
else:
self.vob[word] += 1
# 生成矩阵
X = np.zeros((len(self.corpus), len(self.vob)))
for i in range(len(self.corpus)):
if isinstance(self.corpus[i], str):
single_corpus = self.corpus[i].strip("\n").split(" ")
else:
single_corpus = self.corpus[i]
for j in range(len(single_corpus)):
feature = single_corpus[j]
feature_id = self.word_id[feature]
X[i, feature_id] = self.vob[feature]
return X.astype(int) # 需要转化成int
def get_tf_idf(self):
"""
计算idf并生成最后的TFIDF矩阵
:return:
"""
X = self.get_vob_fre()
n_samples, n_features = X.shape
df = []
for i in range(n_features):
"""
这里是统计每个特征的非0的数量,也就是逆文档频率指数的分式中的分母,是为了计算idf
"""
df.append(n_samples - np.bincount(X[:,i])[0])
df = np.array(df)
# perform idf smoothing if required
df += int(self.smooth_idf)
n_samples += int(self.smooth_idf)
idf = np.log(n_samples / df) + 1 # 核心公式
# print(self.vob)
# print(self.word_id)
return X*idf/len(self.vob)
if __name__ == '__main__':
corpus = [["我","a","e"],["我","a","c"],["我","a","b"]]
test = TFIDF(corpus)
# print(test.get_vob_fre())
print(test.get_tf_idf())