数据分析与挖掘:电商产品评论数据情感分析

电商产品评论数据情感分析

  • 1. 背景与挖掘目标
  • 2. 分析方法与过程
    • 2.1 数据抽取
    • 2.2 评论预处理
    • 2.3 LDA 主题分析

1. 背景与挖掘目标

  • 项目为《Python 数据分析与挖掘实战》第 15 章:电商产品评论数据情感分析。参考了书中的源代码,并补充了机械压缩去词实现。
  • 挖掘目标为评论文本数据中提取有价值的内容

2. 分析方法与过程

2.1 数据抽取

'''读取数据'''
import pandas as pd
import numpy as np

inputfile = 'chapter15/demo/data/huizong.csv'
data = pd.read_csv(inputfile, encoding='utf-8')
data.head()
Id 已采 已发 电商平台 品牌 评论 时间 型号 PageUrl
0 1 True False 京东 AO 挺好的 ,安装人员很负责 指的夸奖 2014/8/26 9:07 AO史密斯(A.O.Smith) ET300J-60 电热水器 60升 http://s.club.jd.com/productpage/p-1008025-s-0...
1 2 True False 京东 AO 自己安装的,感觉蛮好。后续追加。 2014/10/17 14:24 AO史密斯(A.O.Smith) ET300J-60 电热水器 60升 http://s.club.jd.com/productpage/p-1008025-s-0...
2 3 True False 京东 AO \n\nAO史密斯(A.O.Smith) ET300J-60 电热水器 60升 还没安装 2014/11/12 13:36 AO史密斯(A.O.Smith) ET300J-60 电热水器 60升 http://s.club.jd.com/productpage/p-1008025-s-0...
3 4 True False 京东 AO 还没装,等安装之后再来吧,为装修备的货。 2014/11/16 0:04 AO史密斯(A.O.Smith) ET300J-60 电热水器 60升 http://s.club.jd.com/productpage/p-1008025-s-0...
4 5 True False 京东 AO 大小刚刚好,安装收了140材料费,活接、弯头啥的。试机顺利,16度加热到30度用了6分钟左右... 2014/11/11 8:11 AO史密斯(A.O.Smith) ET300J-60 电热水器 60升 http://s.club.jd.com/productpage/p-1008025-s-0...
'''抽取美的的评论'''
outputfile = 'chapter15/demo/tmp2/meidi.txt'
data = data[['评论']][data['品牌'] == '美的']
data.to_csv(outputfile, index=False, header=False)
data.head()
评论
95898 京东商城信得过,买的放心,用的省心、安心、放心!
95899 给公司宿舍买的,上门安装很快,快递也送的及时,不错的。给五分吧
95900 美的值得信赖,质量不错
95901 不错不错的哦,第一次在京东买这些产品,感觉相当好
95902 很满意,水方一晚上都还是热的早上还能再洗,

2.2 评论预处理

1. 文本去重

'''文本去重'''
outputfile = 'chapter15/demo/tmp2/meidi_1.txt'
l1 = len(data)
data = pd.DataFrame(data['评论'].unique(), columns=data.columns)
l2 = len(data)
data.to_csv(outputfile, index=False, header=False, encoding='utf-8')
print('删除了%s条评论' % (l1 - l2))
删除了2725条评论
data.head()
评论
0 京东商城信得过,买的放心,用的省心、安心、放心!
1 给公司宿舍买的,上门安装很快,快递也送的及时,不错的。给五分吧
2 美的值得信赖,质量不错
3 不错不错的哦,第一次在京东买这些产品,感觉相当好
4 很满意,水方一晚上都还是热的早上还能再洗,

2. 机械压缩去词,可以删除评论开头和结尾重复的词语,如“为什么为什么为什么安装费这么贵”压缩为“为什么安装费这么贵”,“真的挺好挺好挺好挺好”压缩为“真的挺好”

'''定义机械压缩去词函数'''

