1. Introduction

In our ever-increasing technological world, clean and understandable data sets are vital in producing practical, knowledgeable conclusions and decisions. Regarding hotels, the data we derive from guests are sent across numerous departments such as marketing, operations, guest services, and sales to produce the most revenue and ensure a satisfactory experience for guests. On the other hand, guests can also derive much information from the data presented by hotels, so they can make the best decisions for their specific circumstances. This perspective is what the following project will concern, as we concentrate on the logistics and attributes that everyday consumers would be interested in when planning their next getaway.

Over the past three years, COVID-19 has had significant damage to the hospitality industry and produced significant losses in revenue and traction as a whole. Additionally, the emergence of new kinds of hospitality such as AirBnBs has made the market thus more competitive, giving consumers many more options to choose from.

So, with this, we must ask, which kind of hotel is most desirable? For our example, we have a young couple who just eloped for their wedding. They are trying to plan a honeymoon in Portugal with only two weeks notice. They are looking for the full honeymoon experience while diving into the culture of the area. To do this, we will analyze six specific variables of Resort vs. City Hotels and decide which are most suitable for the newly weds. More specifically, the meal plans, repeat guests, cancellations, country of origin, and the children in the resort. Let us dive into the complexities and details of Resort Hotels and City Hotels and conclude which method of hospitality is preferred based on the data presented to us.

2. Packages Required

library(tidyr) # creating tidy data 
library(dplyr) # transforming (joining, summarizing) data
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(tibble)# coercing data to tibble   
library(ggplot2) # visualizing data 

3. Data Preparation

The data used was obtained from an open hotel booking demand data set from Antonion, Almeida, and Nunes, 2019. Both hotels are located in Portugal, Resort_Hotels in Algarve, and City_Hotels in Lisbon.

Here, we have two data sets, one from a resort hotel, and the other from a city hotel. The purpose, according to Science Direct writes, “Due to the scarcity of real business data for scientific and educational purposes, these data sets can have an important role for research and education in revenue management, machine learning, or data mining, as well as in other fields.”

Each has the same structure with 31 variables, while describing 40,060 observations from the resort hotel, and 79,330 observations of the city hotel. This data is collected between July 1st, 2015, and August 31st, 2017. Each observation represents a hotel booking and includes those that were canceled. There are no missing data entries; however, there are some cells that read “NULL”, which means the value is not applicable (Science Direct).

Let’s start and import our data

getwd()
## [1] "/Users/briannasexton/Desktop/bing/Past Semesters/Fall 2022/Math 329/MATH329FinalProject"
resort_data <- read.csv("resort_hotels.csv")

city_data <- read.csv("city_hotels.csv")

Now’s lets see the data as a Tibble

as_tibble(resort_data)
## # A tibble: 40,060 × 34
##        X hotel        x1 is_ca…¹ lead_…² arriv…³ arriv…⁴ arriv…⁵ arriv…⁶ stays…⁷
##    <int> <chr>     <int>   <int>   <int>   <int> <chr>     <int>   <int>   <int>
##  1     1 Resort H…     1       0     342    2015 July         27       1       0
##  2     2 Resort H…     2       0     737    2015 July         27       1       0
##  3     3 Resort H…     3       0       7    2015 July         27       1       0
##  4     4 Resort H…     4       0      13    2015 July         27       1       0
##  5     5 Resort H…     5       0      14    2015 July         27       1       0
##  6     6 Resort H…     6       0      14    2015 July         27       1       0
##  7     7 Resort H…     7       0       0    2015 July         27       1       0
##  8     8 Resort H…     8       0       9    2015 July         27       1       0
##  9     9 Resort H…     9       1      85    2015 July         27       1       0
## 10    10 Resort H…    10       1      75    2015 July         27       1       0
## # … with 40,050 more rows, 24 more variables: stays_in_week_nights <int>,
## #   adults <int>, children <int>, babies <int>, meal <chr>, country <chr>,
## #   market_segment <chr>, distribution_channel <chr>, is_repeated_guest <int>,
## #   previous_cancellations <int>, previous_bookings_not_canceled <int>,
## #   reserved_room_type <chr>, assigned_room_type <chr>, booking_changes <int>,
## #   deposit_type <chr>, agent <chr>, company <chr>, days_in_waiting_list <int>,
## #   customer_type <chr>, adr <dbl>, required_car_parking_spaces <int>, …
as_tibble(city_data)
## # A tibble: 79,330 × 34
##        X hotel        x1 is_ca…¹ lead_…² arriv…³ arriv…⁴ arriv…⁵ arriv…⁶ stays…⁷
##    <int> <chr>     <int>   <int>   <int>   <int> <chr>     <int>   <int>   <int>
##  1     1 City Hot…     1       0       6    2015 July         27       1       0
##  2     2 City Hot…     2       1      88    2015 July         27       1       0
##  3     3 City Hot…     3       1      65    2015 July         27       1       0
##  4     4 City Hot…     4       1      92    2015 July         27       1       2
##  5     5 City Hot…     5       1     100    2015 July         27       2       0
##  6     6 City Hot…     6       1      79    2015 July         27       2       0
##  7     7 City Hot…     7       0       3    2015 July         27       2       0
##  8     8 City Hot…     8       1      63    2015 July         27       2       1
##  9     9 City Hot…     9       1      62    2015 July         27       2       2
## 10    10 City Hot…    10       1      62    2015 July         27       2       2
## # … with 79,320 more rows, 24 more variables: stays_in_week_nights <int>,
## #   adults <int>, children <int>, babies <int>, meal <chr>, country <chr>,
## #   market_segment <chr>, distribution_channel <chr>, is_repeated_guest <int>,
## #   previous_cancellations <int>, previous_bookings_not_canceled <int>,
## #   reserved_room_type <chr>, assigned_room_type <chr>, booking_changes <int>,
## #   deposit_type <chr>, agent <chr>, company <chr>, days_in_waiting_list <int>,
## #   customer_type <chr>, adr <dbl>, required_car_parking_spaces <int>, …

