Write text and code here.

Executive summary

What is (are) your main question(s)? What is your story? What does the final graphic show?

I am preparing for a trip to the UK and am particularly interested in hotels located in UK. To ensure reliability, I filtered hotels with over 100 reviews and reviewers who have written more than 10 reviews. I then analyzed the average ratings of these reviews. From this analysis, I identified the top three hotels in London with an average rating of 8 or higher. My aim was to identify hotels with reliable and highly-rated features. So main questions are “What are the strengths of hotels in London, UK, according to reliable reviews? How can I identify hotels that meet my desired criteria for my trip?”

The final graphic displays a comprehensive analysis of the positive reviews for these top-rated hotels. Using a word cloud, bar graph, and bigram analysis, I visualized the common positive feedback themes. Negative reviews were mostly generic comments like ‘No Negative’, ‘nothing’, ‘We liked everything’, and were therefore excluded. Ultimately, this analysis helps me choose a hotel with the desired strengths for my stay.

Data background

Explain where the data came from, what agency or company made it, how it is structured, what it shows, etc.

The data for this analysis was sourced from Kaggle, specifically from the dataset titled “515K Hotel Reviews Data in Europe.” This dataset was compiled by Jiashen Liu and includes over 515,000 customer reviews and scores for 1,493 luxury hotels across Europe.

The dataset is structured in a CSV file format and contains multiple variables including: Hotel Name, Reviewer Name, Reviewer Country, Review Text, Review Rating, Review Date, Positive Review, Negative Review etc.

The dataset provides detailed insights into customer experiences at various hotels. It includes both qualitative data and quantitative data. This dataset is ideal for exploring the strengths and weaknesses of hotels, and since there are many different columns, it is suitable for filtering what we want.

Data loading, cleaning and preprocessing

Describe and show how you cleaned and reshaped the data

hotel_reviews <- read.csv("Hotel_Reviews.csv")

# add 'Country' column
hotel_reviews <- hotel_reviews %>%
  mutate(Country = if_else(grepl("United Kingdom", Hotel_Address), "United Kingdom", NA_character_))

# filtering necessary columns
reviews <- hotel_reviews %>%
  select(Hotel_Name, Hotel_Address, Total_Number_of_Reviews, Total_Number_of_Reviews_Reviewer_Has_Given, Negative_Review, Positive_Review, Reviewer_Score, Country)

# filtering hotels in United Kingdom
UK_hotels <- reviews %>%
  filter(Country %in% c("United Kingdom"))

# filtering reviewers who have written more than 10 reviews
reliable_reviews <- UK_hotels %>% 
  filter(Total_Number_of_Reviews_Reviewer_Has_Given > 10)

# filtering hotels with more than 100 reviews
reliable_reviews <- reliable_reviews %>% 
  filter(Total_Number_of_Reviews > 100)

# average ratings of reviewers who have written more than 10 reviews
avg_ratings <- reliable_reviews %>% 
  group_by(Hotel_Name) %>% 
  summarise(avg_rating = mean(Reviewer_Score))

# filtering top 3 hotels with an average rating greater than 8
high_rated_hotels <- avg_ratings %>% 
  filter(avg_rating >= 8) %>% 
  slice_max(avg_rating, n = 3)

# save names of top 3 hotels
top_hotels <- c("41", "Milestone Hotel Kensington", "Covent Garden Hotel")

# filtering reviews of top 3 hotels
top_hotels_reviews <- reliable_reviews %>%
  filter(Hotel_Name %in% c("41", "Milestone Hotel Kensington", "Covent Garden Hotel"))

And here I concluded that negative reviews were not suitable for deriving the desired results.

Text data analysis

# extracting positive review words for the hotel "41"
Fo_positive_words <- reliable_reviews %>%
  filter(Hotel_Name == "41") %>%
  unnest_tokens(word, Positive_Review) %>%
  anti_join(stop_words) %>%
  count(word, sort = TRUE) %>% 
  slice_max(n, n = 10)
## Joining with `by = join_by(word)`
# extracting positive review words for the hotel "Milestone Hotel Kensington"
MHK_positive_words <- reliable_reviews %>%
  filter(Hotel_Name == "Milestone Hotel Kensington") %>%
  unnest_tokens(word, Positive_Review) %>%
  anti_join(stop_words) %>%
  count(word, sort = TRUE) %>% 
  slice_max(n, n = 10)
