2021泰迪杯-数据挖掘练习1-京东热水器评论分析

  • 时间:
  • 浏览:
  • 来源:互联网

说明:
所有代码和数据可在gitee中获取
地址:https://gitee.com/success123/teddy-cup

本次挖掘目标

​ ①分析某一热水器的用户情感倾向

​ ②从评论文本中挖掘出该热水器的有点与不足

总体流程

文本挖掘相关概念:

①文本挖掘

文本挖掘是一个以半结构(如 WEB 网页)或者无结构(如纯文本)的自然语言文本为对象的数据挖掘,是从大规模文本数据集中发现隐藏的、重要的、新颖的、潜在的有用的规律的过程。

②文本挖掘与数据挖掘

文本挖掘(Text Mining):文档本身是半结构化的或非结构化的,无确定形式并且缺乏机器可理解的语义;

数据挖掘(Data Mining) :其对象以数据库中的结构化数据为主,并利用关系表等存储结构来发现知识。因此,数据挖掘需要建立在对文本集预处理的基础之上。

③自动化或半自动化处理文本的过程

​ 文档聚类、文档分类、文体变化分析及网络挖掘;

​ 语料:报告、信函、出版物等

​ 半结构化的文本库;

​ 词条-文档矩阵

④文本挖掘模型结构

实现方式

爬虫收集数据

​ 爬虫太基础了,而且教程有现成的数据,这里就不做笔记了.

数据抽取

# 导入模块
import pandas as pd
# 读取数据
data = pd.read_csv('content.csv', encoding='utf-8')

​ 使用pd.read_csv(‘content.csv’, encoding=‘utf-8’)后得到的data数据可视化后如下图

data含有很多很多数据,我们进行数据筛选,即取其中为’美的’的品牌进行数据分析

# 获取数据品牌和数量
data["品牌"].value_counts()
# 将数据中关于美的品牌的数据分解出来
midea = data[data.品牌 == '美的']

得到的midea数据可视化后如下图

预处理

得到文本后需要预处理(文本去重,机械语料压缩和短句删除)

文本去重

​ 减少数据冗余

①可能出现大量重复评论(刷单或者系统默认评论)

②同一个人可能出现重复评论(同一个人购买多次服务时会出现)

③复制粘贴上一个人的评论(可能出现)

解决方案

​ 使用pandas中的drop_duplicates()功能进行去重

# 得到美的的评论数据
comment = midea.评论
# 得到美的的评论数量 55774
number_before = comment.shape
# 去重
comment = comment.drop_duplicates()
# 得到去重后美的的评论数量 53049
number_after = comment.shape

可以看到,去重后总处理数据减少了55774-53049=2725项

得到的comment数据可视化后如下图

机械语料压缩

​ 针对当平台限制用户评论最小数字时可能出现的情况

①单一重复

​ 如:

​ 一:非常好非常好非常好非常好非常好非常好非常好非常好非常好非常好非常好

​ 二:好呀好呀好呀好呀好呀好呀好呀好呀好呀好呀好呀好呀好呀好呀好呀好呀好呀

②过分强调

​ 如:

​ 一:真的太贵太贵太贵太贵太贵太贵太贵太贵太贵太贵太贵太贵太贵太贵太贵太贵太贵太贵太贵太贵了

​ 二:真的好快好快好快好快好快好快好快好快好快好快好快好快好快好快好快好快好快好快好快好快

③短句删除

​ 如:

​ 一:还好吧

​ 二:速度超级快

解决方案

​ 设i为重复单词的长度

​ i=1时

​ 如:好好好好好好好好好好好好好好好好好好好好好好好好好好好好好好好

​ i=2时

​ 如:很好很好很好很好很好很好很好很好很好很好很好很好很好很好很好很好

​ 滔滔不绝

​ i>=3时

​ 如:快一点快一点快一点快一点快一点快一点快一点快一点快一点快一点