Let’s combine both the Resort and Hotel Data

rbind <- rbind(resort_data, city_data)
head(rbind)
##   X        hotel x1 is_canceled lead_time arrival_date_year arrival_date_month
## 1 1 Resort Hotel  1           0       342              2015               July
## 2 2 Resort Hotel  2           0       737              2015               July
## 3 3 Resort Hotel  3           0         7              2015               July
## 4 4 Resort Hotel  4           0        13              2015               July
## 5 5 Resort Hotel  5           0        14              2015               July
## 6 6 Resort Hotel  6           0        14              2015               July
##   arrival_date_week_number arrival_date_day_of_month stays_in_weekend_nights
## 1                       27                         1                       0
## 2                       27                         1                       0
## 3                       27                         1                       0
## 4                       27                         1                       0
## 5                       27                         1                       0
## 6                       27                         1                       0
##   stays_in_week_nights adults children babies meal country market_segment
## 1                    0      2        0      0   BB     PRT         Direct
## 2                    0      2        0      0   BB     PRT         Direct
## 3                    1      1        0      0   BB     GBR         Direct
## 4                    1      1        0      0   BB     GBR      Corporate
## 5                    2      2        0      0   BB     GBR      Online TA
## 6                    2      2        0      0   BB     GBR      Online TA
##   distribution_channel is_repeated_guest previous_cancellations
## 1               Direct                 0                      0
## 2               Direct                 0                      0
## 3               Direct                 0                      0
## 4            Corporate                 0                      0
## 5                TA/TO                 0                      0
## 6                TA/TO                 0                      0
##   previous_bookings_not_canceled reserved_room_type assigned_room_type
## 1                              0                  C                  C
## 2                              0                  C                  C
## 3                              0                  A                  C
## 4                              0                  A                  A
## 5                              0                  A                  A
## 6                              0                  A                  A
##   booking_changes deposit_type agent company days_in_waiting_list customer_type
## 1               3   No Deposit  NULL    NULL                    0     Transient
## 2               4   No Deposit  NULL    NULL                    0     Transient
## 3               0   No Deposit  NULL    NULL                    0     Transient
## 4               0   No Deposit   304    NULL                    0     Transient
## 5               0   No Deposit   240    NULL                    0     Transient
## 6               0   No Deposit   240    NULL                    0     Transient
##   adr required_car_parking_spaces total_of_special_requests reservation_status
## 1   0                           0                         0          Check-Out
## 2   0                           0                         0          Check-Out
## 3  75                           0                         0          Check-Out
## 4  75                           0                         0          Check-Out
## 5  98                           0                         1          Check-Out
## 6  98                           0                         1          Check-Out
##   reservation_status_date
## 1              2015-07-01
## 2              2015-07-01
## 3              2015-07-02
## 4              2015-07-02
## 5              2015-07-03
## 6              2015-07-03

