概念:
- 词频(Term Frequency):指的是某一指定的词在该文档中出现的次数。 逆文档频率(Inverse Document
- Frequency):IDF就是每个词的权重,它的大小与一个词的常见程度成反比。
- TF-IDF:衡量某个词是否关键词的指标,该值越大,是关键词的可能性就越大。
- 计算公式:
- TF=该词在文档中出现的频率。
- IDF=log(文档总数/包含该词的文档数+1)
- TF-IDF=TF*IDF
上代码:
首先构建语料库:
import numpy
#创建语料库
import os;
import os.path;
import codecs;
filePaths = [];
fileContents = [];
for root, dirs, files in os.walk(
r"C:\Users\www12\Desktop\data\2.7\\SogouC.mini\\Sample"
):
for name in files:
filePath = os.path.join(root, name);
filePaths.append(filePath);
f = codecs.open(filePath, 'r', 'utf-8')
fileContent = f.read()
f.close()
fileContents.append(fileContent)
import pandas;
corpos = pandas.DataFrame({
'filePath': filePaths,
'fileContent': fileContents
})
进行分词处理
import re
zhPattern = re.compile(u'[\u4e00-\u9fa5]+') #正则表达式识别中文词
import jieba
#分词
segments = []
filePaths = []
for index, row in corpos.iterrows():
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({
'filePath':filePaths,
'segment':segments
})
接着跟之前的代码一样,去掉停用词
#移除停用词
stopwords = pandas.read_csv(
"D:\\PDM\\2.7\\StopwordsCN.txt",
encoding='utf8',
index_col=False,
quoting=3,
sep="\t"
)
segmentDF = segmentDF[
~segmentDF.segment.isin(
stopwords.stopword
)
]
进行词频统计:
#按文章进行词频统计
segStat = segmentDF.groupby(
by=["filePath", "segment"]
)["segment"].agg({
"计数":numpy.size
}).reset_index().sort_values(
by=["计数"],
ascending=False
)
#把小部分的数据删除掉
segStat = segStat[segStat.计数>1]
结果如图:
接着对文档进行向量化处理:
#进行文本向量计算
tf = segStat.pivot_table(
index='filePath',
columns='segment',
values='计数',
fill_value=0
)
#文件路径作为行,分词作为列,计数列作为值,缺失值填充为0。
tf.index
tf.columns
tf是一个DateFrame,分词是行,文件路径是列,值是计数列,如图所示:
计算IDF=log(文档总数/包含该词的文档数+1)
#计算IDF
def hanlder(x):
return (numpy.log2(len(corpos)/(numpy.sum(x>0)+1)))
idf = tf.apply(hanlder)
#计算TF-IDF
tf_idf = pandas.DataFrame(tf*idf)
接着进行关键字提取:
#提取关键词
tag1s = []
tag2s = []
tag3s = []
tag4s = []
tag5s = []
for filePath in tf_idf.index:
tagis = tf_idf.loc[filePath]#提取每一个filepath对应的行,作为tagis的列,共100列,3207行
tagis=tagis.sort_values(#对每一列进行排序,取前五
ascending=False
)[:5]
tag1s.append(tagis[0])#分别取前五位关键词
tag2s.append(tagis[1])
tag3s.append(tagis[2])
tag4s.append(tagis[3])
tag5s.append(tagis[4])
tagDF = pandas.DataFrame({
'filePath':corpos.filePath,
'fileContent':corpos.fileContent,
'tag1':tag1s,
'tag2':tag2s,
'tag3':tag3s,
'tag4':tag4s,
'tag5':tag5s
})
结果如图:
到这里关键词提取就完成了。
在代码执行的过程中我发现了一个细节,让我百思不得其解,是这样的,segStat是词频统计之后,又去掉单字词得到的结果,它的长度为5729行
然后经过文件向量化处理后,也就是segStat.pivot_table()方法之后,它的size变成了(100,3207),这是为啥?第一:这个100是哪来的?第二:分词segStat[segment]总共有5729个,为什么len(tf[columns])=3207?
经过我苦苦思索,终于豁然开朗,原来segStat里是将源文件里的内容分词处理,一个文件被分成了很多词,每个词都有对应的文件路径作为index,所以len(segStat)就有很多行,等于5729,然后 当执行pivot_table()的时候,重复的文件路径就自动筛选掉了,就只有源文件的路径,也就是100个,第一个问题就搞明白了。
第二个问题,我特别困惑,观察了segStat的这个表,发现原因就是同一个词可以在多个文件里出现,比如这个“中国”,所以这个表文件路径唯一,但是词不唯一,
但是到了tf表里,文件路径唯一,词也唯一,如下图:
等于是变成了二维表
tf我们看的是一个词在多个文件的出现频次分布情况。
搞明白了这两个问题,我发现好像对代码的理解更深入了一些,函数的运作使表结构发生变化,从而影响了数据的长度。
通过解决这两个疑惑,我有了一些学习上的感受:如果只是运行一遍程序,得到想要的结果,但肯定不会注意到表格的size发生变化了,偏偏是这一个小小的细节和疑惑,能参透这么多知识,这难道不应该给我们学习思路上的以启示吗?我刚开始学的时候,我也发愁,代码一下子又看不懂,自己写一遍又记不住,慢慢写博客笔记对我来说算是一个更细致地方法,但是还是太粗糙了,深入到表格的结构、字段长度的变化,以及为什么要这样做,我们根本就没注意到,也没有思考过,代码运行肯定能得到结果,但是这其中有很多细节我这种初学者很容易会忽略,又要有扎实的pandas基础,还要心细多思考,数据挖掘的学习,果然需要花很多精力和功夫。