​ 在中文中,我们很少(反正我是没有)见到3个重复的词或词语组成简短的有意义的句子,故当一个词重复出现3次时我们执行压缩

代码实现:
def condense_1(str):
    for i in [1, 2]:
        j = 0
        while j < len(str)-2*i:
            if str[j: j+i] == str[j+i: j+2*i] and str[j: j+i] == str[j+2*i: j+3*i]:
                k = j+2*i
                while k+i < len(str) and str[j: j+i] == str[k+i: k+2*i]:
                    k += i
                str = str[: j+i] + str[k+i:]
            j += 1
        i += 1
    for i in [3, 4, 5]:
        j = 0
        while j < len(str)-2*i:
            if str[j: j+i] == str[j+i: j+2*i]:
                k = j+i
                while k+i < len(str) and str[j: j+i] == str[k+i: k+2*i]:
                    k += i
                str = str[: j+i] + str[k+i:]
            j += 1
        i += 1
    return str

比如在comment中就发现有一条信息为

# 去重前字符串长度:1411208
comment.astype('str').apply(lambda x: len(x)).sum()
# 去重后赋值给data1:
data1 = comment.astype('str').apply(lambda x: condense_1(x))
# 去重后的字符串长度:1362739
data1.astype('str').apply(lambda x: len(x)).sum()

得到压缩后的信息为

短句删除

​ 在评论进行去重和压缩后,可能会留下许多短句,如"很好",“可以”,“还行”,这样的短句,对我们的情感分析的作用几乎为0,我们选择删去.

解决方案

​ 先将data1中的每行数据长度保留到data2中,然后将data1和data2进行捆绑保存到data3,这时我们得到一个关于数据-长度的表格,最后我们将句子长度>4的保留到另一个数据集data4中

# 得到data1每行数据的长度
data2 = data1.apply(lambda x: len(x))
# concat将数据进行合并    axis维度 0是行 1是列
data3 = pd.concat((data1, data2), axis=1)
# 更改列名
data3.columns = ['评论', '长度']

此时我们得到的data3如下图

统计长度从2-20的数量

# 统计长度从2-20的数量
length = data3['长度'].value_counts().sort_index()[1:20]

得到length可视化后如下图

可以看到短句还是有挺多的,我们取评论长度>4的评论

# 短句删除前字符串行数:(53049,2)
length_data3 = data3.shape
# 短句剔除,易于分析情感
data4 = data3.loc[data3['长度'] > 4, '评论']
# 短句删除后字符串行数 (51810,)
length_data4 = data4.shape

删除短句后的data4可视化后如下图

至此,文本的预处理基本完成,接下来就是分词和情感评价以及构造模型了.

分词处理

​ ①在中文中,只有字、句和段落能够通过明显的分界符进行简单的划界,而对于“词”和“词组”来说,它们的边界模糊,没有一个形式上的分界符。因此,进行中文文本挖掘时,首先应对文本分词,即将连续的字序列按照一定的规范重新组合成词序列的过程。

​ ②分词结果的准确性对后续文本挖掘算法有着不可忽视的影响,如果分词效果不佳,即使后续算法优秀也无法实现理想的效果。例如在特征选择的过程中,不同的分词效果,将直接影响词语在文本中的重要性,从而影响特征的选择。

*分词

基本思想

​ 一个待切分的汉字串可能包含多种分词结果, 将其中概率最大的那个作为该字串的分词结果.

​ 常见的分词基本方法有:最大匹配法,最大概率法

最大匹配法

​ 最大匹配法提出最早,但精度不高,放弃这个方法(原理如下图):

image-20210319221832356

最大概率法:

​ 示例:

S:有意见分歧 W1:有/ 意见/ 分歧/ W2:有意/ 见/ 分歧/

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6HkxROMw-1616271566762)(C:\Users\47312\AppData\Roaming\Typora\typora-user-images\image-20210319221605175.png)]

image-20210319221613994

分词中的两大难题
切分歧义

image-20210319222349144

*新词识别

