R Markdown

This is an R Markdown document. Markdown is a simple formatting syntax for authoring HTML, PDF, and MS Word documents. For more details on using R Markdown see http://rmarkdown.rstudio.com.

When you click the Knit button a document will be generated that includes both content as well as the output of any embedded R code chunks within the document. You can embed an R code chunk like this:

summary(cars)
##      speed           dist       
##  Min.   : 4.0   Min.   :  2.00  
##  1st Qu.:12.0   1st Qu.: 26.00  
##  Median :15.0   Median : 36.00  
##  Mean   :15.4   Mean   : 42.98  
##  3rd Qu.:19.0   3rd Qu.: 56.00  
##  Max.   :25.0   Max.   :120.00

Including Plots

You can also embed plots, for example:

Note that the echo = FALSE parameter was added to the code chunk to prevent printing of the R code that generated the plot.

hotel <- read.csv("booking.csv/booking.csv")
#Summary Statistics
head(hotel)
##   Booking_ID number.of.adults number.of.children number.of.weekend.nights
## 1   INN00001                1                  1                        2
## 2   INN00002                1                  0                        1
## 3   INN00003                2                  1                        1
## 4   INN00004                1                  0                        0
## 5   INN00005                1                  0                        1
## 6   INN00006                1                  0                        0
##   number.of.week.nights type.of.meal car.parking.space   room.type lead.time
## 1                     5  Meal Plan 1                 0 Room_Type 1       224
## 2                     3 Not Selected                 0 Room_Type 1         5
## 3                     3  Meal Plan 1                 0 Room_Type 1         1
## 4                     2  Meal Plan 1                 0 Room_Type 1       211
## 5                     2 Not Selected                 0 Room_Type 1        48
## 6                     2  Meal Plan 2                 0 Room_Type 1       346
##   market.segment.type repeated P.C P.not.C average.price special.requests
## 1             Offline        0   0       0         88.00                0
## 2              Online        0   0       0        106.68                1
## 3              Online        0   0       0         50.00                0
## 4              Online        0   0       0        100.00                1
## 5              Online        0   0       0         77.00                0
## 6             Offline        0   0       0        100.00                1
##   date.of.reservation booking.status
## 1           10/2/2015   Not_Canceled
## 2           11/6/2018   Not_Canceled
## 3           2/28/2018       Canceled
## 4           5/20/2017       Canceled
## 5           4/11/2018       Canceled
## 6           9/13/2016       Canceled
dim(hotel)
## [1] 36285    17
str(hotel)
## 'data.frame':    36285 obs. of  17 variables:
##  $ Booking_ID              : chr  "INN00001" "INN00002" "INN00003" "INN00004" ...
##  $ number.of.adults        : int  1 1 2 1 1 1 1 3 1 2 ...
##  $ number.of.children      : int  1 0 1 0 0 0 1 0 1 0 ...
##  $ number.of.weekend.nights: int  2 1 1 0 1 0 1 1 0 0 ...
##  $ number.of.week.nights   : int  5 3 3 2 2 2 4 3 4 5 ...
##  $ type.of.meal            : chr  "Meal Plan 1" "Not Selected" "Meal Plan 1" "Meal Plan 1" ...
##  $ car.parking.space       : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ room.type               : chr  "Room_Type 1" "Room_Type 1" "Room_Type 1" "Room_Type 1" ...
##  $ lead.time               : int  224 5 1 211 48 346 34 83 121 44 ...
##  $ market.segment.type     : chr  "Offline" "Online" "Online" "Online" ...
##  $ repeated                : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ P.C                     : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ P.not.C                 : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ average.price           : num  88 107 50 100 77 ...
##  $ special.requests        : int  0 1 0 1 0 1 1 1 1 3 ...
##  $ date.of.reservation     : chr  "10/2/2015" "11/6/2018" "2/28/2018" "5/20/2017" ...
##  $ booking.status          : chr  "Not_Canceled" "Not_Canceled" "Canceled" "Canceled" ...
summary(hotel)
##      Booking_ID    number.of.adults number.of.children number.of.weekend.nights
##  Length   :36285   Min.   :0.000    Min.   : 0.0000    Min.   :0.0000          
##  N.unique :36285   1st Qu.:2.000    1st Qu.: 0.0000    1st Qu.:0.0000          
##  N.blank  :    0   Median :2.000    Median : 0.0000    Median :1.0000          
##  Min.nchar:    8   Mean   :1.845    Mean   : 0.1054    Mean   :0.8107          
##  Max.nchar:    8   3rd Qu.:2.000    3rd Qu.: 0.0000    3rd Qu.:2.0000          
##                    Max.   :4.000    Max.   :10.0000    Max.   :7.0000          
##  number.of.week.nights    type.of.meal   car.parking.space     room.type    
##  Min.   : 0.000        Length   :36285   Min.   :0.00000   Length   :36285  
##  1st Qu.: 1.000        N.unique :    4   1st Qu.:0.00000   N.unique :    7  
##  Median : 2.000        N.blank  :    0   Median :0.00000   N.blank  :    0  
##  Mean   : 2.205        Min.nchar:   11   Mean   :0.03098   Min.nchar:   11  
##  3rd Qu.: 3.000        Max.nchar:   12   3rd Qu.:0.00000   Max.nchar:   11  
##  Max.   :17.000                          Max.   :1.00000                    
##    lead.time      market.segment.type    repeated            P.C          
##  Min.   :  0.00   Length   :36285     Min.   :0.00000   Min.   : 0.00000  
##  1st Qu.: 17.00   N.unique :    5     1st Qu.:0.00000   1st Qu.: 0.00000  
##  Median : 57.00   N.blank  :    0     Median :0.00000   Median : 0.00000  
##  Mean   : 85.24   Min.nchar:    6     Mean   :0.02563   Mean   : 0.02334  
##  3rd Qu.:126.00   Max.nchar:   13     3rd Qu.:0.00000   3rd Qu.: 0.00000  
##  Max.   :443.00                       Max.   :1.00000   Max.   :13.00000  
##     P.not.C        average.price    special.requests date.of.reservation
##  Min.   : 0.0000   Min.   :  0.00   Min.   :0.0000   Length   :36285    
##  1st Qu.: 0.0000   1st Qu.: 80.30   1st Qu.:0.0000   N.unique :  553    
##  Median : 0.0000   Median : 99.45   Median :0.0000   N.blank  :    0    
##  Mean   : 0.1534   Mean   :103.42   Mean   :0.6197   Min.nchar:    8    
##  3rd Qu.: 0.0000   3rd Qu.:120.00   3rd Qu.:1.0000   Max.nchar:   10    
##  Max.   :58.0000   Max.   :540.00   Max.   :5.0000                      
##    booking.status 
##  Length   :36285  
##  N.unique :    2  
##  N.blank  :    0  
##  Min.nchar:    8  
##  Max.nchar:   12  
## 
#Data observations
table(hotel$booking.status)
## 
##     Canceled Not_Canceled 
##        11889        24396
prop.table(table(hotel$booking.status))
## 
##     Canceled Not_Canceled 
##    0.3276561    0.6723439
hotel_clean <- hotel %>%
  select(
    booking.status,
    lead.time,
    average.price,
    special.requests,
    repeated,
    P.C,
  ) %>%
  filter(
  !is.na(booking.status),
  !is.na(lead.time),
  !is.na(average.price),
  !is.na(special.requests),
  !is.na(P.C)
  ) %>%
  mutate(
    booking.status = factor(
      booking.status, 
      levels = c("Not_Canceled", "Canceled")
    ),
    repeated = factor(repeated,
                      levels = c(0,1),
                      labels = c("No", "Yes")
    )
  )
    

