layout: post # 使用的布局(不需要改)title: Python文本挖掘 # 标题
subtitle: 从非结构化文本中提取特定信息
date: 2019-09-10 # 时间
author: NSX # 作者
header-img: img/post-bg-2015.jpg #这篇文章标题背景图片
catalog: true # 是否归档
tags: #标签
- 技术
- 教程
- Python

用 Python 做文本分析

文本分析的本质是从给定文本中获取高质量、有用信息的自动化过程,其一般步骤为:数据采集、数据清洗、文本挖掘分析、可视化分析
从非结构化文本中提取特定信息,如:实体、情感分析、关键词、主题、知识管理和发现(按主题对文档进行整理和分类以便于发现,然后向读者推荐与同一主题相关的其他文章,以提供个性化的内容推荐)、聚类…
在公司实际工作中,最好的大数据挖掘工程师一定是最熟悉和理解业务的人。对于大数据挖掘的学习心得,作者认为学习数据挖掘一定要结合实际业务背景、案例背景来学习,这样才是以解决问题为导向的学习方法。

文本分析的步骤

2019-09-10-Python文本挖掘 - 图1

常见数据挖掘方法

2019-09-10-Python文本挖掘 - 图2

导包

  1. import re
  2. import os
  3. import numpy as np
  4. import codecs
  5. from time import time
  6. from tqdm import tqdm
  7. import re
  8. from wordCloud import doWordCloud
  9. import jieba
  10. import jieba.posseg # 需要另外加载一个词性标注模块
  11. from jieba.analyse import *
  12. from sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizer
  13. from sklearn.decomposition import LatentDirichletAllocation
  14. from sklearn.cluster import MiniBatchKMeans, SpectralCoclustering
  15. from sklearn.metrics.cluster import v_measure_score
  16. import string
  17. from zhon.hanzi import punctuation
  18. import gensim
  19. import gensim.corpora as corpora
  20. from snownlp import SnowNLP
  21. import matplotlib.pyplot as plt
  22. from colorama import init, Fore, Back, Style

数据采集

读入我们的数据文件,显式指定编码类型,以免出现乱码错误

  1. # 读取无结构文档
  2. for txt_file in tqdm(os.listdir(path)):
  3. if not os.path.isdir(txt_file):
  4. with codecs.open(path + "/" + txt_file, "r", "utf-8") as f:

数据预处理

数据分析/挖掘领域有一条金科玉律:“Garbage in, Garbage out”,做好数据预处理,对于取得理想的分析结果来说是至关重要的。本文的数据规整主要是对文本数据进行清洗,处理的条目如下:

  1. 去除文本噪声信息
    处理编码问题 (codecs)
    去掉停用词(没有实际意义的符号)
    标点符号:, 。! /、*+-
    特殊符号:❤❥웃유♋☮✌☏☢☠✔☑♚▲♪等
    中文停用词
  2. 将文档分割成句子
  3. 中文分词 tokenize (jieba, 了避免歧义和切出符合预期效果的词汇,笔者采取的是精确(分词)模式)
  4. 词性标注。snownlp 是不二选择,还可以使用 pattern
  5. 计算Bigrams:如果两个词经常一起毗邻出现,那么这两个词可以结合成一个新词 (未使用)
  1. stop_words = []
  2. with codecs.open(stopWord_path, "r", "utf-8") as f:
  3. for line in f:
  4. stop_words.append(line.strip("\n").strip())
  5. ...
  6. with codecs.open(path + "/" + txt_file, "r", "utf-8") as f:
  7. file_content = f.read() # 以列表形式读取日志数据
  8. file_content = re.sub(r"^\s+$", "", file_content) # 去除换行符和空白字符
  9. # 去除字符串中的所有数字和字母
  10. file_content = re.sub("[0-9a-zA-Z_-]+", "", file_content)
  11. # 去除特殊中文标点
  12. file_content = re.sub(r"[%s]+" % punctuation, "", file_content)
  13. # 去除特殊英文标点
  14. file_content = re.sub(r"[%s]+" % string.punctuation, "", file_content)
  15. # if word >= u'\u4e00' and word <= u'\u9fa5':#判断是否是汉字
  16. # # 仅保留名词或特定POS (HMM)
  17. # s = SnowNLP(file_content)
  18. # pos_list = s.tags # [(u'这个', u'r'), (u'东西', u'n')]
  19. # word_list = [
  20. # w for w, pos in pos_list if pos in ["n", "a", "v", "d"]
  21. # ] # 'NN', 'ADJ', 'VB', 'ADV'
  22. # # 关键词 (TextRank算法)
  23. # kw_list = s.keywords(3)
  24. # # 文档摘要 (TextRank算法)
  25. # summmary_list = s.summary(3)
  26. # jieba分词
  27. word_list = jieba.cut(file_content)
  28. # 去除停用词
  29. word_list = [w for w in word_list if w not in stop_words]
  30. segments.append(" ".join(word_list))