For this analysis, we will not be using all data variables. Here we will subset the columns being used into a new data frame

used_resort_data <- resort_data[,c(14, 18, 3, 15, 12, 13, 4)]
  
used_city_data <- city_data[,c(14, 18, 3, 15, 12, 13, 4)]

used_resort_and_city_data <- rbind(used_resort_data, used_city_data)
head(used_resort_and_city_data)
##   babies distribution_channel x1 meal adults children is_canceled
## 1      0               Direct  1   BB      2        0           0
## 2      0               Direct  2   BB      2        0           0
## 3      0               Direct  3   BB      1        0           0
## 4      0            Corporate  4   BB      1        0           0
## 5      0                TA/TO  5   BB      2        0           0
## 6      0                TA/TO  6   BB      2        0           0

Handling null values

city_children <- na.omit(used_city_data$children)
#This will allow us to compute future analysis with factoring in null data

Totaling reservations to find percentages

total_resort <- nrow(resort_data)
total_resort #Total number of resort reservation data
## [1] 40060
total_city <- nrow(city_data)
total_city #Total number of city reservation data
## [1] 79330

4. Exploratory Data Analysis

Variable One: Meal Types

Firstly, lets look at the types of meals that a guest can purchase, and the frequency of which they occur.

1. Undefined/SC - no meal package

2. BB - Bed & Breakfast

3. HB - Half Board (breakfast, and one other meal)

4. FB - Full Board (breakfast, lunch, and dinner)

To visualize the difference of these four different meal types, and its frequency depending on the type of hotel, let’s us a barplot.

meal_type <- xtabs( ~meal+hotel , data = rbind)
barplot(meal_type,
        main = "Meal Plan by Hotel",
        xlab = "Hotel Type", ylab = "Frequency",
        col = c("aquamarine", "blue", "purple", "green", "red"),
        legend.text = rownames(meal_type),
        beside = TRUE )

Variable Two: Repeat Data

Looking at this data, we see that it is under the class ‘double’ meaning that a ‘1’ stands for a repeated guest and ‘0’ stands for a non-repeated guest. Because of this, the mean of this column wouldn’t give us much information, so let’s find the number of both ‘0’ and ‘1’ that occur in the column.

percent_repeat <- function(n,total){
  percent <- (n/total)*100
  percent
}
# Resort Repeat Guests
n <- sum(resort_data$is_repeated_guest == 1) #1 is the value input if reservation is a repeat guest
n
## [1] 1778
total <- total_resort
total
## [1] 40060
percent_repeat(n, total) #Percent of repeat guests in resort hotel
## [1] 4.438342
# City Repeat Guests 
n <- sum(city_data$is_repeated_guest ==1)
total <- total_city
percent_repeat(n, total) #Percent of repeat guest
## [1] 2.561452

From our first two lines of code, we see that there are NO repeat guests from the Resort Hotel, which is definitely a remarkable statistic to take note of. The consumer must wonder why there has been no retention in the resort’s guests. Shifting gears to the City Hotel, we see that 2.5615% of the City Hotel guest are repeat guests which comes to 2,032 guests.

Variable Three: Cancellations Data

Like the ‘repeat data’, the ‘cancellation data’ also only has ’0’s, and ’1’s which stands for not canceled, and canceled, respectively. Let us use a function is order to quickly derive the percentage of guests that cancelled their stays.

percent_cancellations <- function(n,total){
  cancelled <- (n/total)*100
  cancelled
}
# Resort Cancellations
sum(used_resort_data$is_canceled == 0)
## [1] 28938
n <- sum(used_resort_data$is_canceled == 1)
total <- total_resort
percent_cancellations(n, total) #Percent of resort reservations cancelled
## [1] 27.76335
# City Cancellations 
sum(used_city_data$is_canceled == 0)
## [1] 46228
n <- sum(used_city_data$is_canceled == 1)
total <- total_city
percent_cancellations(n,total) #Percent of city reservations cancelled
## [1] 41.72696

For the resort hotel, 27.76 % of bookings are canceled, while 41.72% of city hotel booking are canceled. For a customer looking to book at hotel, the city hotel’s cancellation rate is extremely high, and would cause a perspective guest to wonder what the reasoning behind this large statistic.

Variable Four: Country of Origin

