1、直接计算法

import jieba 
 import os 
 import pandas as pd 
 import numpy as np 
 stopw = [line.strip().decode(‘utf-8’) for line in open(u’F:/自然语言处理/stops.txt’).readlines()]#读取停用词 
 def del_stop_words(words,stop_words):#定义一个将分词结果过滤掉停用词的函数 
 result=jieba.cut(words)#分词 
 new_words = [] 
 for i in result:#对分词结果进行遍历 
 if i not in stop_words:#如果词语不在停用词表,是我们需要的词 
 new_words.append(i)#将保留下来的词追加到一个新的list中 
 return new_words 
 names = [ os.path.join(u’F:/自然语言处理/document’,h) for h in os.listdir(u’F:/自然语言处理/document’) ]#主要是读取document文件夹下所有的文本文件的路径,其中os.listdir是读取文本文件的名称如‘1.txt’、‘2.txt’ 
 posts = [ open(name).read() for name in names ]#循环读取每个文本文件的内容 
 docs = [] 
 word_set = set() 
 以下这个循环的主要作用是获取每个文档的词表,然后把 每个文档的词表取并集,形成word_set。词表就是指该篇文档由哪些词组成。用set函数可以得到每篇文档包含的词语。 
 for post in posts: 
 doc = del_stop_words(post,stopw) 
 docs.append(doc) 
 word_set |= set(doc) 
 word_set = list(word_set)#将word_set转为一个list数据类型 
 docs_vsm = []


以下这两个循环的主要作用是计算每一篇文档的词频,并把所有文档的词频全部放在doc_vsm中,doc_vsm转换成矩阵docs_matrix,这就是一个词频矩阵,每行是一篇文档,列是相应文档里的词,矩阵的某个数值表示该词在某篇文档的频数。
for i in range(13):#我的实验数据一共13篇文档,所以遍历13次,或许有同学会问为什么不是for doc in docs,我参考的博客资料确实这么写,但是我运行测试以后发现有问题,就是最后有一个函数是计算矩阵docs_matrix每一行的和,出现为0的情况,这显然不对,不可能一篇文档的词总数为0,当我写成for i in range(13):的时候,就没有这种问题,13这个数字可以根据您测试的文档数进行更换。
temp_vector = []
for word in word_set:#遍历词表,该词表包含组成文档的所有词
temp_vector.append(docs[i].count(word) * 1.0)#计算每篇文档每个词的词频, temp_vector临时保存一篇文档的词频
docs_vsm.append(temp_vector)#将每篇文档的词频依次追加到docs_vsm数据表中
docs_matrix = np.mat(docs_vsm)
column_sum = [ float(len(np.nonzero(docs_matrix[:,i])[0])) for i in range(docs_matrix.shape[1]) ]#计算包含该词的文档数
column_sum = np.array(column_sum)#转换为数组,因为数组可以方便后面的批量除法计算
column_sum = docs_matrix.shape[0] / column_sum#用文档总数除以包含某个词的文档总数(根据idf的概念)
idf = np.log(column_sum)#取对数
idf = np.diag(idf)#将数组转换为n*n的对角矩阵
以下这个循环主要是根据前面的词频矩阵docs_matrix计算tf值,tf值是词频除以该篇文档的总词数。
for doc_v in docs_matrix:
if doc_v.sum() == 0:
doc_v = doc_v / 1
else:
doc_v = doc_v / (doc_v.sum())
tfidf = np.dot(docs_matrix,idf)#tf*idf
tfidf就是一个词袋了
以上是直接计算的方法,为了说明详细,所以写的注释有点多,为了大家看得更清晰一些,我把代码重新复制一次。

import jieba 
 import os 
 import pandas as pd 
 import numpy as np 
 stopw = [line.strip().decode(‘utf-8’) for line in open(u’F:/自然语言处理/stops.txt’).readlines()] 
 def del_stop_words(words,stop_words): 
 result=jieba.cut(words) 
 new_words = [] 
 for i in result: 
 if i not in stop_words: 
 new_words.append(i) 
 return new_words 
 names = [ os.path.join(u’F:/自然语言处理/document’,h) for h in os.listdir(u’F:/自然语言处理/document’) ] 
 posts = [ open(name).read() for name in names ] 
 docs = [] 
 word_set = set() 
 for post in posts: 
 doc = del_stop_words(post,stopw) 
 docs.append(doc) 
 word_set |= set(doc) 
 word_set = list(word_set) 
 docs_vsm = [] 
 #print word.encode(“utf-8”) 
 for i in range(13): 
 temp_vector = [] 
 for word in word_set: 
 temp_vector.append(docs[i].count(word) * 1.0) 
 docs_vsm.append(temp_vector) 
 docs_matrix = np.mat(docs_vsm) 
 column_sum = [ float(len(np.nonzero(docs_matrix[:,i])[0])) for i in range(docs_matrix.shape[1]) ] 
 column_sum = np.array(column_sum) 
 column_sum = docs_matrix.shape[0] / column_sum 
 idf = np.log(column_sum) 
 idf = np.diag(idf) 
 for doc_v in docs_matrix: 
 if doc_v.sum() == 0: 
 doc_v = doc_v / 1 
 else: 
 doc_v = doc_v / (doc_v.sum()) 
 tfidf = np.dot(docs_matrix,idf)