## Joining with `by = join_by(word)`
# extracting positive review words for the hotel "Covent Garden Hotel"
CGH_positive_words <- reliable_reviews %>%
  filter(Hotel_Name == "Covent Garden Hotel") %>%
  unnest_tokens(word, Positive_Review) %>%
  anti_join(stop_words) %>%
  count(word, sort = TRUE) %>% 
  slice_max(n, n = 10)
## Joining with `by = join_by(word)`
# creating bigram codes for positive reviews for the hotel "41"
Fo_P_bigrams <- reliable_reviews %>%
  filter(Hotel_Name == "41") %>%
  unnest_tokens(bigram, Positive_Review, token = "ngrams", n = 2) %>%
  separate(bigram, into = c("word1", "word2"), sep = " ") %>%
  filter(!is.na(word1) & !is.na(word2)) %>%
  filter(!word1 %in% stop_words$word, 
         !word2 %in% stop_words$word) %>%
  unite(bigram, word1, word2, sep = " ") %>% 
  count(bigram, sort = TRUE)

# creating bigram codes for positive reviews for the hotel "Milestone Hotel Kensington"
MHK_P_bigrams <- reliable_reviews %>%
  filter(Hotel_Name == "Milestone Hotel Kensington") %>%
  unnest_tokens(bigram, Positive_Review, token = "ngrams", n = 2) %>%
  separate(bigram, into = c("word1", "word2"), sep = " ") %>%
  filter(!is.na(word1) & !is.na(word2)) %>%
  filter(!word1 %in% stop_words$word, 
         !word2 %in% stop_words$word) %>%
  unite(bigram, word1, word2, sep = " ") %>% 
  count(bigram, sort = TRUE)

# creating bigram codes for positive reviews for the hotel "Covent Garden Hotel"
CGH_P_bigrams <- reliable_reviews %>%
  filter(Hotel_Name == "Covent Garden Hotel") %>%
  unnest_tokens(bigram, Positive_Review, token = "ngrams", n = 2) %>%
  separate(bigram, into = c("word1", "word2"), sep = " ") %>%
  filter(!is.na(word1) & !is.na(word2)) %>%
  filter(!word1 %in% stop_words$word, 
         !word2 %in% stop_words$word) %>%
  unite(bigram, word1, word2, sep = " ") %>% 
  count(bigram, sort = TRUE)

Individual analysis and figures

Anaysis and Figure 1

# wordcloud of positive reviews for the hotel "41"
Fo_positive_words %>% 
  with(wordcloud(word, n, max.words = 100))

# wordcloud of positive reviews for the hotel "Milestone Hotel Kensington"
MHK_positive_words %>% 
  with(wordcloud(word, n, max.words = 100))

# wordcloud of positive reviews for the hotel "Covent Garden Hotel"
CGH_positive_words %>% 
  with(wordcloud(word, n, max.words = 100))

Describe and show how you created the first figure. Why did you choose this figure type?

I used the wordcloud function to create a wordcloud of positive review words for the hotel. The positive_words dataset of each hotels above contains the words and their frequencies extracted from positive reviews specifically for the hotels. I chose the wordcloud function because it visually represents the frequency of words. This figure type is effective for quickly identifying which words are most commonly used in positive reviews, providing an intuitive sense of the most strengths of the hotel.

Anaysis and Figure 2

# bargraph of top 10 positive review words for the hotel "41"
Fo_positive_plot <- ggplot(Fo_positive_words, aes(x = reorder(word, n), y = n)) +
  geom_col(show.legend = F) +
  coord_flip() +
  labs(title = "Top 10 Positive Review Words for Hotel '41'",
       x = "Words",
       y = "Frequency") +
  theme_minimal()
Fo_positive_plot

# bargraph of top 10 positive review words for the hotel "Milestone Hotel Kensington"
MHK_positive_plot <- ggplot(MHK_positive_words, aes(x = reorder(word, n), y = n)) +
  geom_col(show.legend = F) +
  coord_flip() +
  labs(title = "Top 10 Positive Review Words for Hotel 'MHK'",
       x = "Words",
       y = "Frequency") +
  theme_minimal()
MHK_positive_plot