单词之间都已经被空格区分开了。下面我们需要做一项重要工作,叫做文本的向量化

不要被这个名称吓跑。它的意思其实很简单。因为计算机不但不认识中文,甚至连英文也不认识,它只认得数字。我们需要做的,是把文章中的关键词转换为一个个特征(列),然后对每一篇文章数关键词出现个数。

特征抽取

我们希望获取到的词汇,既能保留文本的信息,同时又能反映它们的相对重要性。

特征选取的方式:

  1. 构建词向量空间:统计文本词频,生成文本的词向量空间
  2. 权重策略——TF-IDF:使用TF-IDF发现特征词,并抽取为反映文档主题的特征

经过上面的步骤之后,我们就可以把文本集转化成一个矩阵。

TF-IDF向量化

  1. vectorizer = TfidfVectorizer(stop_words=stop_words, sublinear_tf=True, max_df=0.5)
  2. print("\nvectorizing...")
  3. vector = vectorizer.fit_transform(segments)
  4. # 查看结果
  5. # print(vectorizer.vocabulary_) # 词表索引
  6. # print(vectorizer.idf_) # 词表中每个词的IDF权重
  7. # print(vector.toarray()) # 向量化结果 [[0. 0. 0.07097325 ... 0. 0. 0. ]...]

文本挖掘

关于文本挖掘方面的相关知识,请参看《数据运营|数据分析中,文本分析远比数值型分析重要!(上)》《在运营中,为什么文本分析远比数值型分析重要?一个实际案例,五点分析(下)》

本文的文本挖掘部分主要涉及:

  1. 高频词统计/关键词提取/关键词云
  2. TF-IDF的关键词提取方法
  3. 文章标题聚类
  4. 文章内容聚类
  5. 文章内容LDA主题模型分析
  6. 词向量/关联词分析
  7. ATM模型
  8. 词汇分散图
  9. 词聚类分析

用Python提取中文关键词

对于关键词提取,笔者没有采取词频统计的方法,因为词频统计的逻辑是:一个词在文章中出现的次数越多,则它就越重要。因而,笔者采用的是TF-IDF(termfrequency–inverse document frequency)的关键词提取方法:

  • 关键字抽取步骤:

2019-09-10-Python文本挖掘 - 图3

  1. # TextRank关键词提取
  2. print(Fore.RED + "关键词提取结果如下:")
  3. topk_list = textrank("\n".join(segments), withWeight=True, topK=100)
  4. topk_keyword = []
  5. with codecs.open('result/TextRank算法提取的 TOP100 关键词.txt', 'w', 'utf-8') as f:
  6. for keyword, weight in topk_list:
  7. f.write("{}\t{}\n".format(keyword, weight))
  8. topk_keyword.append(keyword)
  9. print(Fore.GREEN + "%s\t%s" % (keyword, weight))
  10. # 选取TOP100关键词来绘制关键词云
  11. doWordCloud(' '.join(topk_keyword), stop_words)

下面是笔者利用jieba在经预处理后的、近70MB的语料中抽取出的TOP100关键词(略)

统计性分析

