python 文本聚类分析案例——从若干文本中聚类出一些主题词团
阅读原文时间:2021年04月22日阅读:1

python 文本聚类分析案例

说明

实验要求:对若干条文本进行聚类分析,最终得到几个主题词团。
实验思路:将数据进行预处理之后,先进行结巴分词、去除停用词,然后把文档生成tfidf矩阵,再通过K-means聚类,最后得到几个类的主题词。
实验说明:如何用爬虫获取数据可以参考其他博客,这里我们直接用一个周杰伦的歌词文本为例进行分析,把28条歌词聚成3个类。

摘要

实验源数据放在文件sourceData中,中间文件放在resultData中。

源文件:

结果:

1、结巴分词

直接上代码,经测试后,我采用的是精确模式,效果最好。

# jieba分词 精确模式
def get_jiebaword():
    # enconding视文本保存的编码而定,utf-8或gbk
    try:
        with open('sourceData/JayZhou.txt', "r", encoding='gbk') as fr:
            lines = fr.readlines()
    except FileNotFoundError:
        print("no file like this")
    jiebaword = []
    for line in lines:
        line = line.strip('\n')
        # 清除多余的空格
        line = "".join(line.split())
        # 默认精确模式
        seg_list = jieba.cut(line, cut_all=False)
        word = "/".join(seg_list)
        jiebaword.append(word)
    return jiebaword

结果:

2、去除停用词

首先加载停用词表,可以到其他博客出搜寻一个别人整理好的停用词表,保存为StopWord.txt。

# 获取停用词表
def get_stopword():
    stopword = []
    try:
        with open('sourceData/StopWord.txt', "r", encoding='utf-8') as fr:
            lines = fr.readlines()
    except FileNotFoundError:
        print("no file like this")
    for line in lines:
        line = line.strip('\n')
        stopword.append(line)
    return stopword

然后是去除停用词,我们把去掉停用词的结果放到CleanWord.txt中。

# 去除停用词
def clean_stopword(jiebaword,stopword):
    fw = open('resultData/CleanWords.txt', 'a+',encoding='utf-8')
    for words in jiebaword:
        words = words.split('/')
        for word in words:
            if word not in stopword:
                fw.write(word + '\t')
        fw.write('\n')
    fw.close()


这里我们发现文档其实并不Clean,因为存在"- - -"、“10”…这些其他词语。所以我们要在main函数中再对停用词表进行补充:

# ---停用词补充,视具体情况而定---
stopword.append('---')
stopword.append('----')
i = 0
for i in range(19):
    stopword.append(str(10+i))
# ----------------------

这样得到的文档就比较干净啦。

3、生成tfidf矩阵

我们只有把文章词语转化成数字,才能进行计算。转化成tfidf矩阵是一个不错的选择,具体而言tfidf是体现文档相似度的,关于它的解释很多。

以下我的理解:同一个词语,如果在文章A和文章B中都出现了很多次,那说明这两篇文章很相似,这就是tf;相反地,同一个词语,在所有文章中极少出现,而恰恰在文章A和文章B中出现过,也可以说明这两篇文章相似,这就是idf。同时考虑这两个维度,就是我们常说的tfidf。

# 生成tf-idf矩阵文档
def get_tfidf():
    try:
        with open('resultData/CleanWords.txt', "r", encoding='utf-8') as fr:
            lines = fr.readlines()
    except FileNotFoundError:
        print("no file like this")
    transformer=TfidfVectorizer()
    tfidf = transformer.fit_transform(lines)
    # 转为数组形式
    tfidf_arr = tfidf.toarray()
    return tfidf_arr

这里我们可以把结果输出一下:

print(tfidf_arr)
print(tfidf_arr.shape)


这是一个28行x268列的矩阵,代表分出来的268个词在28个短文本中的tfidf值。值为0,说明该词未在此文章中出现过。

4、K-means聚类

接下来我们就可以进行聚类了,这里我们直接调用nltk包。