str(hotel_clean)
## 'data.frame':    36285 obs. of  6 variables:
##  $ booking.status  : Factor w/ 2 levels "Not_Canceled",..: 1 1 2 2 2 2 1 1 1 1 ...
##  $ lead.time       : int  224 5 1 211 48 346 34 83 121 44 ...
##  $ average.price   : num  88 107 50 100 77 ...
##  $ special.requests: int  0 1 0 1 0 1 1 1 1 3 ...
##  $ repeated        : Factor w/ 2 levels "No","Yes": 1 1 1 1 1 1 1 1 1 1 ...
##  $ P.C             : int  0 0 0 0 0 0 0 0 0 0 ...
#Selecting desired variables, removing missing observations, changes variables into appropriate factors 

hotel_clean %>%
  group_by(booking.status) %>%
  
  summarise(
    bookings = n(),
    mean_leadtime = mean(lead.time),
    mean_roomprice = mean(average.price),
    mean_specialrequests = mean(special.requests),
    mean_previouscancellations = mean(P.C)
  )
## # A tibble: 2 × 6
##   booking.status bookings mean_leadtime mean_roomprice mean_specialrequests
##   <fct>             <int>         <dbl>          <dbl>                <dbl>
## 1 Not_Canceled      24396          58.9           99.9                0.759
## 2 Canceled          11889         139.           111.                 0.335
## # ℹ 1 more variable: mean_previouscancellations <dbl>
#Visualization of cancellation counts 

ggplot(
  hotel_clean,
  aes(x = booking.status)
) +
  geom_bar() +
  labs(
    title = "Hotel Reservations by Booking Status",
    x = "Booking Status",
    y = "Number of Reservations"
  ) + 
  theme_minimal()

