www.machinelearningmastery.ru

Машинное обучение, нейронные сети, искусственный интеллект
Header decor

Home

Web Scraping TripAdvisor, анализ текста и анализ настроений для отзывов об отелях

Дата публикации Aug 6, 2018

Исследование за исследованием показало, чтоTripAdvisorстановится ужасающе важным в процессе принятия решений путешественником. Тем не менее, может быть непросто понять нюанс балльной оценки TripAdvisor по сравнению с тысячами текстов обзоров TripAdvisor. Чтобы лучше понять, влияют ли отзывы гостей на производительность отеля сверхурочно, я просмотрел все отзывы на английском языке от TripAdvisor для одного отеля -Hilton Hawaiian Village, Я не буду обсуждать детали веб-очистки, код Python для процесса можно найтиВот, Спасибо за советы отFuras,

Загрузите библиотеки

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())

Данные

df <- read_csv("Hilton_Hawaiian_Village_Waikiki_Beach_Resort-Honolulu_Oahu_Hawaii__en.csv")
df <- df[complete.cases(df), ]
df$review_date <- as.Date(df$review_date, format = "%d-%B-%y")dim(df); min(df$review_date); max(df$review_date)
Рисунок 1

На TripAdvisor для Hilton Hawaiian Village было 13,701 отзывов на английском языке, и диапазон дат обзора с 2002–03–21 до 2018–08–02.

df %>%
count(Week = round_date(review_date, "week")) %>%
ggplot(aes(Week, n)) +
geom_line() +
ggtitle('The Number of Reviews Per Week')
фигура 2

Наибольшее количество еженедельных отзывов было получено в конце 2014 года. За эту неделю отель получил более 70 отзывов.

Текстовое копирование текста обзора

df <- tibble::rowid_to_column(df, "ID")
df <- df %>%
mutate(review_date = as.POSIXct(review_date, origin = "1970-01-01"),month = round_date(review_date, "month"))review_words <- df %>%
distinct(review_body, .keep_all = TRUE) %>%
unnest_tokens(word, review_body, drop = FALSE) %>%
distinct(ID, word, .keep_all = TRUE) %>%
anti_join(stop_words, by = "word") %>%
filter(str_detect(word, "[^\\d]")) %>%
group_by(word) %>%
mutate(word_total = n()) %>%
ungroup()word_counts <- review_words %>%
count(word, sort = TRUE)word_counts %>%
head(25) %>%
mutate(word = reorder(word, n)) %>%
ggplot(aes(word, n)) +
geom_col(fill = "lightblue") +
scale_y_continuous(labels = comma_format()) +
coord_flip() +
labs(title = "Most common words in review text 2002 to date",
subtitle = "Among 13,701 reviews; stop words removed",
y = "# of uses")
Рисунок 3

Мы определенно можем сделать немного лучшую работу, чтобы объединить «остаться» и «остался», а также «бассейн» и «бассейны». Так называемое основание, основание - это процесс приведения слов с перегибом (или иногда производных) к их основанию слова, основанию или корневому формату.

word_counts %>%
head(25) %>%
mutate(word = wordStem(word)) %>%
mutate(word = reorder(word, n)) %>%
ggplot(aes(word, n)) +
geom_col(fill = "lightblue") +
scale_y_continuous(labels = comma_format()) +
coord_flip() +
labs(title = "Most common words in review text 2002 to date",
subtitle = "Among 13,701 reviews; stop words removed and stemmed",
y = "# of uses")
Рисунок 4

биграмм

Мы часто хотим понять связь между словами в обзоре. Какие последовательности слов встречаются в тексте обзора? Из какой последовательности слов какое слово наиболее вероятно последует? Какие слова имеют самые сильные отношения друг с другом? Поэтому многие интересные текстовые анализ основаны на отношениях. Когда мы проверяем пары из двух последовательных слов, это называется «биграммы».

Итак, какие отзывы TripAdvisor от Hilton Hawaiian Village наиболее распространены в биграммах?