解决方案
  1、对汉字进行标注,即由字构词(组词);不仅考虑了文字词语出现的频率信息,同时考虑上下文语境

​ ①字的构词位置:

词首(B),词尾(E),词中(M),单字词(S)

​ 1、对汉字进行标注

	上海人均国内生产总值五千美元。

  上/B 海/E 人/B均/E 国/B 内/E 生/B 产/E 总/B 值/E 五/B 千/M 美/M 元/E 。/S

​ 上海/人均/国内/生产/总值/五千美元/。

​ 2、词性标注

​ 汉语里兼类词是指一个词有两种或两种以上的词性。兼类词又称同词异类。

​ 例如:报道,编辑(名词/动词)

2、采用机器学习的方法:

支持向量机(SVM)

最大熵(Maximum Entropy)

隐马模型(HMM)

最大熵隐马模型(MEMM)

条件随机场(CRFs)

优缺点:

优点:对歧义词和未登录词的识别都具有良好的效果;

缺点:训练周期较长,计算量较大。

Markov模型

HMM

隐式Markov模型(HMM模型)

image-20210319230617865

HMM针对中文分词应用-Viterbi算法

​ ①对于输入的词序列,根据语法词典,列出每个词可能的词性候选,构成词网格,即状态空间。(是完整的栅格状态空间的子空间)

​ ②采用Viterbi算法搜索词网格,搜索最佳路径(词性序列、状态序列)。

​ ③计算相关概率时,取-log对数形式,目的是将乘法运算变成加法运算。

​ ④同时,将求最大概率的路径问题转换成求最小费用的路径问题

​ 根据动态规划原理,最优路径具有这样的特性:如果最优路径从结点 i*{t}到终点 i*{T},那么这两点之间的所有可能的部分路径必须是最优的。
​ 依据这一原理,我们只需从时刻 t=1 开始,递推地计算在时刻 t 状态为 i 的各部分路径的最大概率,直至得到时刻 t=T 状态 i 的各条路径的最大概率 P,最优路径的终结点 i
{T}**也同时得到,这就是维特比算法.

​ 由于这次主要数据集为中文,故我们使用Viterbi算法,jieba库是处理中文最好的库,就自定义分词函数了,直接使用jieba库

​ ①jieba——“结巴”中文分词:做最好的 Python 中文分词组件

​ ②算法

​ •基于前缀词典实现高效的词图扫描,生成句子中汉字所有可能成词情况所构成的有向无环图 (DAG)

​ •采用了动态规划查找最大概率路径, 找出基于词频的最大切分组合

​ •对于未登录词,采用了基于汉字成词能力的 HMM 模型,使用了 Viterbi 算法

# 分词
data5 = data4.apply(lambda x: jieba.lcut(x))

jieba分词后data5可视化效果如下

image-20210319233800338

其他分词方法

​ ①逆向最大匹配法(Reverse Maximum method)

	匹配方向与正向最大匹配法相反,是从右向左。

	实验表明:对于汉语来说,逆向最大匹配法比(正向)最大匹配法更有效。

​ ②双向匹配法(Bi-direction Matching method)

​ 比较正向法与逆向法的分词结果,从而决定正确的分词。

​ ③最佳匹配法(Optimum Matching method, OM法)

	将词典中的单词按它们在文本中的出现频度的大小排列,高频度的单词排在前,频度低的单词排在后,从而提高匹配的速度。

去除停用词

​ 停用词也被称为功能词,与其他词相比通常是没有实际含义的,停用词一般是指在文本内容中出现频率极高或极低的介词,代词,虚词,以及一些与情感无关的字符

种类

​ ①频繁出现但无实际意义

​ 这些词应用十分广泛,有’如果’,'比如’等

​ ②无明确意义的虚词,助词

​ 包括了语气助词,副词,介词,连接词,有’的’,'在’等

解决方案

停用词表(停用表大概如下图所示)

image-20210319234246988image-20210319234307389

