【NLP】情绪分析与酒店评论
🔎大家好,我是Sonhhxg_柒,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流🔎
📝个人主页-Sonhhxg_柒的博客_CSDN博客 📃
🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝
📣系列专栏 - 机器学习【ML】 自然语言处理【NLP】 深度学习【DL】
🖍foreword
✔说明⇢本人讲解主要包括Python、机器学习(ML)、深度学习(DL)、自然语言处理(NLP)等内容。
如果你对这个系列感兴趣的话,可以关注订阅哟👋
过滤和情绪分析操作
您可能已经注意到,数据集存在一些问题。有些列充满了无用的信息,有些则看起来不正确。如果它们是正确的,则不清楚它们是如何计算的,并且无法通过您自己的计算来独立验证答案。
练习:更多的数据处理
稍微清理一下数据。添加稍后有用的列,更改其他列中的值,并完全删除某些列。
-
初始列处理
-
下降
lat
和lng
-
将
Hotel_Address
值替换为以下值(如果地址包含相同的城市和国家,请将其更改为仅城市和国家)。这些是数据集中仅有的城市和国家:
荷兰阿姆斯特丹
西班牙巴塞罗那
伦敦,英国
意大利米兰
法国巴黎
维也纳,奥地利
def replace_address(row): if "Netherlands" in row["Hotel_Address"]: return "Amsterdam, Netherlands" elif "Barcelona" in row["Hotel_Address"]: return "Barcelona, Spain" elif "United Kingdom" in row["Hotel_Address"]: return "London, United Kingdom" elif "Milan" in row["Hotel_Address"]: return "Milan, Italy" elif "France" in row["Hotel_Address"]: return "Paris, France" elif "Vienna" in row["Hotel_Address"]: return "Vienna, Austria" # Replace all the addresses with a shortened, more useful form df["Hotel_Address"] = df.apply(replace_address, axis = 1) # The sum of the value_counts() should add up to the total number of reviews print(df["Hotel_Address"].value_counts())
现在您可以查询国家级数据:
display(df.groupby("Hotel_Address").agg({"Hotel_Name": "nunique"}))
酒店_地址 酒店名称 荷兰阿姆斯特丹 105 西班牙巴塞罗那 211 伦敦,英国 400 意大利米兰 162 法国巴黎 458 维也纳,奥地利 158
-
-
流程酒店元评论专栏
-
降低
Additional_Number_of_Scoring
-
替换
Total_Number_of_Reviews
为该酒店实际在数据集中的评论总数 -
替换
Average_Score
为我们自己计算的分数
# Drop `Additional_Number_of_Scoring`
df.drop(["Additional_Number_of_Scoring"], axis = 1, inplace=True)
# Replace `Total_Number_of_Reviews` and `Average_Score` with our own calculated values
df.Total_Number_of_Reviews = df.groupby('Hotel_Name').transform('count')
df.Average_Score = round(df.groupby('Hotel_Name').Reviewer_Score.transform('mean'), 1)
-
流程审查栏
-
删除
Review_Total_Negative_Word_Counts
,Review_Total_Positive_Word_Counts
,Review_Date
和days_since_review
-
保持
Reviewer_Score
,Negative_Review
,Positive_Review
原样, -
Tags
暂时保留
- 我们将在下一节中对标签进行一些额外的过滤操作,然后将删除标签
-
-
流程审阅者列
-
降低
Total_Number_of_Reviews_Reviewer_Has_Given
-
保持
Reviewer_Nationality
标记列
该Tag
列存在问题,因为它是存储在该列中的列表(以文本形式)。不幸的是,此列中子部分的顺序和数量并不总是相同的。人类很难识别出感兴趣的正确短语,因为有 515,000 行和 1427 家酒店,每家酒店的评论者可以选择的选项略有不同。这就是 NLP 大放异彩的地方。您可以扫描文本并找到最常用的短语,并计算它们。
不幸的是,我们对单个词不感兴趣,而是对多词短语(例如商务旅行)感兴趣。对这么多数据(6762646 个词)运行多词频分布算法可能会花费大量时间,但如果不查看数据,这似乎是一项必要的开支。这就是探索性数据分析派上用场的地方,因为您已经看到了标签的示例,例如[' Business trip ', ' Solo traveler ', ' Single Room ', ' Stayed 5 nights ', ' Submitted from a mobile device ']
,您可以开始询问是否可以大大减少您必须进行的处理。幸运的是,它是 - 但首先您需要按照几个步骤来确定感兴趣的标签。
过滤标签
请记住,数据集的目标是添加情绪和列,以帮助您选择最佳酒店(为您自己或委托您制作酒店推荐机器人的客户)。您需要问自己这些标签在最终数据集中是否有用。这是一种解释(如果您出于其他原因需要数据集,则不同的标签可能会留在/不在选择范围内):
- 旅行的类型是相关的,应该保留
- 客人组的类型很重要,应该保留
- 客人入住的房间、套房或工作室的类型无关紧要(所有酒店的房间基本相同)
- 提交评论的设备无关紧要
- 如果您归因于他们更喜欢酒店的更长时间的住宿,评论者入住的晚数可能是相关的,但这是一个延伸,并且可能无关紧要
总之,保留2种标签并删除其他标签。
首先,您不想计算标签,直到它们的格式更好,这意味着删除方括号和引号。您可以通过多种方式执行此操作,但您希望最快,因为处理大量数据可能需要很长时间。幸运的是,pandas 有一个简单的方法来完成这些步骤。
# Remove opening and closing brackets
df.Tags = df.Tags.str.strip("[']")
# remove all quotes too
df.Tags = df.Tags.str.replace(" ', '", ",", regex = False)
每个标签都变成了这样的东西:Business trip, Solo traveler, Single Room, Stayed 5 nights, Submitted from a mobile device
.
接下来我们发现一个问题。一些评论或行有 5 列,一些 3 列,一些 6 列。这是数据集创建方式的结果,并且难以修复。您想获得每个短语的频率计数,但它们在每个评论中的顺序不同,因此计数可能会关闭,并且酒店可能不会获得应得的分配标签。
相反,您将使用不同的顺序对我们有利,因为每个标签都是多词但也用逗号分隔!最简单的方法是创建 6 个临时列,每个标记插入到与其在标记中的顺序相对应的列中。然后,您可以将 6 列合并为一个大列并value_counts()
在结果列上运行该方法。打印出来,你会看到有 2428 个独特的标签。这是一个小样本:
标签 | 数数 |
---|---|
休闲之旅 | 417778 |
从移动设备提交 | 307640 |
夫妻 | 252294 |
住了一晚 | 193645 |
住了2晚 | 133937 |
独行旅客 | 108545 |
住了 3 晚 | 95821 |
出差 | 82939 |
团体 | 65392 |
有小孩的家庭 | 61015 |
住了4晚 | 47817 |
双人间 | 35207 |
标准双人间 | 32248 |
高级双人间 | 31393 |
有大孩子的家庭 | 26349 |
豪华双人间 | 24823 |
双人或双床间 | 22393 |
住了5晚 | 20845 |
标准双人或双床间 | 17483 |
经典双人房间 | 16989 |
高级双人或双床间 | 13570 |
2 间客房 | 12393 |
一些常见的标签Submitted from a mobile device
对我们来说没有用,所以在计算短语出现之前删除它们可能是一件聪明的事情,但这是一个如此快速的操作,你可以将它们留在里面并忽略它们。
去除停留时间标签
删除这些标签是第 1 步,它会稍微减少要考虑的标签总数。请注意,您不会将它们从数据集中删除,只需选择将它们从考虑中删除,作为要在评论数据集中计数/保留的值。
停留时间 | 数数 |
---|---|
住了一晚 | 193645 |
住了2晚 | 133937 |
住了 3 晚 | 95821 |
住了4晚 | 47817 |
住了5晚 | 20845 |
住了 6 晚 | 9776 |
住了 7 晚 | 7399 |
住了 8 晚 | 2502 |
住了 9 晚 | 1293 |
... | ... |
有各种各样的房间、套房、工作室、公寓等等。它们的含义大致相同,并且与您无关,因此请不要考虑它们。
房间类型 | 数数 |
---|---|
双人间 | 35207 |
标准双人间 | 32248 |
高级双人间 | 31393 |
豪华双人间 | 24823 |
双人或双床间 | 22393 |
标准双人或双床间 | 17483 |
经典双人房间 | 16989 |
高级双人或双床间 | 13570 |
最后,这是令人愉快的(因为它根本不需要太多处理),您将得到以下有用的标签:
标签 | 数数 |
---|---|
休闲之旅 | 417778 |
夫妻 | 252294 |
独行旅客 | 108545 |
出差 | 82939 |
团体(与有朋友的旅行者相结合) | 67535 |
有小孩的家庭 | 61015 |
有大孩子的家庭 | 26349 |
带着宠物 | 1405 |
您可能会争辩说这或多或少Travellers with friends
是相同的,并且将两者结合起来是公平的。Group
识别正确标签的代码是标签笔记本。
最后一步是为每个标签创建新列。然后,对于每个评论行,如果该Tag
列与新列之一匹配,则添加 1,如果不匹配,则添加 0。最终结果将统计有多少评论者选择了这家酒店(总计),例如,商务与休闲,或带宠物,这是推荐酒店时的有用信息。
# Process the Tags into new columns
# The file Hotel_Reviews_Tags.py, identifies the most important tags
# Leisure trip, Couple, Solo traveler, Business trip, Group combined with Travelers with friends,
# Family with young children, Family with older children, With a pet
df["Leisure_trip"] = df.Tags.apply(lambda tag: 1 if "Leisure trip" in tag else 0)
df["Couple"] = df.Tags.apply(lambda tag: 1 if "Couple" in tag else 0)
df["Solo_traveler"] = df.Tags.apply(lambda tag: 1 if "Solo traveler" in tag else 0)
df["Business_trip"] = df.Tags.apply(lambda tag: 1 if "Business trip" in tag else 0)
df["Group"] = df.Tags.apply(lambda tag: 1 if "Group" in tag or "Travelers with friends" in tag else 0)
df["Family_with_young_children"] = df.Tags.apply(lambda tag: 1 if "Family with young children" in tag else 0)
df["Family_with_older_children"] = df.Tags.apply(lambda tag: 1 if "Family with older children" in tag else 0)
df["With_a_pet"] = df.Tags.apply(lambda tag: 1 if "With a pet" in tag else 0)
保存您的文件
最后,使用新名称保存数据集。
df.drop(["Review_Total_Negative_Word_Counts", "Review_Total_Positive_Word_Counts", "days_since_review", "Total_Number_of_Reviews_Reviewer_Has_Given"], axis = 1, inplace=True)
# Saving new data file with calculated columns
print("Saving results to Hotel_Reviews_Filtered.csv")
df.to_csv(r'../data/Hotel_Reviews_Filtered.csv', index = False)
情绪分析操作
在最后一部分中,您将对评论列应用情绪分析并将结果保存在数据集中。
练习:加载和保存过滤后的数据
请注意,现在您正在加载上一节中保存的过滤数据集,而不是原始数据集。
import time
import pandas as pd
import nltk as nltk
from nltk.corpus import stopwords
from nltk.sentiment.vader import SentimentIntensityAnalyzer
nltk.download('vader_lexicon')
# Load the filtered hotel reviews from CSV
df = pd.read_csv('../../data/Hotel_Reviews_Filtered.csv')
# You code will be added here
# Finally remember to save the hotel reviews with new NLP data added
print("Saving results to Hotel_Reviews_NLP.csv")
df.to_csv(r'../data/Hotel_Reviews_NLP.csv', index = False)
删除停用词
如果您要在负面和正面评论列上运行情绪分析,则可能需要很长时间。在具有快速 CPU 的功能强大的测试笔记本电脑上进行测试,需要 12 - 14 分钟,具体取决于使用的情绪库。这是一个(相对)长的时间,因此值得研究是否可以加快速度。
第一步是删除停用词或不会改变句子情绪的常见英语单词。通过删除它们,情绪分析应该运行得更快,但准确度不会降低(因为停用词不会影响情绪,但会减慢分析速度)。
最长的负面评论是 395 个词,但去掉停用词后是 195 个词。
删除停用词也是一项快速操作,在测试设备上从超过 515,000 行的 2 个评论列中删除停用词需要 3.3 秒。根据您的设备 CPU 速度、RAM、是否有 SSD 以及其他一些因素,您可能需要更多或更少的时间。操作的相对较短意味着如果它提高了情感分析时间,那么它是值得做的。
from nltk.corpus import stopwords
# Load the hotel reviews from CSV
df = pd.read_csv("../../data/Hotel_Reviews_Filtered.csv")
# Remove stop words - can be slow for a lot of text!
# Ryan Han (ryanxjhan on Kaggle) has a great post measuring performance of different stop words removal approaches
# https://www.kaggle.com/ryanxjhan/fast-stop-words-removal # using the approach that Ryan recommends
start = time.time()
cache = set(stopwords.words("english"))
def remove_stopwords(review):
text = " ".join([word for word in review.split() if word not in cache])
return text
# Remove the stop words from both columns
df.Negative_Review = df.Negative_Review.apply(remove_stopwords)
df.Positive_Review = df.Positive_Review.apply(remove_stopwords)
执行情绪分析
现在您应该计算负面和正面评论列的情绪分析,并将结果存储在 2 个新列中。情感测试将其与评论者对同一评论的评分进行比较。例如,如果情绪认为负面评论的情绪为 1(非常正面的情绪)和正面的评论情绪为 1,但评论者给酒店的分数可能最低,那么评论文本与分数不匹配,或者情绪分析器无法正确识别情绪。您应该期望某些情绪得分是完全错误的,这通常是可以解释的,例如评论可能非常讽刺“我当然喜欢睡在没有暖气的房间里”,而情绪分析器认为这是积极情绪,
NLTK 提供了不同的情绪分析器来学习,你可以替换它们,看看情绪是否或多或少准确。此处使用 VADER 情绪分析。
Hutto, CJ & Gilbert, EE (2014)。VADER:用于社交媒体文本情感分析的基于规则的简约模型。第八届博客和社交媒体国际会议(ICWSM-14)。密歇根州安娜堡,2014 年 6 月。
from nltk.sentiment.vader import SentimentIntensityAnalyzer
# Create the vader sentiment analyser (there are others in NLTK you can try too)
vader_sentiment = SentimentIntensityAnalyzer()
# Hutto, C.J. & Gilbert, E.E. (2014). VADER: A Parsimonious Rule-based Model for Sentiment Analysis of Social Media Text. Eighth International Conference on Weblogs and Social Media (ICWSM-14). Ann Arbor, MI, June 2014.
# There are 3 possibilities of input for a review:
# It could be "No Negative", in which case, return 0
# It could be "No Positive", in which case, return 0
# It could be a review, in which case calculate the sentiment
def calc_sentiment(review):
if review == "No Negative" or review == "No Positive":
return 0
return vader_sentiment.polarity_scores(review)["compound"]
稍后在您的程序中,当您准备好计算情绪时,您可以将其应用于每个评论,如下所示:
# Add a negative sentiment and positive sentiment column
print("Calculating sentiment columns for both positive and negative reviews")
start = time.time()
df["Negative_Sentiment"] = df.Negative_Review.apply(calc_sentiment)
df["Positive_Sentiment"] = df.Positive_Review.apply(calc_sentiment)
end = time.time()
print("Calculating sentiment took " + str(round(end - start, 2)) + " seconds")
这在我的计算机上大约需要 120 秒,但在每台计算机上都会有所不同。如果您想打印结果并查看情绪是否与评论相符:
df = df.sort_values(by=["Negative_Sentiment"], ascending=True)
print(df[["Negative_Review", "Negative_Sentiment"]])
df = df.sort_values(by=["Positive_Sentiment"], ascending=True)
print(df[["Positive_Review", "Positive_Sentiment"]])
在挑战中使用该文件之前要做的最后一件事就是保存它!您还应该考虑对所有新列重新排序,以便它们易于使用(对于人类来说,这是一种外观上的改变)。
# Reorder the columns (This is cosmetic, but to make it easier to explore the data later)
df = df.reindex(["Hotel_Name", "Hotel_Address", "Total_Number_of_Reviews", "Average_Score", "Reviewer_Score", "Negative_Sentiment", "Positive_Sentiment", "Reviewer_Nationality", "Leisure_trip", "Couple", "Solo_traveler", "Business_trip", "Group", "Family_with_young_children", "Family_with_older_children", "With_a_pet", "Negative_Review", "Positive_Review"], axis=1)
print("Saving results to Hotel_Reviews_NLP.csv")
df.to_csv(r"../data/Hotel_Reviews_NLP.csv", index = False)
您应该运行分析笔记本的整个代码(在运行过滤笔记本以生成 Hotel_Reviews_Filtered.csv 文件之后)。
回顾一下,步骤如下:
- 原始数据集文件Hotel_Reviews.csv在上一课中使用explorer notebook进行了探索
- Hotel_Reviews.csv 由过滤笔记本过滤,生成Hotel_Reviews_Filtered.csv
- Hotel_Reviews_Filtered.csv 由情绪分析笔记本处理,生成Hotel_Reviews_NLP.csv
- 在下面的 NLP 挑战中使用 Hotel_Reviews_NLP.csv
结论
开始时,您有一个包含列和数据的数据集,但并非所有数据都可以验证或使用。您已经浏览了数据,过滤掉了您不需要的内容,将标签转换为有用的内容,计算了您自己的平均值,添加了一些情绪列,并希望学习了一些有关处理自然文本的有趣知识。
相关文章
- MongoDB索引顺序导致慢SQL分析过程
- 天猫小店、京东小店的问题分析
- 卷烟全品规自动分拣解决方案分析
- 园区智能视频监控分析系统
- 【日志审计】极易上手搭建自己日志采集服务器分析日志(winlogbeat+Elasticsearch+Kibana)
- 模型性能分析:ROC 与 AUC
- 自然语言处理NLP:主题LDA、情感分析疫情下的新闻文本数据|附代码数据
- NLP涉及技术原理和应用简单讲解【二】:paddle(分布式训练、AMP自动混合精度训练、模型量化、模型性能分析)
- 从手工作业到工业革命!Nature文章:生物图像分析被深度学习彻底改变的五个领域
- 【R环境配置】在R3.6.2版本上安装转录组分析相关R包
- AI智能视频分析EasyCVR视频融合平台增加历史音视频下载功能
- 分析最佳实践:使用Oracle进行财务分析(oracle财务)
- Linux图形化监控:最全面的机器性能分析(linux图形化监控)
- 深入浅出MySQL数据库分析(mysql分析)
- 扫描Linux系统25端口扫描:深度分析(linux25端口)
- MongoDB版本选择:优势与风险分析(mongodb版本选择)
- Redis与MSSQL的性能对比及优势分析(redis和mssql)
- Linux系统面临CC攻击:威胁分析与防御策略(cc攻击 linux)
- MySQL 慢查询优化之路 如何进行分析(mysql慢查询分析)
- 利用Oracle串联函数聚焦数据挖掘分析(oracle串联函数)
- Oracle中循环的多层嵌套结构分析(oracle中 循环嵌套)
- 分析Redis操作耗时从实践到可视化(redis耗时分析)
- asp.net权限管理分析
- AndroidHttpClient使用Cookie应用分析
- MYSQL主从不同步延迟原理分析及解决方案