126在线民宿 UGC 数据挖掘实战--基于字典的评论主题挖掘

基于字典的评论主题挖掘

数据准备

此次使用基于主题词典的方式解决主题句提取的冷启动问题。启发式规则主要依据简单结构特点抽取主题词和主题句,比如使用《旅游民宿基本要求与评价》标准中的评级指标,通过具体的指标属性词对民宿进行细粒度的评估,具体的评价参考指标如下图所示。


image.png

使用 Pandas 加载在线数据表格,并查看数据维度和第一行数据。

import pandas as pd
data = pd.read_csv('https://labfile.oss.aliyuncs.com/courses/2628/1-1.csv')
print(data.shape)
data.head(1)

数据属性如下表所示


image.png

数据预处理

本实验的数据预处理包含对词语粒度的词性识别和句子切分,实现用户评论更易于统计和挖掘的目的。我们首先对分词器预热,能加快 jieba 分词。

import jieba.posseg as pseg

jieba 词性标注预热,使用 jieba 中的词性标注模块对输入的句子进行处理,产生每个词和对应的词性。

list(pseg.cut('垃圾饭店'))

对待处理的数据进行整合。

comment_process = zip(data['order_id'].tolist(), data['content'].tolist())
comment_process

开始对评论进行分句,利用词性标注技术通过找到用户评价中的词性为标点符号的利用换行符进行替代的方法,此方法解决了一句评论中出现多主题的评论,可以达到了降维且更加突出主题的目的,大幅减少无关评论的影响,为后续的主题评论的查询和情感分析做铺垫。例如:“环境很不错!装修古朴,喜欢”这句评论就可以被拆分为:“环境很不错”、“装修古朴”、“喜欢”三句评论,其中的主题就会直接通过主题词典查询出来,使得总体语料进一步扩充,对于主题属性词构建来说,主题词本身可以直接匹配到相应的主题,批量的对用户评论进行处理,需要一些时间进行处理。

# 添加可视化模块
from tqdm.notebook import tqdm
sentence_cut_list = list()
order_id_list = list()
for order_id, sentence in tqdm(list(comment_process)):
    # 剔除长度小于 1 的主题句
    if len(sentence) > 1:
        sentence_ = str()
        for word, flag in pseg.cut(sentence):
            if flag != 'x':
                sentence_ += word
            else:
                # 使用统一的分隔符号代替所有标点符号
                sentence_ += '\\\\'
        # 去除空格
        comment_cut_split = sentence_.split('\\\\')
        sentence_cut_list += comment_cut_split
        order_id_list += [order_id] * len(comment_cut_split)

对分句后的的数据进行聚合,此目的就是将用户评论中的多个评价主题尽心切分,分散到子句中去,通过 oder_id 字段可以与 data 数据表格进行链接。

comment_cut_data = pd.DataFrame(
    {'comment_cut': sentence_cut_list, 'order_id': order_id_list})
comment_cut_data.head(5)

打印数据行数。

comment_cut_data.shape

针对用户评论中的空值进行清理,清除空字符串,并打印处理后的前 5 行数据。

import numpy as np

# 将空字符串替换为 nan
comment_cut_data.replace(to_replace='', value=np.nan, regex=True, inplace=True)
# 去除 nan 所在的行
comment_cut_data.dropna(axis=0, how='any', inplace=True)
comment_cut_data.head(5)

通过对用户评论进行清理,我们发现有不少空评论,统计最后的数据量。

comment_cut_data.shape

主题词提取

主题词提取一般不需要人工标注的语料,利用某些方法发现文本中比较重要的词作为主题词,进行提取。首先抽取出候选词,然后对各个候选词进行打分,然后输出 topK 个分值最高的候选词作为主题词。根据打分的策略不同,有不同的算法,例如 TF-IDF、TextRank 等算法。
基于 TF-IDF 的主题词提取
原理简介:TF-IDF(Term Frequency/Inverse Document Frequency)是信息检索领域非常重要的搜索词重要性度量,当一个词在某个文档中频率高并且在其他文档出现少的时候,其 TF-IDF 值越高,此方法能过滤一些常见词,并保留能提供更多信息的重要词,速度极快。
加载 jieba 分文本分析模块。

from jieba import analyse