导入停用表

# 导入停用词
stop = pd.read_csv('stoplist.txt', sep='fighting', encoding='utf-8', header=None, engine='python')
# 导入停用表后无空格,自定义一个空格加入stop
stop = [' '] + list(stop[0])

stop如下图

# 除去停用词
data6 = data5.apply(lambda x: [i for i in x if i not in stop])

除去停用词后data6可视化如下图:

情感评价

①情感分析主要是判别文本的情感倾向性,即属于正面、负面、中性。

②它通常由两个标准来衡量:

​ 1、正面或负面

​ 2、强弱程度

​ 主要采用情感评价表,在情感评价表中,有很多词-分数,可对之前的分词列表与情感评价表进行匹配,最后得出该评论的分数

​ 如果分数s>=0,则为正向评论

​ 如果分数s<0,则为负向评论

​ 说明:分数<0不一定为带有"批评语气",只是强度较弱而已

情感词的获取

​ ①、构建为一张情感词表

​ ②、中文分词处理

​ ③、打分

​ ④、判别情感倾向

# 导入情感评价表
feeling = pd.read_csv('BosonNLP_sentiment_score.txt', sep=' ', header=None, encoding='utf-8')
# 改变列名
feeling.columns = ['word', 'score']

feeling可视化后如下图所示:

# 将felling中的word字段转化为列表
feel = list(feeling['word'])
# 自定义查分函数
def classfi(my_list):
    SumScore = 0
    for i in my_list:
        if i in feel:
            SumScore += feeling['score'][feel.index(i)]
        return SumScore
# 运算时间较长
data7 = data6.apply(lambda x: classfi(x))

对data7进行可视化如下图:

按照数值分为正面评论和负面评论

# 把评论划分为2部分:正向评论和负向评论
pos = data6[data7 >= 0]
neg = data6[data7 < 0]

​ 由于情感评价需要大量运算,程序运行时间很长,我们将运算结果保存起来,以便之后重复分析使用.

# 保存
pos.to_csv('pos.txt', encoding='utf-8', index=False, header=False)
neg.to_csv('neg.txt', encoding='utf-8', index=False, header=False)

​ 将data7和data6聚合成data8

# 将data7和data6聚合成data8
data8 = pd.concat((data7, data6), axis=1)
# 改变data8的列名,便于查看
data8.columns = ['评分', '评论']

data8可视化如下图:

*基于LDA模型的主题分析

判断两个文档相似性的方法是通过查看两个文档共同出现的单词的多少,这种方法没有考虑到文字背后的语义关联,可能在两个文档共同出现的单词很少甚至没有,但两个文档是相似的。

​ 在判断文档相关性的时候需要考虑到文档的语义,而语义挖掘的利器是主题模型,LDA就是其中一种比较有效的模型。

​ LDA模型也被称为三层贝叶斯概率模型,包含文档(d),主题(z),词(w)三层结构,能够有效对文本进行建模,和传统的空间向量模型(VSM)相比,增加了概率的信息,通过LDA主题模型,能够挖掘数据集中潜在主题,进而分析数据集的集中关注点及其相关特征词

​ LDA模型采用词袋模型(Bag Of Words, BOW)将每一篇文档视为一个词频向量,从而将文本信息转化为易于建模的数字信息

​ LDA主题模型涉及到贝叶斯理论,Dirichlet分布,多项分布,图模型,变分推断,EM算法,Gibbs抽样等知识

主题分析

​ 算法步骤:

​ 1:对每一篇文档,从主题分布中抽取一个主题

​ 2:从上述被抽到的主题所对应的单词分布中抽取一个词

​ 3:重复上述过程直至遍历文档中的每一个词

​ 可以用生成模型来看文档和主题两件事.所谓生成模型,就是说一篇文章的每个词都是通过"以一定概率选择某个主题,并从这个主题中以一定概率选择某个词语"这一过程得到的.要生成一篇文档,它里面的每个词语出现的概率为:

