123在线民宿 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

数据 EDA
查看数据描述,可以选择数据进行统计描述,注意 describe 函数只对浮点和字符型有效,我们发现除了自增字段 order_id,其余不能进行数据描述,其余不能进行数据描述,用户打分列表的数据出现异常。

data.describe()

查看数据结构,Non-Null Count 显示是否有空值存在,Dtype 显示数据属性,user_score 显示为 object,证实用户打分列表的数据出现异常,里面出现了脏数据。

data.info()

数据清洗
针对用户打分出现的问题进行处理,首先打印用户打分的去重数据

data['user_score'].unique()

我们发现用户打分出现了字符型数据,需要进行清理。

# 复制一个pandas的处理副本
data_clean = data[data.user_score != '信息不存在'].copy()
# 数据检查
data_clean['user_score'].unique()

对清理之后的用户打分进行可视化,发现大部分的用户打分在 4 分以上,原始用户打分整体呈现满意趋势。

import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline
sns.countplot(data=data_clean, x='user_score')
plt.show()

为了和模型打分能进行一致性评价,我们对用户打分数据进行归一化操作。

data_clean['user_score_normal'] = data_clean['user_score'].apply(
    lambda x: round(float(x) / 5, 3))
data_clean.head(1)

用户打分数据标注
假设用户打分和用户情感极性一致,满足用户评论情感建模需求,可以对用户高于 3 分的评价标注为 1,否则为 0。

data_clean['label'] = data_clean['user_score'].apply(
    lambda x: 1 if float(x) > 3 else 0)
data_clean['label'].unique()

针对语料标签出现了失衡的问题,课程设置在后续实验中研究数据失衡对模型的影响和对应的解决标签失衡的解决方法,如上采样/下采样/ smote 采样等等。

# 查看标签的分布情况
sns.countplot(data=data_clean, x='label')
plt.show()

情感分析模型训练

模型介绍
依据统计学模型假设,假设用户评论中的词语之间相互独立,用户评价中的每一个词语都是一个特征,当用户好评中常常出现某一个词语的时候,通过概率模型可以得知含有该词语的评价大概率是好评,基于此我们使用 scikit-learn 库的 MultinomialNB 对用户评论进行训练和情感预测。

# 加载用户评论向量化模块
from sklearn.feature_extraction.text import CountVectorizer
# 对测试集和训练集进行划分
from sklearn.model_selection import train_test_split
import numpy as np
import jieba

jieba 分词器预热,第一次使用需要加载字典和缓存,通过结果看出返回的是分词的列表。

' '.join(jieba.lcut(str('使用分词器进行分词')))

批量对用户评价进行分词。

%time data_clean['text_cut'] = data_clean['content'].apply(lambda x: " ".join(jieba.lcut(str(x))))
data_clean.head(1)

用户评论向量化
用户评论向量化的目的就是将用户评论转化为计算机可以处理的数字信息,通过建立向量化词典对用户评论进行向量化。

# 调整低词频带来的影响
vectorizer = CountVectorizer(token_pattern='\[?\w+\]?', max_features=5000)
vectorizer.fit_transform(data_clean['text_cut'])

查看词典中的词频信息。

vectorizer.vocabulary_

数据集合划分
按照训练集 8 成和测试集 2 成的比例对数据集进行划分,并检查划分之后的数据集数量。

x_train_, y_train_, x_test, y_test = train_test_split(
    data_clean['text_cut'], data_clean['label'], test_size=0.2, random_state=1)
for data_set in [x_train_, y_train_, x_test, y_test]:
    print(data_set.shape)

通过刚刚建立起来的词典,开始对训练集和测试集合进行向量化。

x_train = vectorizer.transform(x_train_)
y_train = vectorizer.transform(y_train_)
for data_set in [x_train, y_train, x_test, y_test]:
    print(data_set.shape)

模型加载和训练

# 模型加载贝叶斯分类模型
from sklearn.naive_bayes import MultinomialNB
own_model = MultinomialNB()
# 模型训练
%time own_model.fit(x_train, x_test)
own_model_result = own_model.predict(y_train)

模型评价
"classification_report" 函数通过传入原始的标签和预测的标签可以直接将分类器性能进行度量,利用常用的分类模型评价指标对训练好的模型进行模型评价,acc 评价被正确预测的样本占总样本的比例,Precision 是衡量模型精确率的指标,它是指模型识别出的文档数与识别的文档总数的比率,衡量的是模型的查准率。Recall 召回率也称为敏感度,它是指模型识别出的相关文档数和文档库中所有的相关文档数的比率,衡量的是检索系统的查全率,表示正样本在被正确划分样本中所占的比例,F 值是精确率与召回率的调和平均数。

# 打印测试报告
from sklearn import metrics
print(metrics.classification_report(y_test, own_model_result))

用户评论情感极性推理
基于模型的情感极性预测可以很好的对情感极性进行预测,直接输出用户评论中积极情感对应的概率值作为用户情感极性映射,预测结果根据其中的情感副词不同给出不同的情感极性判断。对照自己输入的测试数据来看,模型受到标签数据不平衡问题的影响,对积极评论识别比较好,但是对消极评论的情感极性预测不合理,后续可以继续改进。