review_bigrams <- df %>%
unnest_tokens(bigram, review_body, token = "ngrams", n = 2)bigrams_separated <- review_bigrams %>%
separate(bigram, c("word1", "word2"), sep = " ")bigrams_filtered <- bigrams_separated %>%
filter(!word1 %in% stop_words$word) %>%
filter(!word2 %in% stop_words$word)bigram_counts <- bigrams_filtered %>%
count(word1, word2, sort = TRUE)bigrams_united <- bigrams_filtered %>%
unite(bigram, word1, word2, sep = " ")bigrams_united %>%
count(bigram, sort = TRUE)
Рисунок 5

Самым распространенным биграммом является «Радужная башня», за которой следует «Гавайская деревня».

Мы можем визуализировать биграммы в текстовых сетях:

review_subject <- df %>% 
unnest_tokens(word, review_body) %>%
anti_join(stop_words)my_stopwords <- data_frame(word = c(as.character(1:10)))
review_subject <- review_subject %>%
anti_join(my_stopwords)title_word_pairs <- review_subject %>%
pairwise_count(word, ID, sort = TRUE, upper = FALSE)set.seed(1234)
title_word_pairs %>%
filter(n >= 1000) %>%
graph_from_data_frame() %>%
ggraph(layout = "fr") +
geom_edge_link(aes(edge_alpha = n, edge_width = n), edge_colour = "cyan4") +
geom_node_point(size = 5) +
geom_node_text(aes(label = name), repel = TRUE,
point.padding = unit(0.2, "lines")) +
ggtitle('Word network in TripAdvisor reviews')
theme_void()
Рисунок 6

Приведенное выше визуализирует общие биграммы в обзорах TripAdvisor, показывая те, которые встречались не менее 1000 раз и где ни одно слово не было стоп-словом.

Сетевой график показывает сильные связи между несколькими верхними словами («гавайский», «деревня», «океан» и «вид»). Однако мы не видим четкой структуры кластеризации в сети.

Триграммы

Биграмм иногда недостаточно, давайте посмотрим, какие триграммы наиболее распространены в обзорах TripAdvisor от Hilton Hawaiian Village?

review_trigrams <- df %>%
unnest_tokens(trigram, review_body, token = "ngrams", n = 3)

trigrams_separated <- review_trigrams %>%
separate(trigram, c("word1", "word2", "word3"), sep = " ")

trigrams_filtered <- trigrams_separated %>%
filter(!word1 %in% stop_words$word) %>%
filter(!word2 %in% stop_words$word) %>%
filter(!word3 %in% stop_words$word)

trigram_counts <- trigrams_filtered %>%
count(word1, word2, word3, sort = TRUE)

trigrams_united <- trigrams_filtered %>%
unite(trigram, word1, word2, word3, sep = " ")

trigrams_united %>%
count(trigram, sort = TRUE)
Рисунок 7

Самая распространенная триграмма - это «хилтон гавайская деревня», за которой следует «башня с бриллиантовой головой» и так далее.

Тенденции важных слов в обзорах

Какие слова и темы стали более частыми или менее частыми с течением времени? Это может дать нам представление об изменяющейся экосистеме отеля, такой как сервис, ремонт, решение проблем, и позволит нам предсказать, какие темы будут продолжать расти в актуальности.

Мы хотим задать такие вопросы, как: какие слова увеличивались по частоте в обзорах TripAdvisor?

reviews_per_month <- df %>%
group_by(month) %>%
summarize(month_total = n())word_month_counts <- review_words %>%
filter(word_total >= 1000) %>%
count(word, month) %>%
complete(word, month, fill = list(n = 0)) %>%
inner_join(reviews_per_month, by = "month") %>%
mutate(percent = n / month_total) %>%
mutate(year = year(month) + yday(month) / 365)mod <- ~ glm(cbind(n, month_total - n) ~ year, ., family = "binomial")slopes <- word_month_counts %>%
nest(-word) %>%
mutate(model = map(data, mod)) %>%
unnest(map(model, tidy)) %>%
filter(term == "year") %>%
arrange(desc(estimate))slopes %>%
head(9) %>%
inner_join(word_month_counts, by = "word") %>%
mutate(word = reorder(word, -estimate)) %>%
ggplot(aes(month, n / month_total, color = word)) +
geom_line(show.legend = FALSE) +
scale_y_continuous(labels = percent_format()) +
facet_wrap(~ word, scales = "free_y") +
expand_limits(y = 0) +
labs(x = "Year",
y = "Percentage of reviews containing this word",
title = "9 fastest growing words in TripAdvisor reviews",
subtitle = "Judged by growth rate over 15 years")
Рисунок 8