jieba 内部存在 TF-IDF 的实现,我们直接批量对用户评论进行基于 TF-IDF 主题词抽取,处理过程需要一些时间。

# 对用户评论进行处理,只保留名词,显示 TF-IDF 的权重并求得前 topK 个主题词
topK = 200
sentence = str(comment_cut_data['comment_cut'].tolist())
%time keywords_tfidf = analyse.extract_tags(sentence, topK=topK,withWeight=True,allowPOS=('n'))

打印 topn 的 TF-IDF 提取结果,我们发现使用抽取用户评论中的名词生成的 TF-IDF 的结果能直接反映出顾客的话题倾向,诸如“服务”、“性价比”等之类的词语,TF-IDF 处理的输出的值是小于 1 ,第一位属于中心词,数值依次减小。

topn = 10
# 对处理好的 TF-IDF 数据进行数据转换
keywords_tfidf_pair = [[i[0], i[1]] for i in keywords_tfidf]
keywords_tfidf_pair[:topn]

安装可视化工具 pyecharts。

!pip install pyecharts==1.6.2

通过对提取的用户评论中的主题词进行可视化,并按照 TF-IDF 的大小进行展示,我们发现用户对当前的民宿和酒店并未完全区分清楚,重庆市区的民宿较多,在大部分的顾客心中仍旧是以酒店来对待,用户评论中占比较大的“景区”、“性价比”、“环境”等名词显示出顾客的关注倾向,民宿当前多在景区附近,环境明显区别于酒店,价格也是一大优势。

# 加载词云模块
from pyecharts.charts import WordCloud
# 加载 pyecharts 配置模块配置词云显示
from pyecharts import options as opts

# 初始化词云模块
comment_wordcloud = WordCloud()

# 设置词云中的文字大小,可以根据需要改变
comment_wordcloud.add(
    series_name="",
    data_pair=keywords_tfidf_pair,
    word_size_range=[10, 100]
)

# 设置词云的全局配置
comment_wordcloud.set_global_opts(
    title_opts=opts.TitleOpts(title="TF-IDF 主题词词云分析",
                              # 设置标题文字大小
                              title_textstyle_opts=opts.TextStyleOpts(font_size=15)),
    # 显示词云中的词频数字
    tooltip_opts=opts.TooltipOpts(is_show=True),
)

# 直接在 notebook 中显示词云
comment_wordcloud.render_notebook()

基于 TextRank 的主题词提取
原理简介:TextRank 算法是通过谷歌的 PageRank 网页排名算法演变过来的,原理保持一致。通过词之间的相邻关系构建网络,然后用算法公式迭代计算每个节点的 rank 值,通过排序 rank 值即可得到主题词。
jieba 内部存在 TextRank 的实现,我们直接批量对用户评论进行基于 TextRank 主题词抽取,需要一些时间处理。

# 设置全语料抽取的 topk、TextRank 结果的权重显示、提取词性设置 n 为名词
sentence = str(comment_cut_data['comment_cut'].tolist())
%time keywords_textrank = analyse.textrank(sentence, topK=topK, withWeight=True, allowPOS=('n'))

打印 topn 的 TextRank 主题词排序,TextRank 处理的输出最大值是 1 ,属于话题中心词,数值依次减小。

# 对处理好的 TextRank 数据进行数据转换
keywords_textrank_pair = [[i[0], i[1]] for i in keywords_textrank]
keywords_textrank_pair[:topn]

使用词云模块对主题词进行可视化处理,我们发现在此用户评价语料下,两种方式产生出来的顾客话题分布保持一致。

# 加载词云模块
tr_wordcloud = WordCloud()
# 设置词云中的文字大小,可以根据需要改变
tr_wordcloud.add(
    series_name="",
    data_pair=keywords_textrank_pair,
    word_size_range=[10, 100]
)
# 设置词云的全局配置
tr_wordcloud.set_global_opts(
    title_opts=opts.TitleOpts(title="TextRank 主题词词云分析",
                              # 设置标题文字大小
                              title_textstyle_opts=opts.TextStyleOpts(font_size=15)),
    # 显示词云中的词频数字
    tooltip_opts=opts.TooltipOpts(is_show=True),
)
# 直接在 notebook 中显示词云
tr_wordcloud.render_notebook()

建立话题词典