只从文本中提取1000个最重要的特征关键词,然后停止

  1. # CountVectorizer统计词频
  2. n_features = 1000 # 提取1000个最重要的特征关键词
  3. vectorizer = CountVectorizer(
  4. max_df=0.95,
  5. min_df=2,
  6. max_features=n_features,
  7. strip_accents="unicode",
  8. stop_words=stop_words,
  9. )
  10. vector = vectorizer.fit_transform(segments)
  11. vocab = vectorizer.vocabulary_
  12. cipin = vector.toarray() # 词频统计 [[1 1 1 1 1 1 1 2]]

利用LDA进行主题抽取

刚才针对关键词的分类较为粗略,且人为划分,难免有失偏颇,达不到全面的效果。因此,笔者采用LDA主题模型来发现该语料中的潜在主题。关于LDA主题模型的相关原理,请参看《【干货】用大数据文本挖掘,来洞察“共享单车”的行业现状及走势》的第4部分。

LDA是一种典型的无监督(也就是说,我们事先不知道每段文本里面说的是啥,每个文本没有啥标签)、基于统计学习的词袋模型,即它认为一篇文档是由一组词构成的一个集合,词与词之间没有顺序以及先后的关系。一篇文档可以包含多个主题,文档中每一个词都由其中的一个主题生成。主题模型通过分析文本中的词来发现文档中的主题、主题之间的联系方式和主题的发展,通过主题模型可以使我们组织和总结无法人工标注的海量电子文档。

  1. def trainLDA(vector):
  2. # 主题分析
  3. n_topics = [5]
  4. perplexityLst = [1.0]*len(n_topics)
  5. #训练LDA并打印训练时间
  6. lda_models = []
  7. for idx, n_topic in enumerate(n_topics):
  8. lda = LatentDirichletAllocation(n_components=n_topic,
  9. max_iter=100,
  10. learning_method='batch', # 'batch' 'online'
  11. evaluate_every=200,
  12. learning_offset=50.0,
  13. random_state=0)
  14. t0 = time()
  15. lda.fit(vector)
  16. # 收敛效果(perplexity)
  17. perplexityLst[idx] = lda.perplexity(vector)
  18. lda_models.append(lda)
  19. print("# of Topic: %d, " % n_topics[idx])
  20. print("done in %0.3fs, N_iter %d, " % ((time() - t0), lda.n_iter_))
  21. print("收敛效果 Perplexity Score (越小越好) %0.3f" % perplexityLst[idx])
  22. #打印最佳模型
  23. best_index = perplexityLst.index(min(perplexityLst))
  24. best_n_topic = n_topics[best_index]
  25. best_model = lda_models[best_index]
  26. print(Fore.RED + "Best # of Topic: ", best_n_topic)
  27. print('\n')
  28. # 定义以下的函数,把每个主题里面的前若干个关键词显示出来
  29. def print_top_words(model, feature_names, n_top_words):
  30. with codecs.open("result/主题簇.txt", "w", "utf-8") as f:
  31. for topic_idx, topic in enumerate(model.components_):
  32. f.write("Topic #%d:\n" % topic_idx)
  33. f.write(
  34. "\n".join(
  35. [feature_names[i] for i in topic.argsort()[: -n_top_words - 1 : -1]]
  36. )
  37. )
  38. f.write("\n")
  39. print(Fore.RED + "Topic #%d:" % topic_idx)
  40. print(
  41. Fore.GREEN
  42. + " ".join(
  43. [feature_names[i] for i in topic.argsort()[: -n_top_words - 1 : -1]]
  44. )
  45. )
  46. print("\n")
  47. # 打印主题建模的结果
  48. n_top_words = 10 # 主题数量
  49. tf_feature_names = vectorizer.get_feature_names()
  50. print_top_words(lda, tf_feature_names, n_top_words)
  51. # 将doc转换为话题分布的函数
  52. doc_topic_dist = lda.transform(vector)
  53. trainLDA(vector)