def compress(string):
    string = string.strip()    # 删除换行符
    str_list = list(string)    # 分隔字符为列表
    s1 = [str_list[0]]    # 初始化 s1 列表为第一个字符
    s2 = []
    del_idx = []
    for i in range(1, len(str_list)):
        # 判断当前字符是否为最后一个字符
        if i == len(str_list) - 1:    # 当前字符为最后一个字符,进行压缩判断
            if s2 != []:
                s2.append(str_list[i])
                if s1 == s2:    # 如果 s1 和 s2 重复,则将需删除的索引加入 del_idx 中
                    # print('{}为最后一个字符,发现重复,删除的索引为{}~{}'.format(i, i-len(s2), i-1))
                    del_idx.extend(range(i-len(s2), i))
            else:    # 不重复则什么也不做
                # print('{}为最后一个字符,未发现重复'.format(i))
                pass
        # 当前字符不是最后一个字符,判断当前字符和 s1 的第一个字符是否相同
        elif s1[0] == str_list[i]:    # 当前字符和 s1 的第一个字符相同,判断 s2 是否为空
            if s2 == []:    # s2 为空,将该字符放入 s2
                # print('{}和 s1 相同,s2 为空,放入 s2'.format(i))
                s2 = [str_list[i]]
            # s2 不为空,进行压缩判断
            elif s1 == s2:    # 如果 s1 和 s2 重复,则将需删除的索引加入 del_idx 中,然后清空 s2
                # print('{}和 s1 相同,s2 不为空,发现重复,删除的索引为{}~{}'.format(i, i-len(s2), i-1))
                del_idx.extend(range(i-len(s2), i))
                s2 = [str_list[i]]
            else:    # 不重复,清空两个列表,将字符放入 s1
                # print('{}和 s1 相同,s2 不为空,未发现重复,清空列表,放入 s1'.format(i))
                s1 = [str_list[i]]
                s2 = []
        # 当前字符和 s1 的第一个字符不同,进行压缩判断
        elif (s1 == s2) & (len(s2) >= 2):    # 如果 s1 和 s2 重复,且字符数大于等于2,则将需删除的索引加入 del_idx 中
            # print('{}和 s1 不相同,发现重复且字符数大于等于2,删除的索引为{}~{}'.format(i, i-len(s2), i-1))
            del_idx.extend(range(i-len(s2), i))
            # 清空两个列表,将字符放入 s1
            s1 = [str_list[i]]
            s2 = []
        elif s1 != s2:    # 如果 s1 和 s2 不重复,判断 s2 是否为空
            if s2 == []:    # s2 为空,放入 s1
                # print('{}和 s1 不相同,s2 为空,放入 s1'.format(i))
                s1.append(str_list[i])
                #print(len(s1))
            else:    # s2 不为空,放入 s2
                # print('{}和 s1 不相同,s2 不为空,放入 s2'.format(i))
                s2.append(str_list[i])
                #print(len(s2))
        else:
            #print(i)
            pass
    # 删除字符,从后往前删,避免索引变化
    del_idx = sorted(del_idx)
    for t in range(len(del_idx)-1, -1, -1):
        del str_list[del_idx[t]]
    new_str = ''.join(str_list)
    return new_str

# 字符串倒序函数
sort = lambda x: ''.join(list(x)[::-1])
# 开头去重
data = pd.DataFrame(data['评论'].astype(str).apply(compress))
# 倒序,然后开头去重(即末尾去重)
data = pd.DataFrame(data['评论'].astype(str).apply(sort))
data = pd.DataFrame(data['评论'].astype(str).apply(compress))
# 还原
data = pd.DataFrame(data['评论'].astype(str).apply(sort))

# 删除 4 字及以下的评论
l1 = len(data)
data = data[data['评论'].apply(lambda x: len(x) > 4)]
l2 = len(data)
print('删除了%s条评论' % (l1 - l2))
删除了993条评论
# 保存数据
outputfile = 'chapter15/demo/tmp2/meidi_2.txt'
data.to_csv(outputfile, index=False, header=False, encoding='utf-8')

2.3 LDA 主题分析

  • (备注:书中情感分析使用的是 ROSTCM6 软件,没找到 Linux 版本,鉴于操作并不复杂,后面直接使用书中提供的数据进行分析)
  1. ROST 情感分析得到的数据具有评分前缀,需先删除
'''删除前缀评分代码'''
import pandas as pd

inputfile1 = 'chapter15/demo/data/meidi_jd_process_end_负面情感结果.txt'
inputfile2 = 'chapter15/demo/data/meidi_jd_process_end_正面情感结果.txt'
outputfile1 = 'chapter15/demo/tmp2/meidi_neg.txt'
outputfile2 = 'chapter15/demo/tmp2/meidi_pos.txt'

data1 = pd.read_csv(inputfile1, encoding='utf-8', header=None)
data2 = pd.read_csv(inputfile2, encoding='utf-8', header=None)

data1 = pd.DataFrame(data1[0].str.replace('.*?\d+?\\t ', ''))
data2 = pd.DataFrame(data2[0].str.replace('.*?\d+?\\t ', ''))