Мы можем увидеть пик дискуссии вокруг «пятничного фейерверка» и «лагуны» до 2010 года. А такие слова, как «курортный сбор» и «занятость», росли быстрее всего до 2005 года.

Какие слова уменьшались по частоте в обзорах?

slopes %>%
tail(9) %>%
inner_join(word_month_counts, by = "word") %>%
mutate(word = reorder(word, estimate)) %>%
ggplot(aes(month, n / month_total, color = word)) +
geom_line(show.legend = FALSE) +
scale_y_continuous(labels = percent_format()) +
facet_wrap(~ word, scales = "free_y") +
expand_limits(y = 0) +
labs(x = "Year",
y = "Percentage of reviews containing this term",
title = "9 fastest shrinking words in TripAdvisor reviews",
subtitle = "Judged by growth rate over 4 years")
Рисунок 9

Это показывает несколько тем, в которых интерес исчез с 2010 года, включая «hhv» (сокращение от «hilton hawaiian village», я считаю), «завтрак», «повышенный уровень», «цены» и «бесплатные».

Давайте сравним пару выбранных слов.

word_month_counts %>%
filter(word %in% c("service", "food")) %>%
ggplot(aes(month, n / month_total, color = word)) +
geom_line(size = 1, alpha = .8) +
scale_y_continuous(labels = percent_format()) +
expand_limits(y = 0) +
labs(x = "Year",
y = "Percentage of reviews containing this term", title = "service vs food in terms of reviewers interest")
Рисунок 10

Обслуживание и еда были главными темами до 2010 года. Разговор об обслуживании и еде достиг пика в начале данных примерно в 2003 году. После 2005 года наблюдалась тенденция к снижению, со случайными пиками.

Анализ настроений

Анализ настроений широко применяется для озвучивания материалов клиентов, таких как обзоры и ответы на опросы, онлайн и социальные сети, для приложений, которые варьируются от маркетинга до обслуживания клиентов и клинической медицины.

В нашем случае мы стремимся определить отношение рецензента (то есть гостя отеля) в отношении его (или ее) прошлого опыта или эмоциональной реакции на отель. Отношение может быть суждением или оценкой.

Самые распространенные положительные и отрицательные слова в отзывах.

reviews <- df %>% 
filter(!is.na(review_body)) %>%
select(ID, review_body) %>%
group_by(row_number()) %>%
ungroup()
tidy_reviews <- reviews %>%
unnest_tokens(word, review_body)
tidy_reviews <- tidy_reviews %>%
anti_join(stop_words)

bing_word_counts <- tidy_reviews %>%
inner_join(get_sentiments("bing")) %>%
count(word, sentiment, sort = TRUE) %>%
ungroup()

bing_word_counts %>%
group_by(sentiment) %>%
top_n(10) %>%
ungroup() %>%
mutate(word = reorder(word, n)) %>%
ggplot(aes(word, n, fill = sentiment)) +
geom_col(show.legend = FALSE) +
facet_wrap(~sentiment, scales = "free") +
labs(y = "Contribution to sentiment", x = NULL) +
coord_flip() +
ggtitle('Words that contribute to positive and negative sentiment in the reviews')
Рисунок 11

Давайте попробуем другую библиотеку настроений, чтобы увидеть, совпадают ли результаты.