2、使用scikit-learn库的feature_extraction.textTrans文本特征抽取模块中的former 和CountVectorizer 相关函数进行计算,比上面的方法简单十倍都不止,但是数据类型要必须满足它的要求,所以在使用这个方法之前要做一些特别的处理,最后形成一个词列表list,list的每个元素是一篇文档的所有词,即每篇文档的所有词构成一个向量,所有的向量构成list。以下是相关代码:
2.1计算TFIDF值之前的前期处理

import re 
 import os 
 import sys 
 import codecs 
 import jieba 
 stopw = [line.strip().decode(‘utf-8’) for line in open(u’F:/自然语言处理/stops.txt’).readlines()] 
 path=u’F:/file/’ 
 ph=u’F:/自然语言处理/document/’ 
 n= 1 
 while n<=13: #遍历每篇文档 
 name = “%d” % n 
 filename = ph + str(name) + “.txt” 
 resname=path+str(name)+”.txt” 
 source = open(filename, ‘r’) 
 result = codecs.open(resname, ‘w’, ‘utf-8’) 
 line = source.readline() #按行读取 
 line = line.rstrip(‘\n’) 
 while line!=”“: #对读取的每行数据进行分词 
 seglist = jieba.cut(line,cut_all=False) 
 newword=[] 
 for i in seglist: 
 if i not in stopw: 
 newword.append(i)#去停后的分词结果是以逗号分隔 
 output = ’ ‘.join(list(newword)) #所以要用join替换成空格拼接 
 result.write(output + ‘\r\n’) #将该篇文档的某一行文本分词后的结果写入新文档 
 line = source.readline() #循环读取该篇文档的每行文本 
 source.close() 
 result.close() 
 n = n + 1 result = codecs.open(u’F:/自然语言处理/document/all.txt’, ‘w’, ‘utf-8’) 
 num = 1 
 while num <= 13: 
 name = “%d” % num 
 fileName = path + str(name) + “.txt” 
 source = open(fileName, ‘r’) 
 line = source.readline() 
 line = line.strip(‘\n’) #去除回车换行符,让所有的词形成一行 
 line = line.strip(‘\r’) 
 while line!=”“: 
 line = line.replace(‘\n’,’ ‘) 
 line = line.replace(‘\r’,’ ‘) #去除回车换行符,让所有的词形成一行 
 result.write(line+ ’ ‘) #将每一行写入一个文档并用空格分隔 
 line = source.readline() 
 source.close() 
 num = num + 1 
 result.close()

主要思路是读取每篇文档进行分词,分词以后将分词结果分别写到新的文本文件中去,我原始文档有13个,最后也会有相应的13个分词结果文件,接着再读取每个分词结果文件到python中,再依次写到一个文件中,这个文件就包含了所有文档的分词结果,这个文件每一行就是一篇文档的分词结果,我这里测试文档是13篇,那么这个文件就只有13行数据,必须这样,否则后面使用函数计算TFIDF会出错。
2.2正式进行计算TF-IDF值

from sklearn.feature_extraction.text import TfidfTransformer 
 from sklearn.feature_extraction.text import CountVectorizer 
 corpus = [] 
 for line in open(u’F:/自然语言处理/document/all.txt’, ‘r’).readlines(): 
 #print line 
 corpus.append(line.strip())#all文件就是存放所有文档分词结果的文件,一共13行,每行代表一篇文本,把它读取到corpus中 
 vectorizer = CountVectorizer()#将文本中的词语转换为词频矩阵 矩阵元素a[i][j] 表示j词在i类文本下的词频 
 transformer = TfidfTransformer() #该类会统计每个词语的tf-idf权值 
 tfidf = transformer.fit_transform(vectorizer.fit_transform(corpus))#第一个fit_transform是计算tf-idf 第二个fit_transform是将文本转为词频矩阵 
 word = vectorizer.get_feature_names() #获取词袋模型中的所有词语 
 weight = tfidf.toarray() #将tf-idf矩阵抽取出来,元素w[i][j]表示j词在i类文本中的tf-idf权重 
 weight就是tfidf矩阵


3、聚类
根据前面计算的tfidf值,我们已经把文本转换成了数值,可以直接进行聚类了。实际上为了测试效果,我使用的文本是已经分好类的,是搜狗实验室的文本数据,我分别从财经、IT、汽车三个类抽取出13篇文章,所以我可以很容易了解到聚类效果。我尝试了所有的聚类方法,聚类效果都不是很好,在其他一些作者写的关于文本聚类的文章里面,也有提到效果不是很好。根据我以前看过的文献资料,应该是最后的TFIDF矩阵是一个高维稀疏矩阵,导致聚类效果很差,解决方法就是降维,目前我还没有继续往下优化,希望对这方面有研究的朋友跟我交流,多多指导。
以下是聚类的有关代码:

from sklearn.cluster import KMeans 
 kmeans=KMeans(n_clusters=3) 
 kmeans.fit(weight) 
 kmeans.labels_#输出k-means聚类的结果 
 from sklearn import cluster 
 ms = cluster.MeanShift() 
 ms.fit_predict(weight) 
 two_means = cluster.MiniBatchKMeans(n_clusters=3) 
 two_means.fit_predict(weight) 
 ward = cluster.AgglomerativeClustering(n_clusters=3, linkage=’ward’) 
 ward.fit_predict(weight) 
 spectral = cluster.SpectralClustering(n_clusters=3,eigen_solver=’arpack’,affinity=”nearest_neighbors”) 
 spectral.fit_predict(weight) 
 dbscan = cluster.DBSCAN(eps=.2) 
 dbscan.fit_predict(weight) 
 affinity_propagation = cluster.AffinityPropagation() 
 affinity_propagation.fit_predict(weight)