自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

租房有深坑?手把手教你如何用R速讀評(píng)論+科學(xué)選房

大數(shù)據(jù) 數(shù)據(jù)分析
要想租到稱心如意的房子,不僅要眼明手快,還得看清各類“前輩”的評(píng)價(jià)避開大坑。一位程序員在出行選酒店的時(shí)候就借用了程序工具:先用python爬下了海外點(diǎn)評(píng)網(wǎng)站TripAdvisor的數(shù)千評(píng)論,并且用R進(jìn)行了文本分析和情感分析,科學(xué)選房,高效便捷,極具參考價(jià)值。

[[242297]]

大數(shù)據(jù)文摘出品

編譯:Hope、臻臻、CoolBoy

最近,租房這事兒成了北漂族的一大bug,要想租到稱心如意的房子,不僅要眼明手快,還得看清各類“前輩”的評(píng)價(jià)避開大坑。一位程序員在出行選酒店的時(shí)候就借用了程序工具:先用python爬下了海外點(diǎn)評(píng)網(wǎng)站TripAdvisor的數(shù)千評(píng)論,并且用R進(jìn)行了文本分析和情感分析,科學(xué)選房,高效便捷,極具參考價(jià)值。

以下,這份超詳實(shí)的教程拿好不謝。

TripAdvisor提供的信息對(duì)于旅行者的出行決策非常重要。但是,要去了解TripAdvisor的泡沫評(píng)分和數(shù)千個(gè)評(píng)論文本之間的細(xì)微差別是極具挑戰(zhàn)性的。

為了更加全面地了解酒店旅客的評(píng)論是否會(huì)對(duì)之后酒店的服務(wù)產(chǎn)生影響,我爬取了TripAdvisor中一個(gè)名為Hilton Hawaiian Village酒店的所有英文評(píng)論。這里我不會(huì)對(duì)爬蟲的細(xì)節(jié)進(jìn)行展開。

Python源碼:

https://github.com/susanli2016/NLP-with-Python/blob/master/Web%20scraping%20Hilton%20Hawaiian%20Village%20TripAdvisor%20Reviews.py

加載擴(kuò)展包

  • library(dplyr)
  • library(readr)
  • library(lubridate)
  • library(ggplot2)
  • library(tidytext)
  • library(tidyverse)
  • library(stringr)
  • library(tidyr)
  • library(scales)
  • library(broom)
  • library(purrr)
  • library(widyr)
  • library(igraph)
  • library(ggraph)
  • library(SnowballC)
  • library(wordcloud)
  • library(reshape2)
  • theme_set(theme_minimal())

數(shù)據(jù)集

  1. df <- read_csv("Hilton_Hawaiian_Village_Waikiki_Beach_Resort-Honolulu_Oahu_Hawaii__en.csv") 
  2. df <- df[complete.cases(df), ] 
  3. df$review_date <- as.Date(df$review_date, format = "%d-%B-%y"
  4.  
  5. dim(df); min(df$review_date); max(df$review_date) 

Figure 2

我們?cè)赥ripAdvisor上一共獲得了13,701條關(guān)于Hilton Hawaiian Village酒店的英文評(píng)論,這些評(píng)論的時(shí)間范圍是從2002–03–21 到2018–08–02。

  1. df %>
  2.   count(Week = round_date(review_date, "week")) %>
  3.   ggplot(aes(Week, n)) + 
  4.   geom_line() +  
  5.   ggtitle('The Number of Reviews Per Week') 

Figure 2

在2014年末,周評(píng)論數(shù)量達(dá)到最高峰。那一個(gè)星期里酒店被評(píng)論了70次。