一般情况下,笔者将主题的数量设定为10个,经过数小时的运行,得到如下结果:

  1. Topic #0:
  2. 学习 模型 使用 算法 方法 机器 可视化 神经网络 特征 处理 计算 系统 不同 数据库 训练 分类 基于 工具 一种 深度
  3. Topic #1:
  4. 这个 就是 可能 如果 他们 没有 自己 很多 什么 不是 但是 这样 因为 一些 时候 现在 用户 所以 非常 已经
  5. Topic #2:
  6. 企业 平台 服务 管理 互联网 公司 行业 数据分析 业务 用户 产品 金融 创新 客户 实现 系统 能力 产业 工作 价值
  7. Topic #3:
  8. 中国 2016 电子 增长 10 市场 城市 2015 关注 人口 检索 30 或者 其中 阅读 应当 美国 全国 同比 20
  9. Topic #4:
  10. 人工智能 学习 领域 智能 机器人 机器 人类 公司 深度 研究 未来 识别 已经 医疗 系统 计算机 目前 语音 百度 方面
  • 利用LDA做了主题抽取,不论是5个还是10个主题,可能都不是最优的数量选择。你可以根据程序反馈的结果不断尝试。

ATM模型

ATM模型(author-topic model)也是“概率主题模型”家族的一员,是LDA主题模型(Latent Dirichlet Allocation )的拓展,它能对某个语料库中作者的写作主题进行分析,找出某个作家的写作主题倾向,以及找到具有同样写作倾向的作家,它是一种新颖的主题探索方式。

词向量/关联词分析

词聚类

文档聚类

每一条评论都围绕一个或几个核心主旨进行阐述,这个核心主旨就是评论的中心思想。基于主旨话题分析技术获取文本主旨,每一条评论可能包含多个主旨话题,每一个话题包含多个主题词,通过评论的隶属度确定评论所属的主旨话题,通过主题词的分布确定主旨的含义。评论话题的具体分析流程如下,此过程要依次进行词法分析、文本过滤、主旨话题分析:

2019-09-10-Python文本挖掘 - 图4

接下来采用的是基于谱联合聚类算法(Spectral Co-clustering algorithm)的文档聚类,这部分的原理涉及到艰深的数学和算法知识,可能会引起小伙伴们的阅读不适感,如果是这样,请快速跳过,直接看后面的操作和结果。

先将待分析的文本经TF-IDF向量化构成了词频矩阵,然后使用Dhillon的谱联合聚类算法(Spectral Co-clustering algorithm)进行双重聚类(Biclusters)。所得到的“文档-词汇”双聚类(Biclusters)会把某些文档子集中的常用词汇聚集在一起,由若干个关键词构成某个主题。

  1. # 将聚类结果写入文件
  2. def writeClusterResult(y):
  3. result_dict = {}
  4. for i in range(len(y)):
  5. if y[i] not in result_dict:
  6. result_dict[y[i]] = []
  7. result_dict[y[i]].append(os.listdir(path)[:15][i].strip())
  8. # 按value长度逆序排序,写入
  9. result_list = sorted(result_dict.items(), key=lambda d: len(d[1]), reverse=True)
  10. with codecs.open("result/prediction_cluster.txt", "w", encoding='utf-8') as target:
  11. for item in result_list:
  12. target.write("{}\n{}\n\n".format(item[0], "\n".join(item[1])))
  13. print('聚类结果写入文件完成!!!')
  14. # 文本聚类
  15. n_clusters = 5
  16. kmeans = MiniBatchKMeans(n_clusters=n_clusters, batch_size=20000, random_state=0)
  17. print("MiniBatchKMeans...")
  18. y_kmeans = kmeans.fit_predict(vector).tolist()
  19. y_kmeans = np.array(y_kmeans)
  20. writeClusterResult(y_kmeans)

参考

如何用Python从海量文本抽取主题?

如何用Python做词云

如何从 “用户评论”中挖掘业务价值

【干货】用大数据文本挖掘,来洞察“共享单车”的行业现状及走势

以虎嗅网4W+文章的文本挖掘为例,展现数据分析的一整套流程