zl程序教程

您现在的位置是:首页 >  其它

当前栏目

【NLP】情绪分析与酒店评论

分析 NLP 评论 酒店 情绪
2023-09-14 09:06:10 时间

 🔎大家好,我是Sonhhxg_柒,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流🔎

📝个人主页-Sonhhxg_柒的博客_CSDN博客 📃

🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝​

📣系列专栏 - 机器学习【ML】 自然语言处理【NLP】  深度学习【DL】

 🖍foreword

✔说明⇢本人讲解主要包括Python、机器学习(ML)、深度学习(DL)、自然语言处理(NLP)等内容。

如果你对这个系列感兴趣的话,可以关注订阅哟👋

过滤和情绪分析操作

您可能已经注意到,数据集存在一些问题。有些列充满了无用的信息,有些则看起来不正确。如果它们是正确的,则不清楚它们是如何计算的,并且无法通过您自己的计算来独立验证答案。

练习:更多的数据处理

稍微清理一下数据。添加稍后有用的列,更改其他列中的值,并完全删除某些列。

  1. 初始列处理

    1. 下降latlng

    2. 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
  2. 流程酒店元评论专栏

  3. 降低Additional_Number_of_Scoring

  4. 替换Total_Number_of_Reviews为该酒店实际在数据集中的评论总数

  5. 替换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)

  1. 流程审查栏

    1. 删除Review_Total_Negative_Word_CountsReview_Total_Positive_Word_Counts,Review_Datedays_since_review

    2. 保持Reviewer_ScoreNegative_Review,Positive_Review原样,

    3. Tags暂时保留

    • 我们将在下一节中对标签进行一些额外的过滤操作,然后将删除标签
  2. 流程审阅者列

  3. 降低Total_Number_of_Reviews_Reviewer_Has_Given

  4. 保持Reviewer_Nationality

标记列

Tag列存在问题,因为它是存储在该列中的列表(以文本形式)。不幸的是,此列中子部分的顺序和数量并不总是相同的。人类很难识别出感兴趣的正确短语,因为有 515,000 行和 1427 家酒店,每家酒店的评论者可以选择的选项略有不同。这就是 NLP 大放异彩的地方。您可以扫描文本并找到最常用的短语,并计算它们。

不幸的是,我们对单个词不感兴趣,而是对多词短语(例如商务旅行)感兴趣。对这么多数据(6762646 个词)运行多词频分布算法可能会花费大量时间,但如果不查看数据,这似乎是一项必要的开支。这就是探索性数据分析派上用场的地方,因为您已经看到了标签的示例,例如[' Business trip ', ' Solo traveler ', ' Single Room ', ' Stayed 5 nights ', ' Submitted from a mobile device '],您可以开始询问是否可以大大减少您必须进行的处理。幸运的是,它是 - 但首先您需要按照几个步骤来确定感兴趣的标签。

过滤标签

请记住,数据集的目标是添加情绪和列,以帮助您选择最佳酒店(为您自己或委托您制作酒店推荐机器人的客户)。您需要问自己这些标签在最终数据集中是否有用。这是一种解释(如果您出于其他原因需要数据集,则不同的标签可能会留在/不在选择范围内):

  1. 旅行的类型是相关的,应该保留
  2. 客人组的类型很重要,应该保留
  3. 客人入住的房间、套房或工作室的类型无关紧要(所有酒店的房间基本相同)
  4. 提交评论的设备无关紧要
  5. 如果您归因于他们更喜欢酒店的更长时间的住宿,评论者入住的晚数可能是相关的,但这是一个延伸,并且可能无关紧要

总之,保留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 文件之后)。

回顾一下,步骤如下:

  1. 原始数据集文件Hotel_Reviews.csv在上一课中使用explorer notebook进行了探索
  2. Hotel_Reviews.csv 由过滤笔记本过滤,生成Hotel_Reviews_Filtered.csv
  3. Hotel_Reviews_Filtered.csv 由情绪分析笔记本处理,生成Hotel_Reviews_NLP.csv
  4. 在下面的 NLP 挑战中使用 Hotel_Reviews_NLP.csv

结论

开始时,您有一个包含列和数据的数据集,但并非所有数据都可以验证或使用。您已经浏览了数据,过滤掉了您不需要的内容,将标签转换为有用的内容,计算了您自己的平均值,添加了一些情绪列,并希望学习了一些有关处理自然文本的有趣知识。