# bargraph of top 10 positive review words for the hotel "Covent Garden Hotel"
CGH_positive_plot <- ggplot(CGH_positive_words, aes(x = reorder(word, n), y = n)) +
  geom_col(show.legend = F) +
  coord_flip() +
  labs(title = "Top 10 Positive Review Words for Hotel 'CGH'",
       x = "Words",
       y = "Frequency") +
  theme_minimal()
CGH_positive_plot

Describe and show how you created the first figure. Why did you choose this figure type?

I used the ggplot2 package to create a bargraph of the top 10 positive review words for the hotels. And i used coord_flip() to make the bars horizontal for easier reading of word labels. I chose a horizontal bargraph because it effectively displays the frequency of each word in the positive reviews. By sorting the bars in descending order, it allows easy identification of the most frequently mentioned positive aspects of the hotel. This figure type is clear and straightforward, making it ideal for presenting the top positive review words in a concise manner.

Anaysis and Figure 3

# bigram network of positive review for the hotel "41"
Fo_P_bigram_graph <- Fo_P_bigrams %>%
  separate(bigram, into = c("word1", "word2"), sep = " ") %>%
  filter(n >= 1) %>%
  graph_from_data_frame()

set.seed(2024)
a <- grid::arrow(type = "closed", length = unit(.15, "inches"))

ggraph(Fo_P_bigram_graph, layout = "fr") +
  geom_edge_link(aes(edge_alpha = n), show.legend = FALSE,
                 arrow = a, end_cap = circle(.07, 'inches')) +
  geom_node_point(color = "pink", size = 5) +
  geom_node_text(aes(label = name), vjust = 1, hjust = 1) +
  theme_void() +
  labs(title = "41 - Positive Reviews")

# bigram network of positive review for the hotel "Milestone Hotel Kensington"
MHK_P_bigram_graph <- MHK_P_bigrams %>%
  separate(bigram, into = c("word1", "word2"), sep = " ") %>%
  filter(n >= 1) %>%
  graph_from_data_frame()

ggraph(MHK_P_bigram_graph, layout = "fr") +
  geom_edge_link(aes(edge_alpha = n), show.legend = FALSE,
                 arrow = a, end_cap = circle(.07, 'inches')) +
  geom_node_point(color = "lightgreen", size = 5) +
  geom_node_text(aes(label = name), vjust = 1, hjust = 1) +
  theme_void() +
  labs(title = "Milestone Hotel Kensington - Positive Reviews")

# bigram network of positive review for the hotel "Covent Garden Hotel"
CGH_P_bigram_graph <- CGH_P_bigrams %>%
  filter(n >= 1) %>%
  separate(bigram, into = c("word1", "word2"), sep = " ") %>%
  graph_from_data_frame()

ggraph(CGH_P_bigram_graph, layout = "fr") +
  geom_edge_link(aes(edge_alpha = n), show.legend = FALSE,
                 arrow = a, end_cap = circle(.07, 'inches')) +
  geom_node_point(color = "lightblue", size = 5) +
  geom_node_text(aes(label = name), vjust = 1, hjust = 1) +
  theme_void() +
  labs(title = "Covent Garden Hotel - Positive Reviews")

In showing the figures that you created, describe why you designed it the way you did. Why did you choose those colors, fonts, and other design elements? Does it convey truth?

I used the ggraph and igraph packages to create bigrams extracted from positive reviews for the hotels. The P_bigrams dataset of each hotels above contains bigrams and their frequencies, filtered to include only those with at least one occurrence (filter(n >= 1)). I extracted the top 10 words using a bargraph and analyzed their combinations in the bigram graph to understand the flows. This figure type effectively communicates the co-occurrence patterns of positive review words, allowing us to identify prominent themes or features of the hotel based on guest feedback. I chose light colors like pink, light blue, and light green to ensure they wouldn’t distract from observing the connections between words. I also set the size of geom_node_point to 5 to keep the circles from being too large. As a result, I could confirm that ‘41’ Hotel is praised for its lounge and hotel snack bar, ‘Milestone Hotel Kensington’ is noted for its excellent hotel staff, and ‘Covent Garden Hotel’ is appreciated mostly for its location. Initially, I intended to analyze negative reviews as well, but due to concerns about their reliability, I focused solely on positive reviews. Despite this limitation, I feel this project will greatly assist me in selecting hotels for my trip to the UK later.

You can also include images like this:

options(repos = 'https://cloud.r-project.org/')