Diversity, equity, and inclusion is vital for travelers to understand when traveling, so understanding the profile of guests that will be at the hotels is an important metric to have. To start, let us make a table that shows the country of origin of each of the travelers.

# Resort: Country of Origin 
country_resort <- xtabs(~country, data = resort_data)
country_resort <- data.frame(country_resort)
colnames(country_resort) <- c("Country Code", "Frequency")
attach(country_resort)
country_resort <- country_resort[order(-Frequency),]
view(country_resort)

# City: Country of Origin 
country_city <- xtabs(~country, data = city_data)
country_city <- data.frame(country_city)
colnames(country_city) <- c("Country Code", "Frequency")
attach(country_city)
## The following objects are masked from country_resort:
## 
##     Country Code, Frequency
country_city <- country_city[order(-Frequency),]
view(country_city)

Let us examine the top four countries of origin from each data set and compare them. From this data we find that the Resort Data has 17,630 individuals from Portugal, 6,814 from the United Kingdom, 3,957 from Spain, and lastly, 2,166 from Ireland. Moving into the city data we see that Portugal has 30,960 individuals, France with 8,804, Germany with 6,084, and lastly the United Kingdom with 5,315.

percent_frequency <- function(n,total){
  percent <- (n/total)*100
  percent
}
#Resort Data 
total <- total_resort
percent_frequency(17630, total)# 44.008% of Resort guest are from Portugal
## [1] 44.00899
percent_frequency(6814, total)# 17.009% of Resort guests are from the UK 
## [1] 17.00949
percent_frequency(3957, total)# 9.877% of Resort guests are from Spain
## [1] 9.877683
percent_frequency(2166, total)# 5.406% of Resort guests are from Ireland 
## [1] 5.40689
#City Data 
total <- total_city
percent_frequency(30960, total) # 39.026% of City guests are from Portugal
## [1] 39.02685
percent_frequency(8804, total) # 11.097% of City guests are from France 
## [1] 11.09795
percent_frequency(6084, total) # 7.669% of City guests are from Germany 
## [1] 7.66923
percent_frequency(5315, total) # 6.6998% of City guests are from the UK 
## [1] 6.699861
# Resort Data 
(44.008 + 17.009 + 9.877 + 5.406)
## [1] 76.3
# 76.3% of guest are accounted for in the top four countries

#City Data 
(39.026 + 11.097 + 7.669 + 6.6998)
## [1] 64.4918
# 64.49% of guest are accounted for in the top four countries 

Variable Five: Children in the Resort

The environment, and culture of a hotel is something to consider when planning a trip. This culture can be heavily affected by the age of who is staying at these places. Therefore, we have created a section of code that shows which hotel has a higher frequency of children in attendance. This is illustrated in the barchart below. We have made the new variable “city_kids” which combines both children and babies into one variable.

city_children <- na.omit(used_city_data$children)
city_kids <- sum(city_children, used_city_data$babies != 0)
city_kids
## [1] 7617
resort_kids <- sum(used_resort_data$children, used_resort_data$babies != 0)
resort_kids
## [1] 5703
kids <- c(city_kids/nrow(used_city_data), resort_kids/nrow(used_resort_data))
barplot(kids, beside=TRUE, legend=TRUE, names.arg=c("City Hotel", "Resort Hotel"), ylab = "Number of Kids (Children and Babies)", main = "Kids in Resort Hotel vs. City Hotel", col = c("pink", "blue"))

#City Children 
(7617/79330)# 9.601% of City Hotel guests are children 
## [1] 0.09601664
#Resort Children 
(5703/40060) #14.236% of Resort Hotel guests are children 
## [1] 0.1423615

Then we can separate by hotel in the barchart and see that city hotels have 7,617 children, and resort hotels have 5,703 children. So while city hotels have a higher number of children, the percentage of children over the total number of guest at the hotel is 9.601% of guests, while the resort data is 14.236% of guest.

Variable Six: Lead Time

The lead time, in respect to the hospitality industry, is the number of days that elapsed between the entering date of the booking into the property management system and the arrival date.

city_lead_time <- city_data$lead_time
resort_lead_time <- resort_data$lead_time
city_resort_lead_times <- c(city_lead_time, resort_lead_time)

sum_city <- sum(city_lead_time < 14)
sum_resort <- sum(resort_lead_time < 14)
lead_times <- c(sum_city, sum_resort)

