在本节中,您将使用前面课程中的技术对大型数据集进行一些探索性数据分析。一旦您对各个列的用处有了很好的了解,您将了解到:
- 如何删除不必要的列
- 如何根据现有列计算一些新数据
- 如何保存生成的数据集以用于最终挑战
到目前为止,您已经了解了文本数据与数字类型数据的不同之处。如果它是由人类书写或??说出的文本,则可以对其进行分析以找到模式和频率、情感和意义。本课将带您进入一个具有真正挑战的真实数据集:欧洲的 515K 酒店评论数据,包括CC0:公共领域许可。它是从 Booking.com 的公共资源中抓取的。数据集的创建者是刘家申。
准备
你会需要:
- 使用 Python 3 运行 .ipynb 笔记本的能力
- 熊猫
- NLTK,您应该在本地安装
- 该数据集可在欧洲的 Kaggle 515K 酒店评论数据中找到。解压后大约有 230 MB。
/data
将其下载到与这些 NLP 课程相关的根文件夹中。
使用 Python、酒店评论数据集和 NLTK 的情绪分析,您可以发现:
- 评论中最常用的单词和短语是什么?
- 描述酒店的官方标签是否与评论分数相关(例如,对于有小孩的家庭对特定酒店的负面评论是否 比单人旅行者更负面,也许表明它对单人旅行者更好?)
- NLTK 情绪得分是否与酒店评论者的数字得分“一致”?
让我们探索一下您已下载并保存在本地的数据集。在 VS Code 甚至 Excel 等编辑器中打开文件。
数据集中的标头如下:
Hotel_Address、Additional_Number_of_Scoring、Review_Date、Average_Score、Hotel_Name、Reviewer_Nationality、Negative_Review、Review_Total_Negative_Word_Counts、Total_Number_of_Reviews、Positive_Review、Review_Total_Positive_Word_Counts、Total_Number_of_Reviews_Reviewer_Has_Given、Reviewer_Score、标签、days_since_review、lat、lng
在这里,它们以一种更容易检查的方式分组:
酒店专栏
Hotel_Name
,Hotel_Address
,lat
(纬度),lng
(经度)- 使用lat和lng,您可以使用 Python 绘制显示酒店位置的地图(可能对负面和正面评论进行颜色编码)
- Hotel_Address 显然对我们没有用处,我们可能会将其替换为国家/地区以便于排序和搜索
-
Average_Score
- 根据数据集创建者的说法,这一列是酒店的平均得分,根据去年的最新评论计算得出。这似乎是一种不寻常的计算分数的方法,但它是抓取的数据,所以我们现在可以把它当作面值。
-
Total_Number_of_Reviews
- 这家酒店收到的评论总数 - 不清楚(不写一些代码)这是否指的是数据集中的评论。
-
Additional_Number_of_Scoring
- 这意味着给出了评论分数,但评论者没有写正面或负面评论
Reviewer_Score
- 这是一个数值,在最小值和最大值 2.5 和 10 之间最多有 1 个小数位
- 没有解释为什么 2.5 是可能的最低分数
Negative_Review
- 如果审稿人什么都没写,这个字段将有“ No Negative ”
- 请注意,评论者可能会在负面评论栏中写正面评论(例如“这家酒店没有什么不好的”)
Review_Total_Negative_Word_Counts
- 较高的负字数表示较低的分数(不检查多愁善感)
Positive_Review
- 如果审稿人什么都没写,这个字段将有“ No Positive ”
- 请注意,评论者可能会在正面评论栏中写负面评论(例如“这家酒店没有任何好处”)
Review_Total_Positive_Word_Counts
- 较高的正字数表示较高的分数(不检查多愁善感)
Review_Date
和days_since_review
- 可能会对评论应用新鲜度或陈旧度衡量标准(旧评论可能不如新评论准确,因为酒店管理发生了变化,或者已经完成装修,或者添加了游泳池等)
Tags
- 这些是评论者可以选择的简短描述符来描述他们的客人类型(例如单人或家庭)、他们拥有的房间类型、入住时间以及如何提交评论。
- 不幸的是,使用这些标签是有问题的,请查看下面讨论它们的有用性的部分
Total_Number_of_Reviews_Reviewer_Has_Given
- 这可能是推荐模型中的一个因素,例如,如果您可以确定拥有数百条评论的多产评论者更有可能是负面的而不是正面的。但是,任何特定评论的评论者都没有唯一的代码标识,因此无法链接到一组评论。有 30 位评论者有 100 条或更多评论,但很难看出这对推荐模型有何帮助。
Reviewer_Nationality
- 有些人可能会认为,由于民族倾向,某些民族更有可能给予正面或负面的评价。小心将此类轶事视图构建到您的模型中。这些是国家(有时是种族)的刻板印象,每个评论者都是根据他们的经验撰写评论的个人。它可能已经通过许多镜头过滤,例如他们以前的酒店住宿,旅行的距离以及他们的个人气质。认为他们的国籍是评分的原因是很难证明的。
平均分 | 评论总数 | 审稿人分数 | 负面 评论 |
正面评价 | 标签 |
---|---|---|---|---|---|
7.8 | 1945 | 2.5 | 目前这不是一家酒店,而是一个建筑工地,我从一大早开始就被吓坏了,一整天的建筑噪音让我在长途旅行后休息并在房间里工作,人们整天都在工作,即在相邻的房间里拿着手提钻我要求一个换了房间,但没有安静的房间 更糟糕的是,我被多收了我晚上退房,因为我不得不离开很早的航班并收到了适当的账单 一天后,酒店在未经我同意的情况下再次收取超过预订价格的费用这是一个可怕的地方不要在这里预订来惩罚自己 | 没有什么可怕的地方远离 | 出差情侣标准双人房住了2晚 |
标签
如上所述,乍一看,用于
Tags
对数据进行分类的想法是有道理的。不幸的是,这些标签不是标准化的,这意味着在给定的酒店中,选项可能是Single room、Twin room和Double room,但在下一家酒店,它们是Deluxe Single Room、Classic Queen Room和Executive King Room。这些可能是相同的东西,但有很多变化,选择变成:- 尝试将所有条款更改为单一标准,这非常困难,因为不清楚每种情况下的转换路径是什么(例如,经典单人房映射到单人房,但高级大床房有庭院花园或城市景观很多更难映射)
- 我们可以采用 NLP 方法并测量某些术语(例如Solo、Business Traveler或Family with young kids)应用于每家酒店时的频率,并将其纳入推荐中
以 Group 类型为例。列中的该字段有 1025 个独特的可能性
Tags
,不幸的是,其中只有一些是指一个组(一些是房间的类型等)。如果您只过滤提到家庭的那些,结果会包含许多家庭房间类型的结果。如果将术语与 一起包括在内,即用值计算家庭,结果会更好,515,000 个结果中有超过 80,000 个包含短语“有小孩的家庭”或“有大孩子的家庭”。这意味着标签列对我们来说并非完全无用,但需要一些工作才能使其有用。
酒店平均分
数据集有一些我无法弄清楚的奇怪或差异,但在此处进行了说明,以便您在构建模型时了解它们。如果你弄明白了,请在讨论区告诉我们!
该数据集具有与平均分数和评论数量相关的以下列:
- 酒店名称
- Additional_Number_of_Scoring
- 平均分
- Total_Number_of_Reviews
- Reviewer_Score
Total_Number_of_Reviews
这家酒店的值,它是 9086。您可能会猜测没有评论的分数要多得多,所以也许我们应该在Additional_Number_of_Scoring
列中添加值。该值为 2682,将其添加到 4789 得到 7,471,但仍比Total_Number_of_Reviews
.如果你拿这些
Average_Score
列,你可能会猜测它是数据集中评论的平均值,但 Kaggle 的描述是“酒店的平均分数,根据去年的最新评论计算得出”。这似乎没什么用,但我们可以根据数据集中的评论分数来计算我们自己的平均值。以同一家酒店为例,酒店平均得分为 7.1,但计算出的得分(数据集中的平均评论者得分)为 6.8。这很接近,但不是相同的值,我们只能猜测Additional_Number_of_Scoring
评论中给出的分数将平均值提高到 7.1。不幸的是,由于无法测试或证明该断言,因此难以使用或信任Average_Score
,Additional_Number_of_Scoring
并且Total_Number_of_Reviews
当它们基于或参考我们没有的数据时。更复杂的是,评论数量第二高的酒店的计算平均得分为 8.12,数据集
Average_Score
为 8.1。这个正确的分数是巧合还是第一家酒店的差异?关于这些酒店可能是异常值的可能性,并且可能大多数值都符合(但有些不是出于某种原因),我们接下来将编写一个简短的程序来探索数据集中的值并确定正确的用法(或不使用)的值。
注意事项练习 - 数据探索 加载数据
使用此数据集时,您将编写从文本计算某些内容的代码,而无需自己阅读或分析文本。这就是 NLP 的精髓,无需人工就能解释意义或情感。但是,您可能会阅读一些负面评论。我劝你不要这样做,因为你不必这样做。其中一些是愚蠢的或无关紧要的负面酒店评论,例如“天气不好”,这是酒店或任何人无法控制的。但有些评论也有阴暗面。有时负面评论是种族主义、性别歧视或年龄歧视。这是不幸的,但在从公共网站上抓取的数据集中是可以预料的。一些评论者留下的评论会让您觉得反感、不舒服或令人不安。最好让代码衡量情绪,而不是自己阅读它们并感到沮丧。也就是说,写这样的东西是少数,但它们都是一样的。
直观地检查数据就足够了,现在您将编写一些代码并获得一些答案!本节使用 pandas 库。您的首要任务是确保您可以加载和读取 CSV 数据。pandas 库有一个快速的 CSV 加载器,并且结果被放置在一个数据框中,就像之前的课程一样。我们正在加载的 CSV 有超过 50 万行,但只有 17 列。Pandas 为您提供了许多与数据框交互的强大方法,包括对每一行执行操作的能力。
从本课开始,将有代码片段和一些代码解释,以及一些关于结果含义的讨论。将包含的notebook.ipynb用于您的代码。
让我们从加载您正在使用的数据文件开始:
# Load the hotel reviews from CSV
import pandas as pd
import time
# importing time so the start and end time can be used to calculate file loading time
print("Loading data file now, this could take a while depending on file size")
start = time.time()
# df is 'DataFrame' - make sure you downloaded the file to the data folder
df = pd.read_csv('../../data/Hotel_Reviews.csv')
end = time.time()
print("Loading took " + str(round(end - start, 2)) + " seconds")
现在数据已经加载完毕,我们可以对其进行一些操作。将此代码保留在程序的顶部以供下一部分使用。
探索数据 在这种情况下,数据已经是干净的,这意味着它已经准备好使用,并且没有其他语言中的字符,这些字符可能会使只需要英文字符的算法出错。
?在应用 NLP 技术之前,您可能必须处理需要一些初始处理才能格式化的数据,但这次不是。如果必须,您将如何处理非英文字符?
花点时间确保加载数据后,您可以使用代码对其进行探索。想要专注于
Negative_Review
和Positive_Review
列是很容易的。它们充满了自然文本,供您的 NLP 算法处理。可是等等!在你进入 NLP 和情绪之前,你应该按照下面的代码来确定数据集中给出的值是否与你用 pandas 计算的值相匹配。数据框操作 本课的第一个任务是通过编写一些检查数据帧的代码(不更改它)来检查以下断言是否正确。
像许多编程任务一样,有几种方法可以完成此任务,但好的建议是以最简单、最简单的方式完成,尤其是当您将来回到这段代码时更容易理解时。使用数据帧,有一个全面的 API,通常可以有效地做你想做的事。将以下问题视为编码任务,并尝试在不查看解决方案的情况下回答它们。
- 打印出刚刚加载的数据框的形状(形状是行数和列数)
- 计算审稿人国籍的频率计数:
- 该列有多少不同的值,
Reviewer_Nationality
它们是什么? - 数据集中最常见的审稿人国籍是什么(印刷国家和审稿数量)?
- 下一个最常见的 10 个国籍是什么,以及他们的频率计数?
- 该列有多少不同的值,
- 在评论最多的 10 个国家/地区中,哪家酒店的评论最多?
- 数据集中每家酒店(酒店的频率计数)有多少条评论?
- 虽然
Average_Score
数据集中的每家酒店都有一列,但您还可以计算平均分数(获取数据集中每家酒店的所有评论者分数的平均值)。Calc_Average_Score
使用包含计算的平均值的列标题向您的数据框添加一个新列。 - 是否有任何酒店具有相同的(四舍五入到小数点后一位)
Average_Score
和Calc_Average_Score
?- 尝试编写一个 Python 函数,将 Series(行)作为参数并比较值,当值不相等时打印出一条消息。然后使用该
.apply()
方法使用该函数处理每一行。
- 尝试编写一个 Python 函数,将 Series(行)作为参数并比较值,当值不相等时打印出一条消息。然后使用该
- 计算并打印出有多少行的列
Negative_Review
值为“No Negative” - 计算并打印出有多少行的列
Positive_Review
值为“No Positive” - 计算并打印出有多少行具有
Positive_Review
“No Positive”的列值和Negative_Review
“No Negative”的值
- 打印出刚刚加载的数据框的形状(形状是行数和列数)
print("The shape of the data (rows, cols) is " + str(df.shape)) > The shape of the data (rows, cols) is (515738, 17)
- 计算审稿人国籍的频率计数:
- 该列有多少不同的值,
Reviewer_Nationality
它们是什么? - 数据集中最常见的审稿人国籍是什么(印刷国家和审稿数量)?
# value_counts() creates a Series object that has index and values in this case, the country and the frequency they occur in reviewer nationality nationality_freq = df["Reviewer_Nationality"].value_counts() print("There are " + str(nationality_freq.size) + " different nationalities") # print first and last rows of the Series. Change to nationality_freq.to_string() to print all of the data print(nationality_freq)
There are 227 different nationalities United Kingdom245246 United States of America35437 Australia21686 Ireland14827 United Arab Emirates10235 ... Comoros1 Palau1 Northern Mariana Islands1 Cape Verde1 Guinea1 Name: Reviewer_Nationality, Length: 227, dtype: int64
- 下一个最常见的 10 个国籍是什么,以及他们的频率计数?
print("The highest frequency reviewer nationality is " + str(nationality_freq.index[0]).strip() + " with " + str(nationality_freq[0]) + " reviews.") # Notice there is a leading space on the values, strip() removes that for printing # What is the top 10 most common nationalities and their frequencies? print("The next 10 highest frequency reviewer nationalities are:") print(nationality_freq[1:11].to_string())
The highest frequency reviewer nationality is United Kingdom with 245246 reviews. The next 10 highest frequency reviewer nationalities are: United States of America35437 Australia21686 Ireland14827 United Arab Emirates10235 Saudi Arabia8951 Netherlands8772 Switzerland8678 Germany7941 Canada7894 France7296
- 该列有多少不同的值,
- 在评论最多的 10 个国家/地区中,哪家酒店的评论最多?
# What was the most frequently reviewed hotel for the top 10 nationalities # Normally with pandas you will avoid an explicit loop, but wanted to show creating a new dataframe using criteria (don't do this with large amounts of data because it could be very slow) for nat in nationality_freq[:10].index: # First, extract all the rows that match the criteria into a new dataframe nat_df = df[df["Reviewer_Nationality"] == nat] # Now get the hotel freq freq = nat_df["Hotel_Name"].value_counts() print("The most reviewed hotel for " + str(nat).strip() + " was " + str(freq.index[0]) + " with " + str(freq[0]) + " reviews.")
The most reviewed hotel for United Kingdom was Britannia International Hotel Canary Wharf with 3833 reviews. The most reviewed hotel for United States of America was Hotel Esther a with 423 reviews. The most reviewed hotel for Australia was Park Plaza Westminster Bridge London with 167 reviews. The most reviewed hotel for Ireland was Copthorne Tara Hotel London Kensington with 239 reviews. The most reviewed hotel for United Arab Emirates was Millennium Hotel London Knightsbridge with 129 reviews. The most reviewed hotel for Saudi Arabia was The Cumberland A Guoman Hotel with 142 reviews. The most reviewed hotel for Netherlands was Jaz Amsterdam with 97 reviews. The most reviewed hotel for Switzerland was Hotel Da Vinci with 97 reviews. The most reviewed hotel for Germany was Hotel Da Vinci with 86 reviews. The most reviewed hotel for Canada was St James Court A Taj Hotel London with 61 reviews.
- 数据集中每家酒店(酒店的频率计数)有多少条评论?
# First create a new dataframe based on the old one, removing the uneeded columns hotel_freq_df = df.drop(["Hotel_Address", "Additional_Number_of_Scoring", "Review_Date", "Average_Score", "Reviewer_Nationality", "Negative_Review", "Review_Total_Negative_Word_Counts", "Positive_Review", "Review_Total_Positive_Word_Counts", "Total_Number_of_Reviews_Reviewer_Has_Given", "Reviewer_Score", "Tags", "days_since_review", "lat", "lng"], axis = 1)# Group the rows by Hotel_Name, count them and put the result in a new column Total_Reviews_Found hotel_freq_df['Total_Reviews_Found'] = hotel_freq_df.groupby('Hotel_Name').transform('count')# Get rid of all the duplicated rows hotel_freq_df = hotel_freq_df.drop_duplicates(subset = ["Hotel_Name"]) display(hotel_freq_df)
酒店名称 Total_Number_of_Reviews Total_Reviews_Found 金丝雀码头大不列颠国际酒店 9086 4789 公园广场威斯敏斯特桥伦敦 12158 4169 伦敦肯辛顿国敦塔拉酒店 7105 3578 ... ... ... 巴黎奥尔良门美居酒店 110 10 瓦格纳酒店 135 10 加利津伯格酒店 173 8 Total_Number_of_Reviews
。目前尚不清楚数据集中的这个值是否代表酒店的评论总数,但并非所有评论都被抓取,或者其他一些计算。Total_Number_of_Reviews
由于这种不明确性,未在模型中使用。
- 虽然
Average_Score
数据集中的每家酒店都有一列,但您还可以计算平均分数(获取数据集中每家酒店的所有评论者分数的平均值)。Calc_Average_Score
使用包含计算的平均值的列标题向您的数据框添加一个新列。打印出Hotel_Name
、Average_Score
和列Calc_Average_Score
。
# define a function that takes a row and performs some calculation with it def get_difference_review_avg(row): return row["Average_Score"] - row["Calc_Average_Score"]# 'mean' is mathematical word for 'average' df['Calc_Average_Score'] = round(df.groupby('Hotel_Name').Reviewer_Score.transform('mean'), 1)# Add a new column with the difference between the two average scores df["Average_Score_Difference"] = df.apply(get_difference_review_avg, axis = 1)# Create a df without all the duplicates of Hotel_Name (so only 1 row per hotel) review_scores_df = df.drop_duplicates(subset = ["Hotel_Name"])# Sort the dataframe to find the lowest and highest average score difference review_scores_df = review_scores_df.sort_values(by=["Average_Score_Difference"])display(review_scores_df[["Average_Score_Difference", "Average_Score", "Calc_Average_Score", "Hotel_Name"]])
您可能还想知道该Average_Score
值以及为什么它有时与计算的平均分数不同。由于我们不知道为什么某些值匹配,但其他值存在差异,因此在这种情况下使用我们必须自己计算平均值的评论分数是最安全的。也就是说,差异通常很小,以下是与数据集平均值和计算平均值偏差最大的酒店:
Average_Score_Difference 平均分 Calc_Average_Score 酒店名称 -0.8 7.7 8.5 阿斯托利亚贝斯特韦斯特酒店 -0.7 8.8 9.5 斯汤达广场 Vend me 巴黎美憬阁酒店 -0.7 7.5 8.2 巴黎奥尔良门美居酒店 -0.7 7.9 8.6 巴黎旺多姆万丽酒店 -0.5 7.0 7.5 皇家伊利斯酒店是 ... ... ... ... 0.7 7.5 6.8 Mercure Paris Op ra Faubourg Montmartre 0.8 7.1 6.3 巴黎蒙帕纳斯巴斯德假日酒店 0.9 6.8 5.9 尤金妮别墅 0.9 8.6 7.7 MARQUIS Faubourg St Honor Relais Chateaux 1.3 7.2 5.9 库贝酒店冰吧
- 计算并打印出有多少行的列
Negative_Review
值为“No Negative”
- 计算并打印出有多少行的列
Positive_Review
值为“No Positive”
- 计算并打印出有多少行具有
Positive_Review
“No Positive”的列值和Negative_Review
“No Negative”的值
# with lambdas: start = time.time() no_negative_reviews = df.apply(lambda x: True if x['Negative_Review'] == "No Negative" else False , axis=1) print("Number of No Negative reviews: " + str(len(no_negative_reviews[no_negative_reviews == True].index)))no_positive_reviews = df.apply(lambda x: True if x['Positive_Review'] == "No Positive" else False , axis=1) print("Number of No Positive reviews: " + str(len(no_positive_reviews[no_positive_reviews == True].index)))both_no_reviews = df.apply(lambda x: True if x['Negative_Review'] == "No Negative" and x['Positive_Review'] == "No Positive" else False , axis=1) print("Number of both No Negative and No Positive reviews: " + str(len(both_no_reviews[both_no_reviews == True].index))) end = time.time() print("Lambdas took " + str(round(end - start, 2)) + " seconds")
Number of No Negative reviews: 127890 Number of No Positive reviews: 35946 Number of both No Negative and No Positive reviews: 127 Lambdas took 9.64 seconds
# without lambdas (using a mixture of notations to show you can use both)
start = time.time()
no_negative_reviews = sum(df.Negative_Review == "No Negative")
print("Number of No Negative reviews: " + str(no_negative_reviews))no_positive_reviews = sum(df["Positive_Review"] == "No Positive")
print("Number of No Positive reviews: " + str(no_positive_reviews))both_no_reviews = sum((df.Negative_Review == "No Negative") & (df.Positive_Review == "No Positive"))
print("Number of both No Negative and No Positive reviews: " + str(both_no_reviews))end = time.time()
print("Sum took " + str(round(end - start, 2)) + " seconds")
Number of No Negative reviews: 127890 Number of No Positive reviews: 35946 Number of both No Negative and No Positive reviews: 127 Sum took 0.19 seconds
您可能已经注意到,有 127 行的列
Negative_Review
和Positive_Review
分别具有“No Negative”和“No Positive”值。这意味着评论者给了酒店一个数字分数,但拒绝写正面或负面评论。幸运的是,这是少量的行(515738 中的 127 行,或 0.02%),因此它可能不会使我们的模型或结果偏向任何特定方向,但您可能没想到评论数据集包含没有评论,因此值得探索数据以发现这样的行。【机器学习(ML)|带有酒店评论的情绪分析 - 处理数据】现在您已经探索了数据集,在下一课中,您将过滤数据并添加一些情绪分析。
推荐阅读
- KBQA|使用句法依存分析实现KBQA中的约束挂载
- 【Python常用包】itertools
- python|python可视化模块pandas,python数据可视化软件
- 2022高教社杯全国大学生数学建模竞赛C题详细解析
- 数学建模|2022年高教社杯全国大学生数学建模国赛B题思路详解
- python|2022高教社杯数学建模国赛C题思路代码实现
- 无人机|2022数学建模国赛B题无人机遂行编队飞行中的纯方位无源定位思路分析思路+参考论文+代码
- Python第二天( 变量详解及变量赋值)
- Python#常用的模块和简单用法