#Lead time by booking status, comparing canceled and non-canceled
ggplot(
  hotel_clean,
  aes(x = booking.status, y = lead.time)
) + 
  geom_boxplot() +
  labs(
    title = "Lead Time by Booking Status",
    x = "Booking Status",
    y = "Lead Time (Days)"
  ) +
  theme_minimal()

# Cancellation Status by the Number of requests, comparing proportion of both
ggplot(
  hotel_clean,
  aes(
    x = factor(special.requests),
    fill = booking.status
  )
) + 
  geom_bar(position = "fill") +
  labs(
    title = "Cancellation Status by Number of Special Requests",
    x = "Number of Special Requests",
    y = "Proportion",
    fill = "Booking Status"
  ) +
  theme_minimal()

# logistic model, resulting p-values demonstrate statistically significant results 
hotel_model <- glm(
  booking.status ~
    lead.time +
    average.price +
    special.requests +
    repeated +
    P.C,
  data = hotel_clean,
  family = "binomial"
)

summary(hotel_model)
## 
## Call:
## glm(formula = booking.status ~ lead.time + average.price + special.requests + 
##     repeated + P.C, family = "binomial", data = hotel_clean)
## 
## Coefficients:
##                    Estimate Std. Error z value Pr(>|z|)    
## (Intercept)      -3.3276294  0.0528570 -62.955  < 2e-16 ***
## lead.time         0.0127696  0.0001770  72.156  < 2e-16 ***
## average.price     0.0187913  0.0004284  43.868  < 2e-16 ***
## special.requests -1.0461837  0.0211591 -49.444  < 2e-16 ***
## repeatedYes      -2.8310306  0.3569922  -7.930 2.19e-15 ***
## P.C               0.2452009  0.0637074   3.849 0.000119 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 45901  on 36284  degrees of freedom
## Residual deviance: 34295  on 36279  degrees of freedom
## AIC: 34307
## 
## Number of Fisher Scoring iterations: 7
#Confidence Intervals, Odds ratios 
confint(hotel_model)
## Waiting for profiling to be done...
##                        2.5 %      97.5 %
## (Intercept)      -3.43162940 -3.22442865
## lead.time         0.01242430  0.01311804
## average.price     0.01795418  0.01963339
## special.requests -1.08784691 -1.00490229
## repeatedYes      -3.59493105 -2.18696962
## P.C               0.11700428  0.36823977
exp(coef(hotel_model))
##      (Intercept)        lead.time    average.price special.requests 
##       0.03587806       1.01285149       1.01896895       0.35127578 
##      repeatedYes              P.C 
##       0.05895207       1.27787800
exp(confint(hotel_model))
## Waiting for profiling to be done...
##                       2.5 %    97.5 %
## (Intercept)      0.03233421 0.0397785
## lead.time        1.01250180 1.0132045
## average.price    1.01811633 1.0198274
## special.requests 0.33694118 0.3660804
## repeatedYes      0.02746258 0.1122564
## P.C              1.12412424 1.4451885
r2 <- 1 -
  (hotel_model$deviance / hotel_model$null.deviance)
r2
## [1] 0.2528464
AIC(hotel_model)
## [1] 34307.08
1 - pchisq(
  hotel_model$null.deviance - hotel_model$deviance,
  df = length(hotel_model$coefficients) - 1
)
## [1] 0
#Confusion Matrix

model_data <- model.frame(hotel_model)

actual_classes <- ifelse(
  model_data$booking.status == "Canceled",
  1,
  0
)

predicted_prob <- hotel_model$fitted.values

predicted_classes <- ifelse(
  predicted_prob >= 0.5,
  1,
  0
)

confusion <- table(
  Predicted = factor(predicted_classes, levels = c(0, 1)),
  Actual = factor(actual_classes, levels = c(0, 1))
)

confusion
##          Actual
## Predicted     0     1
##         0 22021  5684
##         1  2375  6205
TN <- confusion["0", "0"]
FN <- confusion["0", "1"]
FP <- confusion["1", "0"]
TP <- confusion["1", "1"]

accuracy <- (TP + TN) / (TP + TN + FP + FN)
sensitivity <- TP / (TP + FN)
specificity <- TN / (TN + FP)

accuracy
## [1] 0.7778972
sensitivity
## [1] 0.521911
specificity
## [1] 0.902648
library(pROC)

roc_obj <- roc(
  response = model_data$booking.status,
  predictor = predicted_prob,
  levels = c("Not_Canceled", "Canceled"),
  direction = "<"
)

auc_value <- auc(roc_obj)
auc_value
## Area under the curve: 0.8232
plot.roc(
  roc_obj,
  print.auc = TRUE,
  legacy.axes = TRUE,
  xlab = "False Positive Rate (1 - specificity)",
  ylab = "True Positive Rate (sensitivity)"
)