4.9 内容延伸:非结构化数据的分析与挖掘
在之前的内容中,我们主要介绍了常用的数据分析和挖掘方法,大多数是针对结构化数据(或者已经被转换为结构化的数据)开展的。本节将介绍一些针对非结构化数据的分析和挖掘方法,主要侧重于文本处理领域。
4.9.1 词频统计
词频统计是针对文档中的词出现频率的统计分析。通常词频统计采用的方法是TF-IDF方法,但本节使用的只是基于分词后的词语统计出现的次数。有关TF-IDF的应用,会在下面的章节中提到。
示例模拟的是从一篇文章中提取关键字并作词频统计分析,主要用到的技术是中文分词、词频统计和词云展示。数据源文件article1.txt位于“附件-chapter4”中,默认工作目录为“附件-chapter4”(如果不是,请cd切换到该目录下,否则会报“IOError:File article1.txt does not exist”)。完整代码如下:
# 导入库 import re # 正则表达式库 import collections # 词频统计库 import numpy as np # numpy库 import jieba # 结巴分词 import wordcloud # 词云展示库 from PIL import Image # 图像处理库 import matplotlib.pyplot as plt # 图像展示库 # 读取文本文件 fn = open('article1.txt') # 以只读方式打开文件 string_data = fn.read() # 使用read方法读取整段文本 fn.close() # 关闭文件对象 # 文本预处理 pattern = re.compile(u'\t|\n|\.|-|一|:|;|\)|\(|\?|"') # 建立正则表达式匹配模式 string_data = re.sub(pattern, '', string_data) # 将符合模式的字符串替换掉 # 文本分词 seg_list_exact = jieba.cut(string_data, cut_all=False) # 精确模式分词[默认模式] object_list = [] # 建立空列表用于存储分词结果 remove_words = [u'的', u',', u'和', u'是', u'随着', u'对于', ' ', u'对', u'等', u'能', u'都', u'。', u'、', u'中', u'与', u'在', u'其', u'了', u'可以', u'进行', u'有', u'更', u'需要', u'提供', u'多', u'能力', u'通过', u'会', u'不同', u'一个', u'这个', u'我们', u'将', u'并', u'同时', u'看', u'如果', u'但', u'到', u'非常', u'—', u'如何', u'包括', u'这'] # 自定义去除词库 # remove_words = [] #空去除词列表,用于跟关键字提取做效果对比 for word in seg_list_exact: # 迭代读出每个分词对象 if word not in remove_words: # 如果不在去除词库中 object_list.append(word) # 分词追加到列表 # 词频统计 word_counts = collections.Counter(object_list) # 对分词做词频统计 word_counts_top5 = word_counts.most_common(5) # 获取前10个频率最高的词 for w, c in word_counts_top5: # 分别读出每条词和出现从次数 print w, c # 打印输出 # 词频展示 mask = np.array(Image.open('wordcloud.jpg')) # 定义词频背景 wc = wordcloud.WordCloud( font_path='C:/Windows/Fonts/simhei.ttf', # 设置字体格式,不设置将无法显示中文 mask=mask, # 设置背景图 max_words=200, # 设置最大显示的词数 max_font_size=100 # 设置字体最大值 ) wc.generate_from_frequencies(word_counts) # 从字典生成词云 image_colors = wordcloud.ImageColorGenerator(mask) # 从背景图建立颜色方案 wc.recolor(color_func=image_colors) # 将词云颜色设置为背景图方案 plt.imshow(wc) # 显示词云 plt.axis('off') # 关闭坐标轴 plt.show() # 显示图像
上述代码以空行分为6个部分。
第一部分:导入库。示例中用到了正则表达式库re,用来做字符串匹配替换;词频统计库collections用来基于分词列表做词频统计;Numpy库配合PIL的Image做背景图像的读取和转换;jieba用来做中文分词;wordcloud用来做根据词频的词云图像展示;Matplotlib配合wordcloud做图像展示。
第二部分:读取文本文件。该部分使用Python标准文件读取方法,先通过默认的open方法以只读方式打开文本文件,然后使用read方法将所有文本信息读取为整个字符串,最后关闭文件对象。
第三部分:文本预处理。该部分主要功能是去除文本数据中无效的字符,先使用re.compile方法建立正则表达式匹配模式,然后使用re.sub方法将符合匹配模式的字符串替换掉(去掉)。
第四部分:文本分词。通过jieba.cut以默认的精确分词模式做中文分词,然后分别建立空列表用于接下来的循环分词的追加,建立remove_words用来过滤掉一些停用词、助词、语气词等无关词汇,接着使用for循环读取结巴分词的每个结果,先判断是否属于要过滤的词,如果不属于则追加到要使用的分词列表中。
上述几个部分在第三章都有相关介绍,这里不再做详细用法说明。
第五部分:词频统计。这里使用collections.Counter方法对分词列表做统计,然后提取词频统计结果中词频最高的5个词,打印输出结果如下:
数据113
分析48
功能47
Adobe 45
Analytics 37
上述结果的第一列是分词后的词语,第二列是对应出现的次数(注:该demo示例没有排除所有无关词,因此在接下来的词云展示中仍然会出现部分无关词汇)。在真实使用场景下,通常会建立去除词的列表并定期做更新维护,这样越到后期该列表会越有效。
第六部分:词频展示。这里使用了wordcloud库做基于自定义图案的词云展示。
先使用Image的open方法读取一个文件,返回结果是一个文件对象实例,通过numpy.array将其转换为数组才能在wordcloud做应用,该步骤建立了一个自定义展示图案,同时也是自定义图案配色的来源。
接着通过wordcloud.WordCloud方法建立一个词云对象,并详细定义展示细节。
font_path:展示词云需要的字体库,中文下需要自定义该参数,否则中文将无法正常显示;
mask:设置词云展示的图案;
max_words:设置最大展示词云的数量;
max_font_size:控制字体最大值。
对词云对象使用generate_from_frequencies方法,从词频统计结果中获取词频数据。
使用wordcloud.ImageColorGenerator方法自定义展示颜色方案,并应用到wc.recolor方法中,该步骤可以改变默认词云的颜色方案,用来自适应图像本身的区域和对应颜色方案。
最后通过plt的imshow方法展示词云,设置关闭坐标轴并展示图像。词云的展示以Python默认Logo样式和颜色方案为基准(原图形文件可参看附件4中的wordcloud.jpg文件),原图形文件和生成的词云结果如图4-18所示。
上述过程中,主要需要考虑的关键点是如何将词频形象的展示出来。除了传统的基于关键字数量的条形图、柱形图展示以外,可以应用词云展示方案,并可以基于不同的图案和颜色做展示配置。
图4-18 词云展示
如果使用自定义背景图案做词云展示时,背景图案应尽量简单、明了,同时要颜色对比明显且有大面积的色块,不要有复杂的图案和线条,否则词云展示效果不好。
代码实操小结:在本节的代码中,主要使用了以下几个知识点:
使用正则表达式库re.compile做多模式的匹配以及使用re.sub做模式替换;
使用jieba.cut做中文分词;
使用列表的append方法做列表追加;
使用for循环配合if条件语言做循环处理;
使用collections库collections.Counter方法做词频统计,并基于词频统计结果的most_common方法提取词频最高的n的词;
使用Image.open读取图像;
使用numpy.array做数据转换;
使用wordcloud做词云展示,包括自定义词云背景、字体、最大显示的词语数量、字体最大值、自定义配合方案等。
4.9.2 词性标注
所谓词性标注是给每个词语确定一个词性分类。例如,“我爱Python数据分析与数据化运营”分词后的结果可能是“我|爱|Python|数据分析|与|数据|化|运营”,其中每个词都有专属的分类:
我:代词
爱:动词
Python:英语
数据分析:习用语
与:介词
数据:名词
化:名词
运营:名动词,具有名词功能的动词
在运营中,有很多场景需要做词性标注,然后基于标注的词性可以做进一步应用。例如统计竞争对手新闻稿的主要词语分布、分词结果筛选和过滤、配合文章标签的提取等。
本示例中,模拟的是从一篇文章中提取关键字并作词性标注,主要用到的技术是中文分词和标注。数据源文件article1.txt位于“附件-chapter4”中,默认工作目录为“附件-chapter4”(如果不是,请cd切换到该目录下,否则会报“IOError:File article1.txt does not exist”)。完整代码如下:
# 导入库 import jieba.posseg as pseg import pandas as pd # 读取文本文件 fn = open('article1.txt') # 以只读方式打开文件 string_data = fn.read() # 使用read方法读取整段文本 fn.close() # 关闭文件对象 # 分词+词性标注 words = pseg.cut(string_data) # 分词 words_list = [] # 空列表用于存储分词和词性分类 for word in words: # 循环得到每个分词 words_list.append((word.word, word.flag)) # 将分词和词性分类追加到列表 words_pd = pd.DataFrame(words_list, columns=['word', 'type']) # 创建结果数据框 print (words_pd.head(4)) # 展示结果前4条 # 词性分类汇总-两列分类 words_gb = words_pd.groupby(['type', 'word'])['word'].count() print (words_gb.head(4)) # 词性分类汇总-单列分类 words_gb2 = words_pd.groupby('type').count() words_gb2 = words_gb2.sort_values(by='word', ascending=False) print (words_gb2.head(4)) # 选择特定类型词语做展示 words_pd_index = words_pd['type'].isin(['n', 'eng']) words_pd_select = words_pd[words_pd_index] print (words_pd_select.head(4))
上述代码以空行分为6个部分。
第一部分:导入库。本示例中用到了jieba.posseg直接做带有词性标注的分词,用pandas做数据结构化展示。
第二部分:分词+词性标注。使用pseg.cut(结巴分词的jieba.posseg)将读取的文本内容做分词;创建一个空列表用于存储分词和词性分类;然后通过一个循环读取每个分词,将分词和词性分类追加到列表并创建结果数据框,前4条数据如下:
word type 0 Adobe eng 1 x 2 Analytics eng 3 和 c
数据框中的word列是分词结果,type是分词标注的类型。结巴分词的词性标注方法采用和ictclas兼容的标记法。常用的分类如表4-8所示。
表4-8 常用结巴分词词性分类
第四部分:词性分类汇总——两列分类。使用Pandas的grouby方法对'type'和'word'这两列同时做分类汇总,汇总列为'word',汇总方式为计数。得到的前4条结果如下:
type word a 不同 14 不足 2 不通 1 严谨 2 Name: word, dtype: int64
第五部分:词性分类汇总——单列分类。仍然使用Pandas的grouby方法对'type'做分类汇总,汇总列为'type',汇总方式为计数;然后对汇总结果使用sort_values方法对word列做逆序排序,得到的前4条结果如下:
word type x 994 n 981 v 834 eng 295
第六部分:选择特定类型词语做展示。代码中通过使用isin方法从列表中选择了名词(n)和英文(eng),然后得到新的数据框,前4条数据如下:
word type 0 Adobe eng 2 Analytics eng 4 Webtrekk eng 9 领域 n
上述过程中,所有步骤相对简单,没有技术上和理解上的难点。
代码实操小结:在本节的代码中,主要使用了以下几个知识点:
使用jieba.posseg做带有词性标注的分词,并通过循环得到每个分词的词语和类别结果;
使用列表的append方法追加元素;
使用Pandas的Dataframe方法基于列表建立数据框;
使用数据框的groupby方法做分类汇总,并可指定分类汇总的列以及汇总方式;
使用数据框的head方法展示前n条数据;
使用数据框的isin从数据框中查找符合列表元素的数据索引;
使用数据框的sort_values方法对数据数据框排序,并指定排序规则。
4.9.3 关键字提取
关键字提取是从文本中提取跟内容最相关的词语,关键字抽取的结果经常使用的场景是文档检索、文章标签编辑等,也经常用在文本聚类、文本分类、关键字摘要等方面。
本示例中,模拟的是从一篇文章中提取关键字,主要用到的技术是中文关键字提取。数据源文件article1.txt位于“附件-chapter4”中,默认工作目录为“附件-chapter4”(如果不是,请cd切换到该目录下,否则会报“IOError:File article1.txt does not exist”)。完整代码如下:
# 导入库 import jieba.analyse # 导入关键字提取库 import pandas as pd # 导入pandas # 读取文本数据 fn = open('article1.txt') # 以只读方式打开文件 string_data = fn.read() # 使用read方法读取整段文本 fn.close() # 关闭文件对象 # 关键字提取 tags_pairs = jieba.analyse.extract_tags(string_data, topK=5, withWeight=True, allowPOS=['ns', 'n', 'vn', 'v', 'nr'], withFlag=True) # 提取关键字标签 tags_list = [] # 空列表用来存储拆分后的三个值 for i in tags_pairs: # 打印标签、分组和TF-IDF权重 tags_list.append((i[0].word, i[0].flag, i[1])) # 拆分三个字段值 tags_pd = pd.DataFrame(tags_list, columns=['word', 'flag', 'weight']) # 创建数据框 print (tags_pd) # 打印数据框
上述代码以空行分为3部分。
第一部分:导入库。本示例用到了关键字提取库jieba.analyse,使用Pandas做格式化输出。
第二部分:读取文本数据。使用read方法读取文件的内容为字符串。
第三部分:关键字提取。使用jieba.analyse.extract_tags方法,提取前5个关键字标签,以下是各参数信息:
string_data:要提取的文本对象;
topK:提取的关键字数量,不指定则提取全部;
withWeight:设置为True指定输出词对应的IF-IDF权重;
allowPOS:只允许提取符合特定词语类别的标签,具体类别见4.9.2节中的完整表格;
withFlag设置为True指定输出的关键字标签带有词语类别信息。
关键字提取的结果是一个字典,每一个元素都由一个关键字和词语类别组成的元组以及权重值组成。接下来将结果解析出来:先创建一个空列表用于存储数据,然后通过循环将每个列表元素的关键字标签、分组和TF-IDF权重以元组的形式追加到列表中;最后建立一个数据框用来存储和展示数据,结果如下:
word flag weight 0 数据 n 0.313395 1 报表 n 0.163367 2 功能 n 0.150263 3 分析 vn 0.134857 4 用户 n 0.126633
相关知识点:TF-IDF
TF-IDF(term frequency–inverse document frequency)是一种针对关键字的统计分析方法,用来评估关键字或词语对于文档、语料库和文件集合的重要程度。关键字的重要程度跟它在文档中出现的次数成正比,但同时跟它出现的频率呈反比。这种加权的形式能有效避免常用词对于关键字搜索结果的影响,提高搜索结果与关键字之间的相关性。
TF-IDF的主要思想是:如果某个关键字在一篇文档中出现的频率(TF,Term Frequ-ency)高,并且在其他文档中很少出现,那么认为该关键字具有良好的区分不同文档的能力,也就越重要。
在4.9.1节中,我们使用了基本的频率(TF,Term Frequency)方法,这会导致这样的问题:出现频率最高的往往是文档中的助词、介词、标签符号等,例如的、逗号、句号等。因此,在做关键字相关处理之前基本上都要有停用词处理的步骤。在4.9.1节对应的代码附件中,读者可取消第567行代码注释#remove_words=[]#空去除词列表,用于跟关键字提取做效果对比,再次运行其代码查看前5个关键字最高的结果如下:
的291 ,241 123 。117 数据113
因此,TF-IDF是做词频统计的基本思路和方法,也是做词语向量化、以及基于文本向量的聚类、分类等应用时的基本方法。
上述过程中,所有步骤都比较简单,没有技术上和理解上的难点。
代码实操小结:在本节的代码中,主要使用了以下几个知识点:
使用关键字提取库jieba.analyse.extract_tags提取关键字,并可设置关键字提取类别、权重、数量等;
使用Pandas做格式化输出;
使用Python标准open方法读取文件并使用read方法从文件对象读取内容为字符串;
使用列表的append方法追加元素;
使用Pandas的Dataframe方法基于列表建立数据框。
4.9.4 文本聚类
文本聚类就是要在一堆文档中,找出哪些文档具有较高的相似性,然后可以针对这些相似性文档的聚合进行类别划分。文本聚类应用场景:提供大规模文档集进行类别划分并提取公共内容的概括和总览;找到潜在的各个文档间的相似度以进行相似度判别、类别修正,以减少浏览相似文档和信息的时间和精力。
通常,聚类分析(也包括其他算法)大多是针对数值型做计算的,K均值这类基于聚类的算法要求只有数值型变量才能得到距离相似度。对于文本聚类而言,由于不同文本集出现的全部都是文字内容,因此无法直接针对这些文本进行聚类。
要实现文本聚类,除了要进行必要的文本数据清洗和预处理外,还有两个前置条件:
分词。分词是实现聚类条件的第一个必要步骤,分词之后会得到不同文本集中已经分割好的单词(也包括中文词语)。
word to vector。也称为文本转向量、词转向量,目的是将不同文本集的单词集合,转换为向量集合,然后通过向量空间模型建立向量矩阵。
完成上述两个基本步骤之后,可以基于向量矩阵做文本聚类分析、情感分析、词频统计、相似词频等。有关中文分词和word to vector的更多介绍,请参照3.12.4节。
本示例中,模拟的是从某商品的60个商品评论中提取用户的评价关键字,然后对关键字进行聚类,找到特定类别的关键字特征。数据源文件comment.txt位于“附件-chapter4”中,默认工作目录为“附件-chapter4”(如果不是,请cd切换到该目录下,否则会报“IOError:File comment.txt does not exist”)。完整代码如下:
# 导入库 import numpy as np import pandas as pd from sklearn.feature_extraction.text import TfidfVectorizer # 基于TF-IDF的词频转向量库 from sklearn.cluster import KMeans import jieba.posseg as pseg # 中文分词 def jieba_cut(comment): word_list = [] # 建立空列表用于存储分词结果 seg_list = pseg.cut(comment) # 精确模式分词[默认模式] for word in seg_list: if word.flag in ['a', 'ag', 'an']: # 只选择形容词 word_list.append(word.word) # 分词追加到列表 return word_list # 读取数据文件 fn = open('comment.txt') comment_list = fn.readlines() # 读取文件内容为列表 fn.close() # word to vector stop_words = [u'…', u'。', u',', u'?', u'!', u'+', u' ', u'、', u':', u';', u'(', u')', u'.', u'-'] # 定义停用词 vectorizer = TfidfVectorizer(stop_words=stop_words, tokenizer=jieba_cut, use_idf=True) # 创建词向量模型 X = vectorizer.fit_transform(comment_list) # 将评论关键字列表转换为词向量空间模型 # K均值聚类 model_kmeans = KMeans(n_clusters=3) # 创建聚类模型对象 model_kmeans.fit(X) # 训练模型 # 聚类结果汇总 cluster_labels = model_kmeans.labels_ # 聚类标签结果 word_vectors = vectorizer.get_feature_names() # 词向量 word_values = X.toarray() # 向量值 comment_matrix = np.hstack((word_values, cluster_labels.reshape(word_values.shape[0], 1))) # 将向量值和标签值合并为新的矩阵 word_vectors.append('cluster_labels') # 将新的聚类标签列表追加到词向量后面 comment_pd = pd.DataFrame(comment_matrix, columns=word_vectors) # 创建包含词向量和聚类标签的数据框 print (comment_pd.head(1)) # 打印输出数据框第1条数据 # 聚类结果分析 comment_cluster1 = comment_pd[comment_pd['cluster_labels'] == 2].drop('cluster_labels', axis=1) # 选择聚类标签值为2的数据,并删除最后一列 word_importance = np.sum(comment_cluster1, axis=0) # 按照词向量做汇总统计 print (word_importance.sort_values(ascending=False)[:5]) # 按汇总统计的值做逆序排序并打印输出前5个词
上述代码以空行分为7个部分。
第一部分:导入库。本示例用到的Numpy、Pandas都是基本处理库,用到了Sklearn做word to vector处理以及K均值聚类,使用结巴分词做中文分词。
第二部分:定义了一个函数jieba_cut用来对每一条文本数据(每个评论)做中文分词,该函数将作为sklearn.feature_extraction.text.TfidfVectorizer的分词器对中文分词。实现该功能时先定义一个空列表用于存储分词结果数据,接着使用jieba.posseg的cut方法进行中文分词,然后使用for循环只将属于特定分类(形容词)的关键字加入分词列表中,返回分词列表。
第三部分:读取数据文件。由于原始数据中存储的是每一条都是一个评论,因此需要将每一条作为列表的一个值。使用Python标准open方法打开文件并使用readlines方法将内容读取为列表。
第四部分:word to vector(文本转向量或词语转向量)。先定义一批要去除的词语列表,定义列表时使用u来表示是unicode字符串;然后使用sklearn.feature_extraction.text的TfidfVectorizer方法创建词语转向量的对象,使用fit_transform方法将评论关键字列表转换为词向量空间模型。TfidfVectorizer方法参数如下:
stop_words:指定为自定义的去除词的列表,不指定默认会使用英文的停用词列表。
tokenizer:用来设置定义的分词器,这里是在上面自定义的结巴分词。默认的分词器对于英文下工作良好,但对于中文来讲效果不佳。
use_idf:设置为True指定TF-IDF方法做词频转向量。
第五部分:使用K均值聚类。先使用KMeans方法创建聚类对象,设置聚类数为3;然后使用fit方法训练聚类模式。
第六部分:聚类结果汇总。
先获取要做汇总的三类值:聚类标签、词向量和向量值。使用聚类模型对象的labels_方法获得聚类标签,使用词语转向量的get_feature_names方法获得向量列表,将转换后的词向量应用toarray方法转化为数组。
建立数据框用于聚类标签、词向量和向量值。使用Numpy的hstack将向量值和向量标签按列合并,得到新的矩阵;通过列表的append方法将聚类标签的列名追加到词向量列表中;通过Pandas的DataFrame方法基于新的矩阵创建数据框,数据框第一行数据如下:
一般不厚不爽不贵不错不高便宜具体准确凹...蓝诚意\
一般 不厚 不爽 不贵 不错 不高 便宜 具体 准确 凹 ... 蓝 诚意 \ 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 贵 足 透亮 重要 难 高 麻烦 cluster_labels 0 0.0 0.0 0.0 0.0 0.0 0.392535 0.0 1.0 [1 rows x 64 columns]
使用hstck和vstack可以实现将两个矩阵做基于行或基于列的合并,这在矩阵处理中经常用到。
数据框包含60行(60个评论)、64列(其中63个词向量和1个聚类标签)。
第七部分:聚类结果分析。本段将从结果中抽取聚类标签为2的评论,发现其评论的显著性词语。使用数据框得到聚类标签为2的数据,并将得到的数据框删除最后一列聚类标签;将得到的新数据框按列最数值汇总,并按逆序排序,得到新关键字按总和排序,前5条数据如下:
好 4.890181 流畅 0.808409 清晰 0.685972 着急 0.641769 慢 0.508659 dtype: float64
通过数据发现,聚类标签为2的评论中,对于商品的评论关键词倾向是积极的,主要关键字是好、流畅、清晰等。
上述过程中,主要需要注意的关键点:
中文分词需要自定义分词器,结巴分词是比较好的分词工具;
将评论列表转换为评论——向量空间模型,然后做文本聚类。
代码实操小结:在本节的代码中,主要使用了以下几个知识点:
使用sklearn.feature_extraction.text的TfidfVectorizer方法将文本转换为向量;
使用文本转向量模型的get_feature_names方法获得向量列表;
使用sklearn.cluster的KMeans方法对文本向量做聚类分析;
使用聚类模型对象的labels_方法获得训练数据的聚类标签;
使用jieba.posseg的cut方法将中文文本切分为带有分类词性标签的关键字;
使用Python标准open方法读取文件并使用readlines方法从文件对象读取内容为列表;
使用列表的append方法追加元素;
使用Pandas的Dataframe方法基于列表建立数据框;
使用Pandas的sort_values方法排序;
使用数据框的head方法只显示前n条数据;
使用toarray方法将对象转换为数组;
使用Numpy的hstack方法将两个矩阵做列合并;
使用Numpy的sum方法沿特定轴做汇总统计;
通过Numpy的shape方法获得矩阵形状,并使用reshape方法做矩阵形状转换。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论