data1.to_csv(outputfile1, index=False, header=False, encoding='utf-8')
data2.to_csv(outputfile2, index=False, header=False, encoding='utf-8')
  1. 使用结巴分词模块进行分词,并和停用词文档一次作为 LDA 程序输入
'''分词代码'''
import jieba

outputfile1 = 'chapter15/demo/tmp2/meidi_neg_cut.txt'
outputfile2 = 'chapter15/demo/tmp2/meidi_pos_cut.txt'

mycut = lambda x: ' '.join(jieba.cut(x))
data1 = data1[0].apply(mycut)
data2 = data2[0].apply(mycut)

data1.to_csv(outputfile1, index=False, header=False, encoding='utf-8')
data2.to_csv(outputfile2, index=False, header=False, encoding='utf-8')
  1. 使用 Gensim 库完成 LDA 分析:可以看到负面主题中 3 个主题的高频特征词为“安装”,“师傅”,“安装费”等,主要体现安装费用、安装师傅服务等不能让客户满意
'''LDA 代码'''
stoplist = 'chapter15/demo/data/stoplist.txt'    # 停用词表
stop = pd.read_csv(stoplist, encoding='utf-8', header=None, sep='tipdm')
# sep 设置分隔词,由于 csv 默认使用半角逗号作为分隔词,该词刚好在停用词表中,因此会导致出错,所以需要设置一个不存在的分隔词
stop = [' ',''] + list(stop[0])    # pandas 会自动过滤空格,添加进去

neg = pd.DataFrame(data1)
pos = pd.DataFrame(data2)
neg[1] = neg[0].apply(lambda x: x.split(' '))    # 分隔
neg[2] = neg[1].apply(lambda x: [i for i in x if i not in stop])    # 判断是否在停用词表中
pos[1] = pos[0].apply(lambda x: x.split(' '))
pos[2] = pos[1].apply(lambda x: [i for i in x if i not in stop])

from gensim import corpora, models

# 负面主题分析
neg_dict = corpora.Dictionary(neg[2])    # 建立词典
neg_corpus = [neg_dict.doc2bow(i) for i in neg[2]]    # 建立语料库
neg_lda = models.LdaModel(neg_corpus, num_topics=3, id2word=neg_dict)    # LDA 模型训练
for i in range(3):
    print(neg_lda.print_topic(i))
    print('\n')
print('***************************************************************')
# 正面主题分析
pos_dict = corpora.Dictionary(pos[2])    # 建立词典
pos_corpus = [pos_dict.doc2bow(i) for i in pos[2]]    # 建立语料库
pos_lda = models.LdaModel(pos_corpus, num_topics=3, id2word=pos_dict)    # LDA 模型训练
for i in range(3):
    print(pos_lda.print_topic(i))
    print('\n')
0.038*"安装" + 0.024*"买" + 0.022*"热水器" + 0.013*"师傅" + 0.013*"美的" + 0.011*"京东" + 0.009*"问题" + 0.008*"一个" + 0.007*"东西" + 0.006*"售后"


0.033*"安装" + 0.020*"好" + 0.016*"买" + 0.016*"知道" + 0.015*"热水器" + 0.014*"不错" + 0.013*"加热" + 0.011*"n" + 0.008*"使用" + 0.008*"挺"


0.047*"安装" + 0.028*"不错" + 0.014*"安装费" + 0.013*"好" + 0.012*"热水器" + 0.011*"师傅" + 0.011*"美的" + 0.011*"有点" + 0.010*"不好" + 0.010*"东西"


***************************************************************
0.059*"安装" + 0.021*"师傅" + 0.013*"一个" + 0.013*"热水器" + 0.013*"加热" + 0.011*"买" + 0.011*"好" + 0.011*"美的" + 0.010*"有点" + 0.008*"安装费"


0.101*"不错" + 0.096*"好" + 0.038*"安装" + 0.023*"买" + 0.022*"送货" + 0.021*"东西" + 0.020*"挺" + 0.016*"热水器" + 0.016*"很快" + 0.016*"速度"


0.021*"美的" + 0.017*"n" + 0.016*"值得" + 0.015*"京东" + 0.013*"买" + 0.012*"信赖" + 0.011*"热水器" + 0.011*"问题" + 0.011*"安装" + 0.010*"品牌"

源代码及数据文件参考:https://github.com/Raymone23/Data-Mining

你可能感兴趣的:(项目)