20210318105536.png

即:

​ ①其中”文档-词语”矩阵表示每个文档中每个单词的词频,即出现的概率;”主题-词语”矩阵表示每个主题中每个单词的出现概率;”文档-主题”矩阵表示每个文档中每个主题出现的概率。

​ ②给定一系列文档,通过对文档进行分词,计算各个文档中每个单词的词频就可以得到左边这边”文档-词语”矩阵。 主题模型就是通过左边这个矩阵进行训练,学习出右边两个矩阵。

​ ③虽然LDA可以直接对文本做主题分析,但是文本的正面评价和负面评价混淆在一起,并且由于分词粒度的影响(否定词或程度词等),可能在一个主题下生成一些令人迷惑的词语。因此,将文本分为正面评价和负面评价两个文本,再分别进行LDA主题分析

​ ④接下来需要对两文本进行分词,保存成两个txt文档,并和停用词文档一起作为LDA程序的输入。

​ ⑤经过LDA主题分析后,评论文本被聚成3个主题,每个主题下生成10个最有可能出现的词语以及相应的概率。

Gensim库

​ Gensim用于从原始的非结构化的文本中,无监督地学习到文本隐层的主题向量表达.它支持TF-IDF,LSA,LDA,和word2vec在内的多种主题模型算法,支持流式训练,并提供相似度算法,信息检索等一些常用人物的API接口

​ 基本概念:

​ ①语料(Corpus)一组原始文本的集合,用于无监督地训练文本主题的隐层结构.语料中不需要人工标注的附加信息.在Gensim中,Corpus通常是一个可迭代的对象(比如列表).每一次迭代返回一个可用于表达文本对象的稀疏向量.

​ ②向量(Vector)由一组文本特征构成的列表,是一段文本在Gensim中的内部表达

​ ③稀疏向量(Sparse Vector)通常我们可以略去向量中多余的0元素,此时,向量中的每一个元素是一个(Key,Value)的tuple

​ ④模型(Model)是一个抽象的术语,定义了两个向量空间的交换(即从文本的一种向量表达变换为另一种向量表达)

代码实现:

​ 将neg[0]和pos[0]分割放入neg[1]和pos[1]作为语料库

# 定义一个分割函数,然后用 apply 广播
neg[1] = neg[0].apply(lambda s: s.split(' '))
pos[1] = pos[0].apply(lambda s: s.split(' '))

neg可视化后如下图(pos同理):

构造LDA模型

# 正面主题分析
# 建立词典
pos_dict = corpora.Dictionary(pos[1])
# 建立语料库
pos_corpus = [pos_dict.doc2bow(i) for i in pos[1]]
# LDA模型训练
pos_lda = models.LdaModel(pos_corpus, num_topics=3, id2word=pos_dict)
print("\n正面评价")
# 输出每个主题
for i in range(3):
    print("主题%d : " % i)
    print(pos_lda.print_topic(i))


# 负面主题分析
# 建立词典
neg_dict = corpora.Dictionary(neg[1])
# 建立语料库
neg_corpus = [neg_dict.doc2bow(i) for i in neg[1]]
# LDA模型训练
neg_lda = models.LdaModel(neg_corpus, num_topics=3, id2word=neg_dict)
print("\n负面评价")
# 输出每个主题
for i in range(3):
    print("主题%d : " % i)
    print(neg_lda.print_topic(i))

最后结果:

正面评论结果如下左图: 负面评论结果如下右图

至此:评分和选择主题都已经成功了,但需要优化的地方太多了.

自我总结

分析完模型:可以优化的地方:

1.jieba库分词出现新词的时候虽然有一定识别新词的能力,但是如果能增加特定字典会更好

​ 相关链接:https://blog.csdn.net/gzmfxy/article/details/78994396

2.情感分析字典需要重新设置并优化

3.LDA模型的主题分解需要处理一些简单词才行

本文链接http://www.dzjqx.cn/news/show-617287.html