pie(lead_times, labels = c("City Hotel", "Resort Hotel"), main = "Number of Lead Times Less Than 2 Weeks", col = c("purple", "light blue"))

5. Summary

Throughout this project we were trying to analyze which hotel would be the best fit for the newlyweds planning their last minute honeymoon in Portugal. We have taken a deeper dive into six specific variables, and we can now make an informative decision for this couple. Let’s run through the finding of these variables.

To begin, we have the ‘Meal Types’. We concluded that the City Hotel has a higher frequency of the Bed and Breakfast option compared to the Resort Hotel through the application of a bar chart. Because the bed and breakfast would mean they will eat lunch and dinner somewhere else, this would allow the couple to experience the surrounding area more, instead of being tied to the hotel for the entirety of the day.

Moving to repeat guests, through our work with the function sum, the Resort Hotel had 4.44% of guests being repeat guests, while 2.5615% of the City Hotel’s guests were repeat guests. In terms of retention, the Resort Hotel seems to be the more desirable option, as they have a base of guests wanting to come back.

The third variable was cancellations. Here we found that 27.76% of Resort Hotel bookings are cancelled while 41.72% of City Hotel bookings are canceled. Due to this high cancellation rate, this is definitely a startling statistic; however, because the couple is traveling with such short notice, this high cancellation rate could be in their benefit because there may be a higher chance of being able to book a room. High cancellation rates also do not necessarily reflect the quality of the hotel because it happens before guests even experience the hotel. Therefore, in this case the City Hotel would be the better option because the newlyweds have a higher chance of acquiring a reservation.

The fourth variable was the country of origin. The couple wants to immerse themselves in the culture of the area, but also experience new cultures from travelers and neighboring areas. When we add up the percentages of the top four countries of origin for both Resort and City Hotels, we find that the Resort Hotel has 76.3% of guest accounted for, while the City Hotel has 64.49% of guests accounted for. If the couple were to go with the City Hotel, they would be able experience almost double the amount of locals as there 17,630 from Portugal in the Resort Hotel, and 30,960 from Portugal in the City Hotel. Additionally, there are more cultures accounted for in the the City Hotel, merely due to its size compared to the Resort.

The fifth variable is the amount of children in the resort. As a newly wed couple they are looking for a quiet peaceful area to enjoy their honeymoon. When we computed these statistics, we added the ‘children’ and ‘baby’ columns together and created a new variable called ‘kids’ that would account for both. Through the bar graph we see that roughly 10% of City Hotel guests are kids, while 14% of Resort Hotel guests are kids. Hence, we can infer that a City Hotel would provide an environment more conducive to a honeymoon retreat. An interesting point about this variable is that when it was initially coded, as merely comparing the amount of children the City Hotel was pretty notably higher than the resort data; however, this data was inherently misleading because after we divide the number of children by the total population we see that the Resort Data has a pretty significantly higher percentage of children compared to the City Hotel Data.

The sixth and last variable is lead time. Lead time in the context of hotel data is the number of days that elapsed between the entered date of the booking into the property management system and the arrival date. This is a highly relevant data point for our couple because they only have two weeks to plan their trip. Creating a pie chart to see the number of bookings made in less than 14 days. Through this we see that the City Hotel has more booking within the last two weeks before the guests arrive. For our couple, the City Hotel would be a better option, since they are also booking two or less weeks in advance.

Before coming to our definite conclusion, there are some serious limitations with our data set provided that must be discussed. To start, we are only provided with the number associated with the decisions made by the guests in terms of repeat stays, and cancellations. Meaning we do not know why the guest cancelled their stay. Was it for reasons relating to the hotel and its operations, or was it personal reasons completely unrelated it the hotel itself? The same can be said for the number of repeat stays: is there a low number of repeat stays because the hotel is very far from the guests’ home, or is it because they had a poor experience at the hotel, and are purposely avoiding staying at it? We lack context with some of the variables presented to us, which may skew some of the findings. The results we found with the data presented are not inherently wrong, however they could easily be reworked when given the additional context necessary. For instance, we could have an addition of a “tell us more about your stay’ survey when the guest are leaving.

Analyzing all of the variables used, and exclusively considering the numbers presented to us, it seems as though the City Hotel would be the best fit for the newlywed couple planning their last minute honeymoon. Despite not having all of the context presented to us, we have come to a conclusion using a majority of the variables we have analyzed. The data has proven to us that a short-notice honeymoon would be best planned at the City Hotel in Portugal.