對(duì)評(píng)論文本進(jìn)行文本挖掘

  1. df <- tibble::rowid_to_column(df, "ID") 
  2. df <- df %>
  3.   mutate(review_date = as.POSIXct(review_date, origin = "1970-01-01"),month = round_date(review_date, "month")) 
  4.  
  5. review_words <- df %>
  6.   distinct(review_body, .keep_all = TRUE) %>
  7.   unnest_tokens(word, review_body, drop = FALSE) %>
  8.   distinct(ID, word, .keep_all = TRUE) %>
  9.   anti_join(stop_words, by = "word") %>
  10.   filter(str_detect(word, "[^\\d]")) %>
  11.   group_by(word) %>
  12.   mutate(word_total = n()) %>
  13.   ungroup() 
  14.  
  15. word_counts <- review_words %>
  16.   count(word, sort = TRUE
  17.  
  18. word_counts %>
  19.   head(25) %>
  20.   mutate(word = reorder(word, n)) %>
  21.   ggplot(aes(word, n)) + 
  22.   geom_col(fill = "lightblue") + 
  23.   scale_y_continuous(labels = comma_format()) + 
  24.   coord_flip() + 
  25.   labs(title = "Most common words in review text 2002 to date"
  26.        subtitle = "Among 13,701 reviews; stop words removed"
  27.        y = "# of uses"

Figure 3

我們還可以更進(jìn)一步的把“stay”和“stayed”,“pool”和“pools”這些意思相近的詞合并起來。這個(gè)步驟被稱為詞干提取,也就是將變形(或是衍生)詞語縮減為詞干,基詞或根詞的過程。

  1. word_counts %>
  2.   head(25) %>
  3.   mutate(word = wordStem(word)) %>%  
  4.   mutate(word = reorder(word, n)) %>
  5.   ggplot(aes(word, n)) + 
  6.   geom_col(fill = "lightblue") + 
  7.   scale_y_continuous(labels = comma_format()) + 
  8.   coord_flip() + 
  9.   labs(title = "Most common words in review text 2002 to date"
  10.        subtitle = "Among 13,701 reviews; stop words removed and stemmed"
  11.        y = "# of uses"

Figure 4

二元詞組

通常我們希望了解評(píng)論中單詞的相互關(guān)系。哪些詞組在評(píng)論文本中比較常用呢?如果給出一列單詞,那么后面會(huì)隨之出現(xiàn)什么單詞呢?哪些詞之間的關(guān)聯(lián)性最強(qiáng)?許多有意思的文本挖掘都是基于這些關(guān)系的。在研究?jī)蓚€(gè)連續(xù)單詞的時(shí)候,我們稱這些單詞對(duì)為“二元詞組”。

所以,在Hilton Hawaiian Village的評(píng)論中,哪些是最常見的二元詞組呢?

  1. review_bigrams <- df %>
  2.   unnest_tokens(bigram, review_body, token = "ngrams"n = 2
  3.  
  4. bigrams_separated <- review_bigrams %>
  5.   separate(bigram, c("word1", "word2"), sep = " "
  6.  
  7. bigrams_filtered <- bigrams_separated %>
  8.   filter(!word1 %in% stop_words$word) %>
  9.   filter(!word2 %in% stop_words$word) 
  10.  
  11. bigram_counts <- bigrams_filtered %>%  
  12.   count(word1, word2, sort = TRUE
  13.  
  14. bigrams_united <- bigrams_filtered %>
  15.   unite(bigram, word1, word2, sep = " "
  16.  
  17. bigrams_united %>
  18.   count(bigram, sort = TRUE

Figure 5

最常見的二元詞組是“rainbow tower”(彩虹塔),其次是“hawaiian village”(夏威夷村)。

我們可以利用網(wǎng)絡(luò)可視化來展示這些二元詞組:

  1. review_subject <- df %>%  
  2.   unnest_tokens(word, review_body) %>%  
  3.   anti_join(stop_words) 
  4.  
  5. my_stopwords <- data_frame(word = c(as.character(1:10))) 
  6. review_subject <- review_subject %>%  
  7.   anti_join(my_stopwords) 
  8.  
  9. title_word_pairs <- review_subject %>%  
  10.   pairwise_count(word, ID, sort = TRUEupper = FALSE
  11.  
  12. set.seed(1234) 
  13. title_word_pairs %>
  14.   filter(n >= 1000) %>
  15.   graph_from_data_frame() %>
  16.   ggraph(layout = "fr") + 
  17.   geom_edge_link(aes(edge_alpha = nedge_width = n), edge_colour = "cyan4") + 
  18.   geom_node_point(size = 5) + 
  19.   geom_node_text(aes(label = name), repel = TRUE,  
  20.                  point.padding = unit(0.2, "lines")) + 
  21.   ggtitle('Word network in TripAdvisor reviews') 
  22.   theme_void() 

Figure 6

上圖展示了TripAdvisor評(píng)論中較為常見的二元詞組。這些詞至少出現(xiàn)了1000次,而且其中不包含停用詞。

在網(wǎng)絡(luò)圖中我們發(fā)現(xiàn)出現(xiàn)頻率最高的幾個(gè)詞存在很強(qiáng)的相關(guān)性(“hawaiian”, “village”, “ocean” 和“view”),不過我們沒有發(fā)現(xiàn)明顯的聚集現(xiàn)象。

三元詞組

二元詞組有時(shí)候還不足以說明情況,讓我們來看看TripAdvisor中關(guān)于Hilton Hawaiian Village酒店最常見的三元詞組有哪些。

  1. review_trigrams <- df %>
  2.   unnest_tokens(trigram, review_body, token = "ngrams"n = 3
  3.  
  4. trigrams_separated <- review_trigrams %>
  5.   separate(trigram, c("word1", "word2", "word3"), sep = " "
  6.  
  7. trigrams_filtered <- trigrams_separated %>
  8.   filter(!word1 %in% stop_words$word) %>
  9.   filter(!word2 %in% stop_words$word) %>
  10.   filter(!word3 %in% stop_words$word) 
  11.  
  12. trigram_counts <- trigrams_filtered %>%  
  13.   count(word1, word2, word3, sort = TRUE
  14.  
  15. trigrams_united <- trigrams_filtered %>
  16.   unite(trigram, word1, word2, word3, sep = " "
  17.  
  18. trigrams_united %>
  19.   count(trigram, sort = TRUE

Figure 7

最常見的三元詞組是“hilton hawaiian village”,其次是“diamond head tower”,等等。

評(píng)論中關(guān)鍵單詞的趨勢(shì)

隨著時(shí)間的推移,哪些單詞或話題變得更加常見,或者更加罕見了呢?從這些信息我們可以探知酒店做出的調(diào)整,比如在服務(wù)上,翻新上,解決問題上。我們還可以預(yù)測(cè)哪些主題會(huì)更多地被提及。

我們想要解決類似這樣的問題:隨著時(shí)間的推移,在TripAdvisor的評(píng)論區(qū)中哪些詞出現(xiàn)的頻率越來越高了?

  1. reviews_per_month <- df %>
  2.   group_by(month) %>
  3.   summarize(month_total = n()) 
  4.  
  5. word_month_counts <- review_words %>
  6.   filter(word_total >= 1000) %>
  7.   count(word, month) %>
  8.   complete(word, month, fill = list(n = 0)) %>
  9.   inner_join(reviews_per_month, by = "month") %>
  10.   mutate(percent = n / month_total) %>
  11.   mutate(yearyear = year(month) + yday(month) / 365) 
  12.  
  13. mod <- ~ glm(cbind(n, month_total - n) ~ year, ., family = "binomial"
  14.  
  15. slopes <- word_month_counts %>
  16.   nest(-word) %>
  17.   mutate(model = map(data, mod)) %>
  18.   unnest(map(model, tidy)) %>
  19.   filter(term == "year") %>
  20.   arrange(desc(estimate)) 
  21.  
  22. slopes %>
  23.   head(9) %>
  24.   inner_join(word_month_counts, by = "word") %>
  25.   mutate(word = reorder(word, -estimate)) %>
  26.   ggplot(aes(month, n / month_total, color = word)) + 
  27.   geom_line(show.legend = FALSE) + 
  28.   scale_y_continuous(labels = percent_format()) + 
  29.   facet_wrap(~ word, scales = "free_y") + 
  30.   expand_limits(y = 0) + 
  31.   labs(x = "Year"
  32.        y = "Percentage of reviews containing this word"
  33.        title = "9 fastest growing words in TripAdvisor reviews"
  34.        subtitle = "Judged by growth rate over 15 years"

Figure 8

在2010年以前我們可以看到大家討論的焦點(diǎn)是“friday fireworks”(周五的煙花)和“lagoon”(環(huán)礁湖)。而在2005年以前“resort fee”(度假費(fèi))和“busy”(繁忙)這些詞的詞頻增長最快。

評(píng)論區(qū)中哪些詞的詞頻在下降呢?

  1. slopes %>
  2.   tail(9) %>
  3.   inner_join(word_month_counts, by = "word") %>
  4.   mutate(word = reorder(word, estimate)) %>
  5.   ggplot(aes(month, n / month_total, color = word)) + 
  6.   geom_line(show.legend = FALSE) + 
  7.   scale_y_continuous(labels = percent_format()) + 
  8.   facet_wrap(~ word, scales = "free_y") + 
  9.   expand_limits(y = 0) + 
  10.   labs(x = "Year"
  11.        y = "Percentage of reviews containing this term"
  12.        title = "9 fastest shrinking words in TripAdvisor reviews"
  13.        subtitle = "Judged by growth rate over 4 years"

Figure 9

這張圖展示了自2010年以來逐漸變少的主題。這些詞包括“hhv” (我認(rèn)為這是 hilton hawaiian village的簡(jiǎn)稱), “breakfast”(早餐), “upgraded”(升級(jí)), “prices”(價(jià)格) and “free”(免費(fèi))。

讓我們對(duì)一些單詞進(jìn)行比較。

  1. word_month_counts %>
  2.   filter(word %in% c("service", "food")) %>
  3.   ggplot(aes(month, n / month_total, color = word)) + 
  4.   geom_line(size = 1alpha = .8) + 
  5.   scale_y_continuous(labels = percent_format()) + 
  6.   expand_limits(y = 0) + 
  7.   labs(x = "Year"
  8.        y = "Percentage of reviews containing this term"title = "service vs food in terms of reviewers interest"

Figure 10

在2010年之前,服務(wù)(service)和食物(food)都是熱點(diǎn)主題。關(guān)于服務(wù)和食物的討論在2003年到達(dá)頂峰,自2005年之后就一直在下降,只是偶爾會(huì)反彈。

情感分析

情感分析被廣泛應(yīng)用于對(duì)評(píng)論、調(diào)查、網(wǎng)絡(luò)和社交媒體文本的分析,以反映客戶的感受,涉及范圍包括市場(chǎng)營銷、客戶服務(wù)和臨床醫(yī)學(xué)等。

在本案例中,我們的目標(biāo)是對(duì)評(píng)論者(也就是酒店旅客)在住店之后對(duì)酒店的態(tài)度進(jìn)行分析。這個(gè)態(tài)度可能是一個(gè)判斷或是評(píng)價(jià)。

下面來看評(píng)論中出現(xiàn)得最頻繁的積極詞匯和消極詞匯。

  1. reviews <- df %>%  
  2.   filter(!is.na(review_body)) %>%  
  3.   select(ID, review_body) %>%  
  4.   group_by(row_number()) %>%  
  5.   ungroup() 
  6. tidy_reviews <- reviews %>
  7.   unnest_tokens(word, review_body) 
  8. tidy_reviews <- tidy_reviews %>
  9.   anti_join(stop_words) 
  10.  
  11. bing_word_counts <- tidy_reviews %>
  12.   inner_join(get_sentiments("bing")) %>
  13.   count(word, sentiment, sort = TRUE) %>
  14.   ungroup() 
  15.  
  16. bing_word_counts %>
  17.   group_by(sentiment) %>
  18.   top_n(10) %>
  19.   ungroup() %>
  20.   mutate(word = reorder(word, n)) %>
  21.   ggplot(aes(word, n, fill = sentiment)) + 
  22.   geom_col(show.legend = FALSE) + 
  23.   facet_wrap(~sentiment, scales = "free") + 
  24.   labs(y = "Contribution to sentiment"x = NULL) + 
  25.   coord_flip() +  
  26.   ggtitle('Words that contribute to positive and negative sentiment in the reviews') 

Figure 11

讓我們換一個(gè)情感文本庫,看看結(jié)果是否一樣。

  1. contributions <- tidy_reviews %>
  2.   inner_join(get_sentiments("afinn"), by = "word") %>
  3.   group_by(word) %>
  4.   summarize(occurences = n(), 
  5.             contribution = sum(score)) 
  6. contributions %>
  7.   top_n(25, abs(contribution)) %>
  8.   mutate(word = reorder(word, contribution)) %>
  9.   ggplot(aes(word, contribution, fill = contribution > 0)) + 
  10.   ggtitle('Words with the greatest contributions to positive/negative  
  11.           sentiment in reviews') + 
  12.   geom_col(show.legend = FALSE) + 
  13.   coord_flip() 

Figure 12

有意思的是,“diamond”(出自“diamond head-鉆石頭”)被歸類為積極情緒。

這里其實(shí)有一個(gè)潛在問題,比如“clean”(干凈)是什么詞性取決于語境。如果前面有個(gè)“not”(不),這就是一個(gè)消極情感了。事實(shí)上一元詞在否定詞(如not)存在的時(shí)候經(jīng)常碰到這種問題,這就引出了我們下一個(gè)話題:

在情感分析中使用二元詞組來辨明語境

我們想知道哪些詞經(jīng)常前面跟著“not”(不)

  1. bigrams_separated %>
  2.   filter(word1 == "not") %>
  3.   count(word1, word2, sort = TRUE

Figure 13

“a”前面跟著“not”的情況出現(xiàn)了850次,而“the”前面跟著“not”出現(xiàn)了698次。不過,這種結(jié)果不是特別有實(shí)際意義。

  1. AFINN <- get_sentiments("afinn") 
  2. not_words <- bigrams_separated %>
  3.   filter(word1 == "not") %>
  4.   inner_join(AFINN, by = c(word2 = "word")) %>
  5.   count(word2, score, sort = TRUE) %>
  6.   ungroup() 
  7.  
  8. not_words 

Figure 14

上面的分析告訴我們,在“not”后面最常見的情感詞匯是“worth”,其次是“recommend”,這些詞都被認(rèn)為是積極詞匯,而且積極程度得分為2。

所以在我們的數(shù)據(jù)中,哪些單詞最容易被誤解為相反的情感?

  1. not_words %>
  2.   mutate(contribution = n * score) %>
  3.   arrange(desc(abs(contribution))) %>
  4.   head(20) %>
  5.   mutate(word2 = reorder(word2, contribution)) %>
  6.   ggplot(aes(word2, n * score, fill = n * score > 0)) + 
  7.   geom_col(show.legend = FALSE) + 
  8.   xlab("Words preceded by \"not\"") + 
  9.   ylab("Sentiment score * number of occurrences") + 
  10.   ggtitle('The 20 words preceded by "not" that had the greatest contribution to  
  11.           sentiment scores, positive or negative direction') + 
  12.   coord_flip() 

Figure 15

二元詞組“not worth”, “not great”, “not good”, “not recommend”和“not like”是導(dǎo)致錯(cuò)誤判斷的最大根源,使得評(píng)論看起來比原來積極的多。

除了“not”以外,還有其他的否定詞會(huì)對(duì)后面的內(nèi)容進(jìn)行情緒的扭轉(zhuǎn),比如“no”, “never” 和“without”。讓我們來看一下具體情況。

  1. negation_words <- c("not", "no", "never", "without") 
  2.  
  3. negated_words <- bigrams_separated %>
  4.   filter(word1 %in% negation_words) %>
  5.   inner_join(AFINN, by = c(word2 = "word")) %>
  6.   count(word1, word2, score, sort = TRUE) %>
  7.   ungroup() 
  8.  
  9. negated_words %>
  10.   mutate(contribution = n * score, 
  11.          word2 = reorder(paste(word2, word1, sep = "__"), contribution)) %>
  12.   group_by(word1) %>
  13.   top_n(12, abs(contribution)) %>
  14.   ggplot(aes(word2, contribution, fill = n * score > 0)) + 
  15.   geom_col(show.legend = FALSE) + 
  16.   facet_wrap(~ word1, scales = "free") + 
  17.   scale_x_discrete(labels = function(x) gsub("__.+$", "", x)) + 
  18.   xlab("Words preceded by negation term") + 
  19.   ylab("Sentiment score * # of occurrences") + 
  20.   ggtitle('The most common positive or negative words to follow negations  
  21.           such as "no", "not", "never" and "without"') + 
  22.   coord_flip() 

Figure 16

看來導(dǎo)致錯(cuò)判為積極詞匯的最大根源來自于“not worth/great/good/recommend”,而另一方面錯(cuò)判為消極詞匯的最大根源是“not bad” 和“no problem”。

最后,讓我們來觀察一下最積極和最消極的評(píng)論。

  1. sentiment_messages <- tidy_reviews %>
  2.   inner_join(get_sentiments("afinn"), by = "word") %>
  3.   group_by(ID) %>
  4.   summarize(sentiment = mean(score), 
  5.             words = n()) %>
  6.   ungroup() %>
  7.   filter(words >= 5) 
  8.  
  9. sentiment_messages %>
  10.   arrange(desc(sentiment)) 

Figure 17

最積極的評(píng)論來自于ID為2363的記錄:“哇哇哇,這地方太好了!從房間我們可以看到很漂亮的景色,我們住得很開心。Hilton酒店就是很棒!無論是小孩還是大人,這家酒店有著所有你想要的東西。”

  1. df[ which(df$ID==2363), ]$review_body[1] 

Figure 18

  1. sentiment_messages %>
  2.   arrange(sentiment) 

Figure 19

最消極的評(píng)論來自于ID為3748的記錄:“(我)住了5晚(16年5月12日-5月17日)。第一晚,我們發(fā)現(xiàn)地磚壞了,小孩子在玩手指。第二晚,我們看到小蟑螂在兒童食物上爬。前臺(tái)給我們換了房間,但他們讓我們一小時(shí)之內(nèi)搬好房間,否則就不能換房。。。已經(jīng)晚上11點(diǎn),我們都很累了,孩子們也睡了。我們拒絕了這個(gè)建議。退房的時(shí)候,前臺(tái)小姐跟我講,蟑螂在他們的旅館里很常見。她還反問我在加州見不到蟑螂嗎?我沒想到能在Hilton遇到這樣的事情。”

  1. df[ which(df$ID==3748), ]$review_body[1] 

Figure 20

Github源碼:

https://github.com/susanli2016/Data-Analysis-with-R/blob/master/Text%20Mining%20Hilton%20Hawaiian%20Village%20TripAdvisor%20Reviews.Rmd

相關(guān)報(bào)道:

https://towardsdatascience.com/scraping-tripadvisor-text-mining-and-sentiment-analysis-for-hotel-reviews-cc4e20aef333

【本文是51CTO專欄機(jī)構(gòu)大數(shù)據(jù)文摘的原創(chuàng)文章,微信公眾號(hào)“大數(shù)據(jù)文摘( id: BigDataDigest)”】

     大數(shù)據(jù)文摘二維碼

戳這里,看該作者更多好文

責(zé)任編輯:趙寧寧 來源: 51CTO專欄
相關(guān)推薦

2009-03-18 11:36:21

代理服務(wù)器下載MyEclipse7.

2014-11-17 11:13:17

易維

2022-12-07 08:42:35

2022-07-27 08:16:22

搜索引擎Lucene

2021-07-14 09:00:00

JavaFX開發(fā)應(yīng)用

2011-01-10 14:41:26

2011-05-03 15:59:00

黑盒打印機(jī)

2025-04-08 08:28:13

RetrofitKtor網(wǎng)絡(luò)庫

2023-10-28 08:51:35

Java多線程服務(wù)

2021-11-09 06:55:03

水印圖像開發(fā)

2010-07-06 09:38:51

搭建私有云

2010-07-06 09:43:57

搭建私有云

2022-06-06 08:50:40

CIOIT轉(zhuǎn)型

2014-08-08 13:22:54

測(cè)試手機(jī)站點(diǎn)移動(dòng)設(shè)備

2021-01-19 09:06:21

MysqlDjango數(shù)據(jù)庫

2022-01-08 20:04:20

攔截系統(tǒng)調(diào)用

2023-04-26 12:46:43

DockerSpringKubernetes

2022-03-14 14:47:21

HarmonyOS操作系統(tǒng)鴻蒙

2021-02-26 11:54:38

MyBatis 插件接口

2011-02-22 13:46:27

微軟SQL.NET
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)