通过上述提取的主题词,利用人工选取的方式建立主题词典,通过建立主题字典的方式对切分后的用户评论进行提取,此字典可以暂时对用户评论中的主题句进行提取,后续利用扩增字典即可,此方式具有速度极快和易于搭建的优点,缺点是精度和召回度受到字典覆盖度的影响,严格定义每个主题词只属于一个主题,避免引发歧义。后续大家可以利用此方式打下的标签进行深度学习训练,以提高精度和召回。

topic_words_list = {
    '环境': ['环境', '周边', '风景', '空气', '江景', '小区', '景点', '夜景', '街', '周围', '景区', '声音', '景色'],
    '价格': ['价格', '房价', '性价比', '价位', '单价', '价钱'],
    '特色': ['特色', '装潢', '布置', '建筑', '结构', '格调', '装修', '设计', '风格', '隔音'],
    '设施': ['设施', '设备', '条件', '硬件', '房间', '热水', '马桶', '电梯', '阳台', '卫生间', '洗手间', '空调', '被子', '床', '大厅', '电话', '电', '摆设'],
    '餐饮': ['餐饮', '早餐', '咖啡', '味道', '饭', '菜', '水果'],
    '交通': ['交通', '车程', '地段', '路程', '停车', '机场'],
    '服务': ['服务', '态度', '前台', '服务员', '老板', '掌柜', '店家', '工作人员'],
    '体验': ['体验', '整体', '感觉'],
}

提取需要处理的内容,转化为列表,并打印数据统计量。

batch_comment_process = comment_cut_data['comment_cut'].tolist()
len(batch_comment_process)

开始批量处理,执行每一句都进行字符串过滤,得出来的句子进行主题划分,得不出来的按照 “其他” 进行标记。

# 定义话题收集列表
comment_topic = []
for comment_cut_list in tqdm(batch_comment_process):
    topic_wait_insert = str()
    for topic, topic_word_list in topic_words_list.items():
        for topics_words in topic_word_list:
            if topics_words in comment_cut_list:
                topic_wait_insert = topic
                # 匹配到即停止
                break

    # 如果找不到主题,则标记为“其他”
    if len(topic_wait_insert):
        comment_topic.append(topic_wait_insert)
    else:
        topic_wait_insert = '其他'
        comment_topic.append(topic_wait_insert)
len(comment_topic)

最后产生主题句,按照每个主题进行匹配找出,打印前 5 行进行查看,由于不存在对应的主题词,所以标注为“其他”。

comment_cut_data['comment_topic'] = comment_topic
comment_cut_data.head()

对主题句进行统计,查看主题的分布情况,我们发现标记为“其他”的占大多数,服务、设施、环境是用户比较关心的三个主要方面。

# 聚合顾客话题的统计信息
user_topic, user_topic_count = [], []
for i in comment_cut_data['comment_topic'].groupby(
        comment_cut_data['comment_topic']).count().items():
    # 取出顾客话题标签
    user_topic.append(i[0])
    # 取出计数信息
    user_topic_count.append(i[1])

开始对顾客话题分布进行统计可视化。

from pyecharts.charts import Bar

# 初始化饼图
bar = Bar(init_opts=opts.InitOpts(width='660px', height='350px'))

# 横坐标为用户真实打分
bar.add_xaxis(user_topic)
# 纵坐标为顾客情感极性的统计量,不设置纵坐标的名字
bar.add_yaxis("", user_topic_count)

# 设置图表的名字
bar.set_global_opts(title_opts=opts.TitleOpts(title='Comment_Topic'))

# 设置数据展示,并设置显示数据
bar.set_series_opts(label_opts=opts.LabelOpts(is_show=True), )

# 直接在 jupyter 上进行显示
bar.render_notebook()

随机抽查一个主题进行检查,查看对应的主题句和主题词的情况,我们发现通过主题词匹配能很快的对用户评论进行过滤和筛选。但是准确度受到所构建词典的制约,后续试验将尝试更多的方法进行扩充主题属性词典。

comment_cut_data[comment_cut_data['comment_topic'] == '环境'].head(5)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,692评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,482评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,995评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,223评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,245评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,208评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,091评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,929评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,346评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,570评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,739评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,437评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,037评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,677评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,833评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,760评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,647评论 2 354

推荐阅读更多精彩内容