predict_text = ['这个民宿环境不错啊', '这家民宿环境真的垃圾']
test_text = vectorizer.transform(
    [" ".join(jieba.lcut(i)) for i in predict_text])
test_pro = own_model.predict_proba(test_text)
# 直接输出用户评论对应的积极情感对应的概率值作为用户情感极性映射
[i[1] for i in test_pro]

快速使用模型进行批量的情感分析
使用训练好的模型对用户评论进行情感极性预测,并输出情感为积极的概率来表示用户评论的情感趋势。

# 用户评论向量化
data_text = vectorizer.transform(data_clean['text_cut'])
# 批量对用户评论进行情感推理
%time data_clean['sa_model_score'] = [round(i[1], 5) for i in own_model.predict_proba(data_text)]

情感可视化

首先对用户自己的原始打分进行可视化,显示原始的用户满意度趋势,横坐标表示用户情感极性,越靠近 1 用户情感越积极,纵坐标表示用户数量。

model_score = data_clean['sa_model_score']
user_score = data_clean['user_score_normal']
# 定义画布大小
plt.rcParams['figure.figsize'] = (8.0, 8.0)
plt.hist(user_score, bins=np.arange(0, 1, 0.01))
plt.xlabel("user_sa_score")
plt.ylabel("count")
plt.title('Customer_Satisfaction_Analysis')
plt.show()

对模型依据用户评价进行建模而预测出来的用户满意度进行可视化。

plt.hist(model_score, bins=np.arange(0, 1, 0.01))
plt.xlabel("model_sa_score")
plt.ylabel("count")
plt.title('Customer_Satisfaction_Analysis')
plt.show()

对两种情感极性进行对比可视化,模型在情感分布上较用户打分更加均匀,通过对用户打分进行建模可以使得将用户评价在同一个标准中进行评价,减少顾客个人的喜好差异对评价的影响。

# 使用红色描述模型情感极性
plt.hist(model_score, bins=np.arange(0, 1, 0.01),
         color='red', label='model_sa_score')
# 使用蓝色描述用户原始打分
plt.hist(user_score, bins=np.arange(0, 1, 0.01),
         color='blue', label='user_sa_score')
plt.legend()
plt.xlabel('count')
plt.ylabel('sa_score')
plt.title('Double_Customer_Satisfaction_Analysis')
plt.show()

使用 Matplotlib 将用户自主打分(归一化之后的分数)和情感模型分析的结果进行对比,通过数据的描述分析得知,模型标签和用户实际打分表现接近,证实使用用户打分映射用户情感极性的可行性。

# 挑选两列数据进行描述统计
static = data_clean[['sa_model_score', 'user_score_normal']]
static.describe()

在实验的开始,我们了解到实验数据中存在缺失的情况,暂时在数据的建模中不进行使用,使用在最开始定义的 data_all,在最后我们使用模型对缺失用户打分的评价数据进行批量的情感极性预测,以此来填补数据不完整的问题。全量用户评价使用模型预测,耐心等候,需要花费一点时间。

# 全量用户评论分词
data['text_cut'] = data['content'].apply(
    lambda x: " ".join(jieba.lcut(str(x))))
# 全量用户评论向量化
data_text = vectorizer.transform(data['text_cut'])
# 全量对用户评论情感推理
data['sa_model_score'] = [round(i[1], 5)
                          for i in own_model.predict_proba(data_text)]
plt.hist(data['sa_model_score'], bins=np.arange(0, 1, 0.01))
plt.xlabel("model_sa_score")
plt.ylabel("count")
plt.title('Final_Customer_Satisfaction_Analysis')
plt.show()

使用 Pandas 的 describe 函数进行满意度描述,从 5677 条用户评价中,通过平均值得出整体民宿满意度为较好,超过一半的用户满意度达到 0.9 以上,2-8 分布情况在整体较为满意,但是关注到用户情感峰值出现在消极的部分,所以仍旧存在改进的地方,后续我们会进行细粒度的用户意见挖掘。

# 对用户情感极性进行描述统计
static = data[['sa_model_score']]
static.describe()

预览最终的数据文件:

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

推荐阅读更多精彩内容

  • 摘要 我在《爬取简书百万页面 分析简书用户画像》 中分析了简书用户的书籍喜好,这里继续尝试通过简书影评文章的抓取、...
    hirainchen阅读 4,883评论 10 52
  • 1. 概述 DRIP:Data Rich,Information Poor(现状)DRIP指的是现实生活中普遍存在...
    九桢阅读 1,917评论 5 14
  • 表情是什么,我认为表情就是表现出来的情绪。表情可以传达很多信息。高兴了当然就笑了,难过就哭了。两者是相互影响密不可...
    Persistenc_6aea阅读 125,050评论 2 7
  • 16宿命:用概率思维提高你的胜算 以前的我是风险厌恶者,不喜欢去冒险,但是人生放弃了冒险,也就放弃了无数的可能。 ...
    yichen大刀阅读 6,052评论 0 4