TF-IDF原理簡(jiǎn)介
jieba分詞提取關(guān)鍵詞是按照詞頻(即每個(gè)詞在文章中出現(xiàn)的次數(shù))來(lái)提取的,比如要提取文章的前五個(gè)關(guān)鍵詞,那么就是提取文章中出現(xiàn)次數(shù)最多的前五個(gè)詞。而TF-IDF算法不僅統(tǒng)計(jì)每個(gè)詞的詞頻,還為每個(gè)詞加上權(quán)重。
這里我很容易就聯(lián)想到了概率論中均值和數(shù)學(xué)期望的。舉個(gè)例子,我們?cè)诖髮W(xué)選修了數(shù)學(xué)和體育兩門課,數(shù)學(xué)為9學(xué)分,體育為1學(xué)分,期末的時(shí)候考試成績(jī)分別為60和100分,那么如果我們說(shuō)平均分是80分合理嗎?其實(shí)是不合理的,因?yàn)橐粋€(gè)9學(xué)分,一個(gè)1學(xué)分,我們投入的時(shí)間和精力是不一樣的,所以應(yīng)該用(9/10*60)+(1/10*100)=64分這樣更為合理一些,這里80分是平均值,64分是數(shù)學(xué)期望,所以我們也說(shuō)數(shù)學(xué)期望是加權(quán)的平均值。詞頻和加權(quán)的詞頻通過(guò)這個(gè)聯(lián)想應(yīng)該能更好的理解了。以上就是我對(duì)這個(gè)算法的理解。
TF-IDF計(jì)算公式如下:
TF = 該詞在文檔中出現(xiàn)的次數(shù)
IDF = log2(文檔總數(shù)/包含該詞的文檔數(shù)量 + 1)
TF-IDF = TF * IDF
開發(fā)環(huán)境
系統(tǒng): macOS Sierra; 開發(fā)軟件: PyChram CE; 運(yùn)行環(huán)境: Python3.6
-
導(dǎo)入所需用的包
import os
import codecs
import pandas
import re
import jieba
import numpy
-
創(chuàng)建語(yǔ)料庫(kù)
# 創(chuàng)建語(yǔ)料庫(kù)
filePaths = []
fileContents = []
for root, dirs, files in os.walk(
'data/SogouC.mini/Sample'
):
for name in files:
filePath = os.path.join(root, name)
f = codecs.open(filePath, 'r', 'utf-8')
fileContent = f.read()
f.close()
filePaths.append(filePath)
fileContents.append(fileContent)
corpus = pandas.DataFrame({
'filePath': filePaths,
'fileContent': fileContents
})
運(yùn)行結(jié)果如下:

語(yǔ)料庫(kù).png
-
分詞
# 匹配中文分詞
zhPattern = re.compile(u'[\u4e00-\u9fa5]+')
# 分詞
segments = []
filePaths = []
for index, row in corpus.iterrows(): # 對(duì)語(yǔ)料庫(kù)按行進(jìn)行遍歷
filePath = row['filePath']
fileContent = row['fileContent']
segs = jieba.cut(fileContent)
for seg in segs:
if zhPattern.search(seg): # 匹配中文分詞
segments.append(seg)
filePaths.append(filePath)
segmentDF = pandas.DataFrame({
'segment': segments,
'filePath': filePaths
})
運(yùn)行結(jié)果如下:

分詞.png
-
停用詞過(guò)濾
# 停用詞過(guò)濾
stopWords = pandas.read_csv( # 讀取停用詞表
'data/StopwordsCN.txt',
encoding='utf-8',
index_col=False,
quoting=3,
sep='\t'
)
segmentDF = segmentDF[~segmentDF['segment'].isin(stopWords['stopword'])]
-
按文章進(jìn)行詞頻統(tǒng)計(jì)
# 按文章進(jìn)行詞頻統(tǒng)計(jì)
segStat = segmentDF.groupby(
by=['filePath', 'segment']
)['segment'].agg({
'計(jì)數(shù)': len
}).reset_index().sort_values( # reset_index()
'計(jì)數(shù)',
ascending=False # 倒序排列
)
# 把詞頻為小于1的詞刪掉
segStat = segStat[segStat['計(jì)數(shù)'] > 1]
運(yùn)行結(jié)果如下:

詞頻統(tǒng)計(jì).png
-
文檔向量化
# 文檔向量化
TF = segStat.pivot_table(
index='filePath', # 數(shù)據(jù)透視表的列
columns='segment', # 數(shù)據(jù)透視表的行
values='計(jì)數(shù)', # 數(shù)據(jù)透視表中的值
fill_value=0, # NA值統(tǒng)一替換為0
)
運(yùn)行結(jié)果如下:

文檔向量化.png
-
計(jì)算TF-IDF
def handler(x):
return numpy.log2(len(corpus) / (numpy.sum(x > 0) + 1))
IDF = TF.apply(handler)
TF_IDF = pandas.DataFrame(TF * IDF)
運(yùn)行結(jié)果如下:

TF-IDF.png
-
提取每篇文檔的前五個(gè)關(guān)鍵詞
tag1s = []
tag2s = []
tag3s = []
tag4s = []
tag5s = []
for filePath in TF_IDF.index:
tags = TF_IDF.loc[filePath].sort_values( # 用loc(索引名)遍歷行,每行為一個(gè)Series
ascending=False # 對(duì)每行進(jìn)行倒序排列
)[:5].index # 取每行前五個(gè)的索引值(即前五個(gè)分詞名稱)
tag1s.append(tags[0])
tag2s.append(tags[1])
tag3s.append(tags[2])
tag4s.append(tags[3])
tag5s.append(tags[4])
tagDF = pandas.DataFrame({
'filePath': corpus['filePath'],
'fileContent': corpus['fileContent'],
'tag1': tag1s,
'tag2': tag2s,
'tag3': tag3s,
'tag4': tag4s,
'tag5': tag5s
})
運(yùn)行結(jié)果如下:

結(jié)果.png
-
文檔向量化的理解
文檔向量化.PNG
-
參考