contributions <- tidy_reviews %>%
inner_join(get_sentiments("afinn"), by = "word") %>%
group_by(word) %>%
summarize(occurences = n(),
contribution = sum(score))
contributions %>%
top_n(25, abs(contribution)) %>%
mutate(word = reorder(word, contribution)) %>%
ggplot(aes(word, contribution, fill = contribution > 0)) +
ggtitle('Words with the greatest contributions to positive/negative
sentiment in reviews') +
geom_col(show.legend = FALSE) +
coord_flip()
Рисунок 12

Интересно видеть, что «алмаз» (как алмазная голова) был отнесен к категории позитивных настроений.

Здесь есть потенциальная проблема, например, «чистый», в зависимости от контекста, имеет негативное отношение, если ему предшествует слово «не». На самом деле у unigrams будет эта проблема с отрицанием в большинстве случаев. Это подводит нас к следующей теме:

Использование биграмм для обеспечения контекста в анализе настроений

Мы хотим увидеть, как часто перед словами стоит слово «не»

bigrams_separated %>%
filter(word1 == "not") %>%
count(word1, word2, sort = TRUE)
Рисунок 13

Было 850 раз в данных, что слову «а» предшествует слово «не», и 698 раз в дате, когда слову «а» предшествует слово «не». Однако эта информация не имеет смысла.

AFINN <- get_sentiments("afinn")
not_words <- bigrams_separated %>%
filter(word1 == "not") %>%
inner_join(AFINN, by = c(word2 = "word")) %>%
count(word2, score, sort = TRUE) %>%
ungroup()

not_words
Рисунок 14

Это говорит нам о том, что в данных наиболее распространенное связанное с чувствами слово, которое следует за «не», - это «стоит», а второе общее связанное с настроением слово, которое следует за «не», - это «рекомендовать», которое обычно имеет (положительный ) оценка 2.

Итак, по нашим данным, какие слова оказали наибольшее влияние в неправильном направлении?

not_words %>%
mutate(contribution = n * score) %>%
arrange(desc(abs(contribution))) %>%
head(20) %>%
mutate(word2 = reorder(word2, contribution)) %>%
ggplot(aes(word2, n * score, fill = n * score > 0)) +
geom_col(show.legend = FALSE) +
xlab("Words preceded by \"not\"") +
ylab("Sentiment score * number of occurrences") +
ggtitle('The 20 words preceded by "not" that had the greatest contribution to
sentiment scores, positive or negative direction') +
coord_flip()
Рисунок 15

Биграммы «не стоит», «не очень», «не хорошо», «не рекомендую» и «не нравится» были основными причинами неправильной идентификации, в результате чего текст казался гораздо более позитивным, чем он есть.

Кроме «нет», есть другие слова, которые отменяют последующий термин, например, «нет», «никогда» и «без». Давайте проверим их.

negation_words <- c("not", "no", "never", "without")

negated_words <- bigrams_separated %>%
filter(word1 %in% negation_words) %>%
inner_join(AFINN, by = c(word2 = "word")) %>%
count(word1, word2, score, sort = TRUE) %>%
ungroup()

negated_words %>%
mutate(contribution = n * score,
word2 = reorder(paste(word2, word1, sep = "__"), contribution)) %>%
group_by(word1) %>%
top_n(12, abs(contribution)) %>%
ggplot(aes(word2, contribution, fill = n * score > 0)) +
geom_col(show.legend = FALSE) +
facet_wrap(~ word1, scales = "free") +
scale_x_discrete(labels = function(x) gsub("__.+$", "", x)) +
xlab("Words preceded by negation term") +
ylab("Sentiment score * # of occurrences") +
ggtitle('The most common positive or negative words to follow negations
such as "no", "not", "never" and "without"') +
coord_flip()
Рисунок 16

Похоже, что крупнейшими источниками неверного определения слова как положительного являются слова «не стоит / здорово / хорошо / рекомендовать», а крупнейшим источником неверно классифицированных негативных настроений являются «не плохо» и «нет проблем».

Наконец, давайте выясним самые положительные и отрицательные отзывы.

sentiment_messages <- tidy_reviews %>%
inner_join(get_sentiments("afinn"), by = "word") %>%
group_by(ID) %>%
summarize(sentiment = mean(score),
words = n()) %>%
ungroup() %>%
filter(words >= 5)sentiment_messages %>%
arrange(desc(sentiment))
Рисунок 17

ID наиболее положительного отзыва - 2363:

df[ which(df$ID==2363), ]$review_body[1]
Рисунок 18
sentiment_messages %>%
arrange(sentiment)
Рисунок 19

ID самого отрицательного отзыва - 3748:

df[ which(df$ID==3748), ]$review_body[1]
Рисунок 20

Это было весело! Исходный код можно найти наGithub, Желаю хорошей недели!

Ссылка:Текст Mining с R

Оригинальная статья

Footer decor

© www.machinelearningmastery.ru | Ссылки на оригиналы и авторов сохранены. | map