# K-means聚类
def get_cluster(tfidf_arr,k):
    kmeans = KMeansClusterer(num_means=k, distance=cosine_distance)  # 分成k类,使用余弦相似分析
    kmeans.cluster(tfidf_arr)
    # 获取分类
    kinds = pd.Series([kmeans.classify(i) for i in tfidf_arr])
    fw = open('resultData/ClusterText.txt', 'a+', encoding='utf-8')
    for i, v in kinds.items():
        fw.write(str(i) + '\t' + str(v) + '\n')
    fw.close()

将分类的结果写入ClusterText.txt文件,前一个数字代表文本的下标,后一个数字代表分类的编号。

5、获取主题词 / 主题词团

分完类之后,我们还要找出这个类的主题词,所以我们先按照分类结果,把28个文本放到对应的3个类之中。

# 获取分类文档
def cluster_text():
    index_cluser = []
    try:
        with open('resultData/ClusterText.txt', "r", encoding='utf-8') as fr:
            lines = fr.readlines()
    except FileNotFoundError:
        print("no file like this")
    for line in lines:
        line = line.strip('\n')
        line = line.split('\t')
        index_cluser.append(line)
    # index_cluser[i][j]表示第i行第j列
    try:
        with open('resultData/CleanWords.txt', "r", encoding='utf-8') as fr:
            lines = fr.readlines()
    except FileNotFoundError:
        print("no file like this")
    for index,line in enumerate(lines):
        for i in range(28):
            if str(index) == index_cluser[i][0]:
                fw = open('resultData/cluster' + index_cluser[i][1] + '.txt', 'a+', encoding='utf-8')
                fw.write(line)
    fw.close()

结果:

得到分类文档之后,再分别统计不同类之中出现频率最高的那个词,默认为我们的主题词。

# 获取主题词
def get_title(cluster):
    for i in range(cluster):
        try:
            with open('resultData/cluster' + str(i) + '.txt', "r", encoding='utf-8') as fr:
                lines = fr.readlines()
        except FileNotFoundError:
            print("no file like this")
        all_words = []
        for line in lines:
            line = line.strip('\n')
            line = line.split('\t')
            for word in line:
                all_words.append(word)
        c = Counter()
        for x in all_words:
            if len(x) > 1 and x != '\r\n':
                c[x] += 1

        print('主题' + str(i+1) + '\n词频统计结果:')
        # 输出词频最高的那个词,也可以输出多个高频词
        for (k, v) in c.most_common(1):  
            print(k,':',v,'\n')

结果如图所示:

学过K-means算法的都知道,每个类的中心一开始随机选取,故每次聚类的结果不同,可能要多试几次,才有满意结果。

以下是头文件和main函数中的调用:

import jieba
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from nltk.cluster.kmeans import KMeansClusterer
from nltk.cluster.util import cosine_distance
from collections import Counter


if __name__ == '__main__':
    # 定义聚类的个数
    cluster = 3
    # 结巴分词
    jiebaword = get_jiebaword()
    # 获取停用词
    stopword = get_stopword()
    # ---停用词补充,视具体情况而定---
    stopword.append('---')
    stopword.append('----')
    i = 0
    for i in range(19):
        stopword.append(str(10+i))
    # ----------------------
    # 去除停用词
    clean_stopword(jiebaword,stopword)
    # 获取tfidf矩阵
    tfidf_arr = get_tfidf()
    # ---输出测试---
    # print(tfidf_arr)
    # print(tfidf_arr.shape)
    # -------------
    # K-means聚类
    get_cluster(tfidf_arr,cluster)
    # 获取分类文件
    cluster_text()
    # 统计出主题词
    get_title(cluster)

以上就是使用python进行文本聚类分析的一个案例,代码可以组合后直接使用,但要注意文本条数的更改和停用词清理的具体情况。经测试可以对1000条左右的文本进行聚类,结果仍然可观。