Trong bối cảnh ngành khách sạn ngày càng cạnh tranh, việc ra quyết định dựa trên dữ liệu đã trở thành yếu tố sống còn để tối ưu hóa vận hành, tối đa hóa doanh thu và nâng cao trải nghiệm khách hàng. Để phục vụ cho mục đích nghiên cứu và phân tích các yếu tố ảnh hưởng đến hành vi đặt phòng, bài luận này sử dụng bộ dữ liệu công khai có tên “Hotel Booking Demand”.
Bộ dữ liệu này được cung cấp bởi Nuno Antonio, Ana Almeida, và Luis Nunes trong bài báo khoa học “Hotel booking demand datasets” đăng trên tạp chí Data in Brief, và đã được phổ biến rộng rãi trên các nền tảng khoa học dữ liệu như Kaggle và TidyTuesday. Dữ liệu ghi lại các giao dịch đặt phòng thực tế tại hai khách sạn: một khách sạn nghỉ dưỡng (Resort Hotel) và một khách sạn thành phố (City Hotel), đều tọa lạc tại Bồ Đào Nha.
hotels <- read.csv("D:/R/hotel_bookings.csv/hotel_bookings.csv")
str(hotels)
## 'data.frame': 119390 obs. of 32 variables:
## $ hotel : chr "Resort Hotel" "Resort Hotel" "Resort Hotel" "Resort Hotel" ...
## $ is_canceled : int 0 0 0 0 0 0 0 0 1 1 ...
## $ lead_time : int 342 737 7 13 14 14 0 9 85 75 ...
## $ arrival_date_year : int 2015 2015 2015 2015 2015 2015 2015 2015 2015 2015 ...
## $ arrival_date_month : chr "July" "July" "July" "July" ...
## $ arrival_date_week_number : int 27 27 27 27 27 27 27 27 27 27 ...
## $ arrival_date_day_of_month : int 1 1 1 1 1 1 1 1 1 1 ...
## $ stays_in_weekend_nights : int 0 0 0 0 0 0 0 0 0 0 ...
## $ stays_in_week_nights : int 0 0 1 1 2 2 2 2 3 3 ...
## $ adults : int 2 2 1 1 2 2 2 2 2 2 ...
## $ children : int 0 0 0 0 0 0 0 0 0 0 ...
## $ babies : int 0 0 0 0 0 0 0 0 0 0 ...
## $ meal : chr "BB" "BB" "BB" "BB" ...
## $ country : chr "PRT" "PRT" "GBR" "GBR" ...
## $ market_segment : chr "Direct" "Direct" "Direct" "Corporate" ...
## $ distribution_channel : chr "Direct" "Direct" "Direct" "Corporate" ...
## $ is_repeated_guest : int 0 0 0 0 0 0 0 0 0 0 ...
## $ previous_cancellations : int 0 0 0 0 0 0 0 0 0 0 ...
## $ previous_bookings_not_canceled: int 0 0 0 0 0 0 0 0 0 0 ...
## $ reserved_room_type : chr "C" "C" "A" "A" ...
## $ assigned_room_type : chr "C" "C" "C" "A" ...
## $ booking_changes : int 3 4 0 0 0 0 0 0 0 0 ...
## $ deposit_type : chr "No Deposit" "No Deposit" "No Deposit" "No Deposit" ...
## $ agent : chr "NULL" "NULL" "NULL" "304" ...
## $ company : chr "NULL" "NULL" "NULL" "NULL" ...
## $ days_in_waiting_list : int 0 0 0 0 0 0 0 0 0 0 ...
## $ customer_type : chr "Transient" "Transient" "Transient" "Transient" ...
## $ adr : num 0 0 75 75 98 ...
## $ required_car_parking_spaces : int 0 0 0 0 0 0 0 0 0 0 ...
## $ total_of_special_requests : int 0 0 0 0 1 1 0 1 1 0 ...
## $ reservation_status : chr "Check-Out" "Check-Out" "Check-Out" "Check-Out" ...
## $ reservation_status_date : chr "2015-07-01" "2015-07-01" "2015-07-02" "2015-07-02" ...
dim(hotels)
## [1] 119390 32
Kết quả trên cho thấy bộ dữ liệu có 119390 quan sát và 32 biến.
so_dong_trung_lap <- sum(duplicated(hotels))
print(so_dong_trung_lap)
## [1] 31994
Kết quả cho thấy có 31994 dòng dữ liệu bị trùng lặp.
colSums(is.na(hotels))
## hotel is_canceled
## 0 0
## lead_time arrival_date_year
## 0 0
## arrival_date_month arrival_date_week_number
## 0 0
## arrival_date_day_of_month stays_in_weekend_nights
## 0 0
## stays_in_week_nights adults
## 0 0
## children babies
## 4 0
## meal country
## 0 0
## market_segment distribution_channel
## 0 0
## is_repeated_guest previous_cancellations
## 0 0
## previous_bookings_not_canceled reserved_room_type
## 0 0
## assigned_room_type booking_changes
## 0 0
## deposit_type agent
## 0 0
## company days_in_waiting_list
## 0 0
## customer_type adr
## 0 0
## required_car_parking_spaces total_of_special_requests
## 0 0
## reservation_status reservation_status_date
## 0 0
Kết quả cho thấy cột Children có chứa dữ liệu bị thiếu.
analyzed <- hotels %>%
distinct() %>%
mutate(children = replace_na(children, 0)) %>%
filter(!(adults == 0 & children == 0 & babies == 0), adr > 0)
Kết quả đã loại bỏ các giá trị trùng lặp, thay thế giá trị bị thiếu và loại bỏ những dòng dữ liệu vô lý.
| Tên Biến | Ý Nghĩa | Kiểu Dữ liệu | Ví dụ Giá trị |
|---|---|---|---|
| hotel | Loại khách sạn (Resort Hotel hoặc City Hotel). | character | Resort Hotel, City Hotel |
| lead_time | Số ngày tính từ lúc đặt phòng đến ngày khách nhận phòng. | integer | 7, 13, 14 |
| arrival_date_month | Tháng khách đến nhận phòng. | character | July, August, September |
| country | Quốc gia của khách hàng (định dạng mã ISO 3166-1 alpha-3). | character | GBR, PRT, USA |
| market_segment | Kênh thị trường tạo ra lượt đặt phòng (ví dụ: TA/TO - Đại lý du lịch). | character | Direct, Corporate, Online TA |
| is_canceled | Biến nhị phân cho biết lượt đặt phòng có bị hủy không (1 = Đã hủy, 0 = Không hủy). | integer | 0, 1 |
| adr | Giá phòng trung bình mỗi ngày (Average Daily Rate = Tổng doanh thu phòng / Tổng số đêm ở). | numeric | 75, 98, 107 |
| arrival_date_year | Năm khách đến nhận phòng. | integer | 2015, 2016, 2017 |
| stays_in_weekend_nights | Số đêm khách ở vào các ngày cuối tuần (Thứ 7 hoặc Chủ Nhật). | integer | 0, 1, 2 |
| stays_in_week_nights | Số đêm khách ở vào các ngày trong tuần (Thứ 2 - Thứ 6). | integer | 1, 2, 3 |
| children | Số lượng trẻ em. | integer | 0, 1, 2 |
| babies | Số lượng em bé. | integer | 0, 1, 2 |
| is_repeated_guest | Biến nhị phân cho biết khách có phải là khách đã từng ở trước đây không (1 = Có, 0 = Không). | integer | 0, 1 |
| reserved_room_type | Mã loại phòng đã được đặt. | character | A, C, D |
| deposit_type | Loại tiền đặt cọc (No Deposit: Không cọc; Non Refund: Cọc không hoàn lại; Refundable: Cọc hoàn lại). | character | No Deposit, Refundable, Non Refund |
| required_car_parking_spaces | Số lượng chỗ đỗ xe được khách yêu cầu. | integer | 0, 1, 2 |
| total_of_special_requests | Tổng số các yêu cầu đặc biệt được khách đưa ra (ví dụ: phòng tầng cao, giường phụ). | integer | 0, 1, 3 |
ruirohuyphong <- analyzed %>%
mutate(lead_time_group = case_when(
lead_time <= 7 ~ "Đặt trong 1 tuần",
lead_time <= 90 ~ "Đặt trong 3 tháng",
TRUE ~ "Đặt rất sớm (>3 tháng)"
)) %>%
group_by(lead_time_group) %>%
summarise(ty_le_huy = mean(is_canceled)) %>%
mutate(lead_time_group = factor(lead_time_group, levels = c("Đặt trong 1 tuần", "Đặt trong 3 tháng", "Đặt rất sớm (>3 tháng)"))) %>%
arrange(lead_time_group)
kable(ruirohuyphong)
| lead_time_group | ty_le_huy |
|---|---|
| Đặt trong 1 tuần | 0.0843032 |
| Đặt trong 3 tháng | 0.2948239 |
| Đặt rất sớm (>3 tháng) | 0.3702148 |
Kết quả sẽ cho thấy nhóm “Đặt rất sớm (>3 tháng)” có tỷ lệ hủy phòng cao nhất, và tỷ lệ này giảm dần ở các nhóm đặt gần ngày hơn. Điều này chứng tỏ rằng các cam kết đặt phòng dài hạn có độ chắc chắn thấp. Dựa vào đây, bộ phận kinh doanh có thể thiết kế các loại giá khác nhau: giá linh hoạt (cho phép hủy) cho nhóm đặt gần ngày, và giá tiết kiệm (không hoàn hủy) cho nhóm đặt rất sớm để giảm thiểu rủi ro.
Giả thuyết H₀: Không có mối liên hệ nào giữa nhóm thời gian đặt phòng và việc hủy phòng. Chúng độc lập với nhau.
Công cụ: Kiểm định Chi-bình phương (Chi-squared Test), vì cả hai đều là biến phân loại.
lead_time_data_for_test <- analyzed %>%
mutate(lead_time_group = case_when(
lead_time <= 7 ~ "Trong 1 tuần",
lead_time <= 90 ~ "Trong 3 tháng",
TRUE ~ "Trên 3 tháng"
))
chi_table_lead_time <- table(lead_time_data_for_test$lead_time_group, lead_time_data_for_test$is_canceled)
chisq.test(chi_table_lead_time)
##
## Pearson's Chi-squared test
##
## data: chi_table_lead_time
## X-squared = 4537.6, df = 2, p-value < 2.2e-16
Với p-value cực kỳ nhỏ (< 2.2e-16), chúng ta có bằng chứng rất mạnh để bác bỏ giả thuyết H₀. Điều này khẳng định rằng mối liên hệ giữa thời gian đặt trước và khả năng hủy phòng là có thật và có ý nghĩa thống kê. Do đó, việc doanh nghiệp xây dựng các chính sách quản lý rủi ro khác nhau cho từng nhóm là một quyết định hoàn toàn có cơ sở dữ liệu vững chắc, không phải là một phỏng đoán.
phanbomucgia <- analyzed %>%
filter(adr > 0 & adr < 500) %>%
mutate(price_group = cut(adr,
breaks = c(0, 80, 150, 500),
labels = c("Giá rẻ (<$80)", "Giá trung bình ($80-$150)", "Giá cao (>$150)"))) %>%
count(hotel, price_group)
kable(phanbomucgia)
| hotel | price_group | n |
|---|---|---|
| City Hotel | Giá rẻ (<$80) | 10589 |
| City Hotel | Giá trung bình ($80-$150) | 33589 |
| City Hotel | Giá cao (>$150) | 8141 |
| Resort Hotel | Giá rẻ (<$80) | 16610 |
| Resort Hotel | Giá trung bình ($80-$150) | 9688 |
| Resort Hotel | Giá cao (>$150) | 6966 |
Kết quả có thể cho thấy City Hotel có nhiều đặt phòng ở phân khúc “Giá trung bình” hơn, trong khi Resort Hotel có thể mạnh hơn ở phân khúc “Giá rẻ”. Điều này giúp nhà quản lý hiểu rõ định vị sản phẩm của mình trên thị trường. Nếu muốn tăng doanh thu cho Resort Hotel, họ có thể cần tạo ra các dịch vụ/gói sản phẩm cao cấp hơn để thu hút khách hàng sẵn sàng chi trả.
Giả thuyết H₀: Số đêm ở trung bình của cả ba nhóm giá là như nhau.
Công cụ: Phân tích phương sai (ANOVA), vì chúng ta so sánh trung bình của một biến số trên nhiều hơn hai nhóm.
price_data_for_test <- analyzed %>%
filter(adr > 0 & adr < 500) %>%
mutate(
price_group = cut(adr, breaks = c(0, 80, 150, 500), labels = c("Giá rẻ", "Giá trung bình", "Giá cao")),
total_nights = stays_in_weekend_nights + stays_in_week_nights
) %>%
filter(total_nights > 0)
aov_price_nights <- aov(total_nights ~ price_group, data = price_data_for_test)
summary(aov_price_nights)
## Df Sum Sq Mean Sq F value Pr(>F)
## price_group 2 1237 618.4 83.2 <2e-16 ***
## Residuals 85580 636133 7.4
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Kết quả ANOVA sẽ cho p-value rất nhỏ. Điều này cho phép chúng ta bác bỏ giả thuyết H₀. Mức giá mà khách hàng trả thực sự có liên quan đến thời gian họ ở lại. Sự khác biệt về số đêm ở trung bình mà chúng ta quan sát được giữa các nhóm giá là có ý nghĩa thống kê. Do đó, khách sạn có thể tự tin thiết kế các gói sản phẩm và chiến lược marketing khác nhau nhắm vào từng phân khúc giá, vì hành vi của họ thực sự khác biệt.
hanhvidatphong <- analyzed %>%
mutate(customer_location = ifelse(country == "PRT", "Nội địa (Bồ Đào Nha)", "Quốc tế")) %>%
filter(!is.na(customer_location)) %>%
group_by(customer_location) %>%
summarise(lead_time_trung_binh = mean(lead_time))
kable(hanhvidatphong)
| customer_location | lead_time_trung_binh |
|---|---|
| Nội địa (Bồ Đào Nha) | 66.70629 |
| Quốc tế | 86.84226 |
Thông thường, khách “Quốc tế” sẽ có lead_time_trung_binh cao hơn đáng kể so với khách “Nội địa” vì họ cần nhiều thời gian hơn để lên kế hoạch cho chuyến đi (vé máy bay, visa…). Hiểu được điều này giúp bộ phận marketing phân bổ thời gian cho các chiến dịch quảng cáo: các chiến dịch quốc tế cần được triển khai sớm hơn nhiều tháng, trong khi các chiến dịch nội địa có thể tập trung vào các ưu đãi gần ngày.
Giả thuyết H₀: lead_time_trung_binh của hai nhóm là như nhau.
Công cụ: Kiểm định T (T-test), vì so sánh trung bình giữa hai nhóm.
location_data_for_test <- analyzed %>%
mutate(customer_location = ifelse(country == "PRT", "Nội địa", "Quốc tế")) %>%
filter(!is.na(customer_location))
t.test(lead_time ~ customer_location, data = location_data_for_test) %>% tidy()
## # A tibble: 1 × 10
## estimate estimate1 estimate2 statistic p.value parameter conf.low conf.high
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 -20.1 66.7 86.8 -31.2 5.62e-212 47866. -21.4 -18.9
## # ℹ 2 more variables: method <chr>, alternative <chr>
p-value cực nhỏ cho thấy chúng ta có bằng chứng để bác bỏ H₀. Sự khác biệt về thời gian lập kế hoạch giữa khách nội địa và quốc tế là có thật. Khách quốc tế thực sự cần nhiều thời gian chuẩn bị hơn. Điều này cung cấp một luận cứ vững chắc cho việc bộ phận marketing cần phải triển khai các chiến dịch quảng cáo quốc tế sớm hơn nhiều so với các chiến dịch trong nước để đạt hiệu quả tối ưu.
mucgiatrungbinh <- analyzed %>%
filter(adr > 0) %>%
mutate(segment_group = case_when(
market_segment == "Online TA" ~ "Kênh Online",
market_segment %in% c("Offline TA/TO", "Groups") ~ "Kênh Offline & Đoàn",
market_segment %in% c("Direct", "Corporate") ~ "Kênh Trực tiếp & Doanh nghiệp",
TRUE ~ "Kênh khác"
)) %>%
group_by(segment_group) %>%
summarise(gia_trung_binh = mean(adr)) %>%
arrange(desc(gia_trung_binh))
kable(mucgiatrungbinh)
| segment_group | gia_trung_binh |
|---|---|
| Kênh Online | 119.00709 |
| Kênh Trực tiếp & Doanh nghiệp | 105.85448 |
| Kênh khác | 87.09528 |
| Kênh Offline & Đoàn | 82.05070 |
Kết quả có thể cho thấy nhóm “Kênh Online” mang lại gia_trung_binh cao nhất. Điều này là do khách sạn không phải trả hoa hồng và có thể có các hợp đồng giá tốt với các công ty. Phân tích này củng cố luận điểm rằng việc đầu tư vào website, đội ngũ sale corporate và các chương trình khách hàng thân thiết là chiến lược tối ưu hóa lợi nhuận về lâu dài.
Giả thuyết H₀: Giá phòng trung bình (adr) là như nhau ở tất cả các nhóm kênh bán hàng.
Công cụ: Phân tích phương sai (ANOVA).
segment_data_for_test <- analyzed %>%
filter(adr > 0) %>%
mutate(segment_group = case_when(
market_segment == "Online TA" ~ "Kênh Online",
market_segment %in% c("Offline TA/TO", "Groups") ~ "Kênh Offline & Đoàn",
market_segment %in% c("Direct", "Corporate") ~ "Kênh Trực tiếp & Doanh nghiệp",
TRUE ~ "Kênh khác"
))
aov_segment_adr <- aov(adr ~ segment_group, data = segment_data_for_test)
summary(aov_segment_adr)
## Df Sum Sq Mean Sq F value Pr(>F)
## segment_group 3 18732939 6244313 2374 <2e-16 ***
## Residuals 85582 225079420 2630
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Giá trị p-value trong kết quả ANOVA là cực kỳ nhỏ. Do đó, chúng ta bác bỏ giả thuyết H₀. Kênh bán hàng thực sự có ảnh hưởng đến giá phòng trung bình. Sự chênh lệch về adr mà chúng ta quan sát được giữa các kênh không phải là do may rủi. Điều này chứng tỏ, một cách có hệ thống, các kênh khác nhau thu hút các phân khúc khách hàng có khả năng chi trả khác nhau, cung cấp một luận cứ tài chính vững chắc để ưu tiên đầu tư vào các kênh trực tiếp.
giatridatphong <- analyzed %>%
filter(adr > 0) %>%
mutate(price_group = cut(adr,
breaks = quantile(adr, probs = c(0, 0.25, 0.75, 1)),
labels = c("Giá thấp", "Giá trung bình", "Giá cao"))) %>%
filter(!is.na(price_group)) %>%
group_by(price_group) %>%
summarise(ty_le_huy = mean(is_canceled))
kable(giatridatphong)
| price_group | ty_le_huy |
|---|---|
| Giá thấp | 0.1880374 |
| Giá trung bình | 0.2912317 |
| Giá cao | 0.3445314 |
Kết quả có thể cho thấy nhóm “Giá cao” có tỷ lệ hủy cao hơn. Điều này có thể do các đặt phòng này thường được lên kế hoạch sớm và có nhiều khả năng thay đổi. Hoặc ngược lại, nhóm “Giá thấp” (có thể là các ưu đãi không hoàn hủy) lại có tỷ lệ hủy thấp nhất. Hiểu được mối quan hệ này giúp điều chỉnh chính sách hủy phòng theo từng mức giá.
Giả thuyết H₀: Nhóm giá phòng và việc hủy phòng là hai biến độc lập. Tỷ lệ hủy phòng là như nhau ở tất cả các nhóm giá.
Công cụ: Kiểm định Chi-bình phương (Chi-squared Test), vì cả hai đều là biến phân loại.
price_data_for_test <- analyzed %>%
filter(adr > 0) %>%
mutate(price_group = cut(adr,
breaks = quantile(adr, probs = c(0, 0.25, 0.75, 1), na.rm = TRUE),
labels = c("Giá thấp", "Giá trung bình", "Giá cao"))) %>%
filter(!is.na(price_group))
chi_table_price <- table(price_data_for_test$price_group, price_data_for_test$is_canceled)
chisq.test(chi_table_price)
##
## Pearson's Chi-squared test
##
## data: chi_table_price
## X-squared = 1363.5, df = 2, p-value < 2.2e-16
Kết quả p-value rất nhỏ (< 2.2e-16), cho phép chúng ta bác bỏ giả thuyết H₀. Mức giá mà khách hàng trả thực sự có liên quan đến khả năng họ hủy phòng. Sự khác biệt về tỷ lệ hủy mà chúng ta quan sát được giữa các nhóm giá là có ý nghĩa thống kê và không phải do ngẫu nhiên. Điều này cho phép doanh nghiệp tự tin áp dụng các chính sách hủy phòng khác nhau cho từng phân khúc giá; ví dụ, có thể áp dụng điều khoản chặt chẽ hơn cho các đặt phòng có giá trị cao hoặc các đặt phòng giá rẻ không hoàn hủy.
hoatdongtheomua <- analyzed %>%
mutate(season = case_when(
arrival_date_month %in% c("June", "July", "August") ~ "Mùa hè (Cao điểm)",
arrival_date_month %in% c("December", "January", "February") ~ "Mùa đông (Thấp điểm)",
TRUE ~ "Mùa chuyển tiếp"
)) %>%
count(hotel, season)
kable(hoatdongtheomua)
| hotel | season | n |
|---|---|---|
| City Hotel | Mùa chuyển tiếp | 26174 |
| City Hotel | Mùa hè (Cao điểm) | 17049 |
| City Hotel | Mùa đông (Thấp điểm) | 9098 |
| Resort Hotel | Mùa chuyển tiếp | 15297 |
| Resort Hotel | Mùa hè (Cao điểm) | 11599 |
| Resort Hotel | Mùa đông (Thấp điểm) | 6369 |
Kết quả có thể cho thấy Resort Hotel hoạt động cực kỳ tốt vào “Mùa chuyển tiếp” cũng như “Mùa hè”. Trong khi đó, City Hotel có thể có số lượng đặt phòng ổn định hơn quanh năm (do khách công tác). Phân tích này giúp ban lãnh đạo đưa ra quyết định chiến lược: ví dụ, cần tạo ra các gói sản phẩm/sự kiện đặc biệt (hội nghị, spa) cho Resort Hotel và City Hotel vào mùa đông để thu hút khách và bù đắp cho tính thời vụ.
Giả thuyết H₀: Việc khách hàng chọn loại khách sạn nào không phụ thuộc vào mùa. Hai biến này độc lập với nhau.
Công cụ: Kiểm định Chi-bình phương.
season_data_for_test <- analyzed %>%
mutate(season = case_when(
arrival_date_month %in% c("June", "July", "August") ~ "Mùa hè (Cao điểm)",
arrival_date_month %in% c("December", "January", "February") ~ "Mùa đông (Thấp điểm)",
TRUE ~ "Mùa chuyển tiếp"
))
chi_table_season <- table(season_data_for_test$season, season_data_for_test$hotel)
chisq.test(chi_table_season)
##
## Pearson's Chi-squared test
##
## data: chi_table_season
## X-squared = 134.94, df = 2, p-value < 2.2e-16
Giá trị p-value cực kỳ nhỏ (< 2.2e-16) cho phép chúng ta bác bỏ giả thuyết H₀. Có một mối liên hệ mạnh mẽ và có ý nghĩa thống kê giữa mùa trong năm và nhu cầu đối với từng loại khách sạn. Sự bùng nổ của Resort Hotel và sự ổn định của City Hotel là những quy luật kinh doanh có thật, không phải là sự tình cờ trong dữ liệu. Điều này cung cấp bằng chứng vững chắc để ban lãnh đạo phân bổ ngân sách, nhân sự và các hoạt động marketing một cách có chủ đích theo từng mùa cho từng loại hình khách sạn.
european_countries <- c("PRT", "GBR", "FRA", "ESP", "DEU", "ITA", "BEL", "NLD", "CHE", "AUT", "IRL")
kenhdatphong <- analyzed %>%
filter(!is.na(country)) %>%
mutate(region = ifelse(country %in% european_countries, "Châu Âu", "Ngoài Châu Âu")) %>%
group_by(region) %>%
summarise(ty_le_online_ta = mean(market_segment == "Online TA"))
kable(kenhdatphong)
| region | ty_le_online_ta |
|---|---|
| Châu Âu | 0.5705685 |
| Ngoài Châu Âu | 0.7279489 |
Nhóm “Ngoài Châu Âu” có ty_le_online_ta cao hơn đáng kể, điều đó cho thấy khách hàng từ xa phụ thuộc nhiều vào các nền tảng đặt phòng trực tuyến toàn cầu để tìm kiếm và đặt phòng. Trong khi đó, khách “Châu Âu” có thể quen thuộc hơn với việc đặt trực tiếp hoặc qua các đại lý địa phương. Chiến lược marketing kỹ thuật số (SEO, quảng cáo trên các OTA) nên được ưu tiên cho các thị trường xa.
Giả thuyết H₀: Khu vực địa lý và kênh thị trường là hai biến độc lập. Việc chọn kênh không phụ thuộc vào việc khách đến từ Châu Âu hay không.
Công cụ: Kiểm định Chi-bình phương (Chi-squared Test).
european_countries <- c("PRT", "GBR", "FRA", "ESP", "DEU", "ITA", "BEL", "NLD", "CHE", "AUT", "IRL")
region_data_for_test <- analyzed %>%
filter(!is.na(country)) %>%
mutate(region = ifelse(country %in% european_countries, "Châu Âu", "Ngoài Châu Âu"))
chi_table_region <- table(region_data_for_test$region, region_data_for_test$market_segment)
chisq.test(chi_table_region)
## Warning in chisq.test(chi_table_region): Chi-squared approximation may be
## incorrect
##
## Pearson's Chi-squared test
##
## data: chi_table_region
## X-squared = 1451.8, df = 7, p-value < 2.2e-16
Kết quả p-value rất nhỏ (< 2.2e-16), cho phép chúng ta bác bỏ giả thuyết H₀. Có một mối liên hệ mạnh mẽ và có ý nghĩa thống kê giữa khu vực của khách hàng và kênh đặt phòng họ lựa chọn. Việc khách hàng từ xa (“Ngoài Châu Âu”) phụ thuộc nhiều hơn vào các kênh OTA là một quy luật có thật, không phải ngẫu nhiên. Điều này cung cấp bằng chứng vững chắc để bộ phận marketing xây dựng các chiến lược quảng cáo kỹ thuật số khác nhau, nhắm mục tiêu vào các kênh OTA toàn cầu cho thị trường xa và có thể tập trung vào các kênh trực tiếp hoặc địa phương cho thị trường Châu Âu.
chienluocgiadong <- analyzed %>%
filter(adr > 0) %>%
mutate(lead_time_group = ifelse(lead_time > 90, "Đặt xa (>90 ngày)", "Đặt gần (<=90 ngày)")) %>%
group_by(hotel, lead_time_group) %>%
summarise(gia_trung_binh = mean(adr))
## `summarise()` has grouped output by 'hotel'. You can override using the
## `.groups` argument.
kable(chienluocgiadong)
| hotel | lead_time_group | gia_trung_binh |
|---|---|---|
| City Hotel | Đặt gần (<=90 ngày) | 114.04416 |
| City Hotel | Đặt xa (>90 ngày) | 111.84156 |
| Resort Hotel | Đặt gần (<=90 ngày) | 97.00181 |
| Resort Hotel | Đặt xa (>90 ngày) | 108.36708 |
Kết quả có thể cho thấy ở Resort Hotel, giá trung bình cho nhóm “Đặt xa” cao hơn đáng kể so với “Đặt gần” (khuyến khích đặt sớm). Nhưng ở City Hotel, sự khác biệt này có thể không lớn (do giá phòng phụ thuộc nhiều vào các sự kiện trong thành phố hơn là thời gian đặt trước). Phân tích này giúp bóc tách và đánh giá hiệu quả của chiến lược giá cho từng dòng sản phẩm.
Giả thuyết H₀: Ảnh hưởng của thời gian đặt phòng lên giá là như nhau ở cả hai loại khách sạn (không có sự tương tác).
Công cụ: Phân tích phương sai hai yếu tố (Two-Way ANOVA), để kiểm tra ảnh hưởng của cả hotel, lead_time_group và sự tương tác giữa chúng.
dynamic_price_data_for_test <- analyzed %>%
filter(adr > 0) %>%
mutate(lead_time_group = ifelse(lead_time > 90, "Đặt xa (>90 ngày)", "Đặt gần (<=90 ngày)"))
aov_dynamic_price <- aov(adr ~ hotel * lead_time_group, data = dynamic_price_data_for_test)
summary(aov_dynamic_price)
## Df Sum Sq Mean Sq F value Pr(>F)
## hotel 1 3017505 3017505 1077.16 <2e-16 ***
## lead_time_group 1 195150 195150 69.66 <2e-16 ***
## hotel:lead_time_group 1 854201 854201 304.92 <2e-16 ***
## Residuals 85582 239745502 2801
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Trong bảng kết quả ANOVA, hãy nhìn vào dòng hotel:lead_time_group. Dòng này kiểm tra hiệu ứng tương tác. p-value ở dòng này sẽ rất nhỏ. Chúng ta có bằng chứng thống kê mạnh để nói rằng có một hiệu ứng tương tác đáng kể. Điều này có nghĩa là, chiến lược giá động thực sự được áp dụng khác nhau giữa hai loại khách sạn. Ảnh hưởng của việc đặt sớm/muộn lên giá phòng không giống nhau ở City Hotel và Resort Hotel. Phân tích này xác nhận rằng khách sạn đang triển khai các chiến lược giá phức tạp và có phân khúc, chứ không áp dụng một quy tắc chung.
chitieu <- analyzed %>%
filter(adr > 0) %>%
mutate(price_group = cut(adr,
breaks = quantile(adr, probs = c(0, 0.5, 1)),
labels = c("Nửa giá thấp", "Nửa giá cao"))) %>%
filter(!is.na(price_group)) %>%
group_by(price_group) %>%
summarise(ty_le_khach_lap_lai = mean(is_repeated_guest == 1))
kable(chitieu)
| price_group | ty_le_khach_lap_lai |
|---|---|
| Nửa giá thấp | 0.0558118 |
| Nửa giá cao | 0.0123081 |
Nhóm “Nửa giá thấp” có tỷ lệ khách lặp lại cao hơn, điều đó có thể cho thấy khách hàng trung thành là những người nhạy cảm về giá và quay lại vì các ưu đãi tốt. Trong trường hợp này, các chương trình giảm giá dành riêng cho thành viên sẽ rất hiệu quả.
Giả thuyết H₀: Việc là khách lặp lại không phụ thuộc vào nhóm giá mà khách chi trả. Hai biến này độc lập.
Công cụ: Kiểm định Chi-bình phương.
loyalty_price_data_for_test <- analyzed %>%
filter(adr > 0) %>%
mutate(price_group = cut(adr,
breaks = quantile(adr, probs = c(0, 0.5, 1), na.rm = TRUE),
labels = c("Nửa giá thấp", "Nửa giá cao"))) %>%
filter(!is.na(price_group))
chi_table_loyalty_price <- table(loyalty_price_data_for_test$price_group, loyalty_price_data_for_test$is_repeated_guest)
chisq.test(chi_table_loyalty_price)
##
## Pearson's Chi-squared test with Yates' continuity correction
##
## data: chi_table_loyalty_price
## X-squared = 1227.1, df = 1, p-value < 2.2e-16
Kết quả p-value nhỏ cho phép bác bỏ giả thuyết H₀. Có một mối liên hệ có ý nghĩa thống kê giữa mức giá khách hàng trả và khả năng họ là khách hàng trung thành. Kết quả cho thấy khách lặp lại tập trung nhiều hơn ở “Nửa giá thấp”, điều này chứng tỏ rằng lòng trung thành của tệp khách hàng hiện tại được xây dựng chủ yếu dựa trên yếu tố giá cả hợp lý. Do đó, các chương trình giữ chân khách hàng nên tập trung vào các ưu đãi về giá, giảm giá cho thành viên để duy trì hiệu quả.
ruirohuyphongtheoquy <- analyzed %>%
mutate(quarter = case_when(
arrival_date_month %in% c("January", "February", "March") ~ "Quý 1",
arrival_date_month %in% c("April", "May", "June") ~ "Quý 2",
arrival_date_month %in% c("July", "August", "September") ~ "Quý 3",
TRUE ~ "Quý 4"
)) %>%
group_by(quarter) %>%
summarise(ty_le_huy = mean(is_canceled))
kable(ruirohuyphongtheoquy)
| quarter | ty_le_huy |
|---|---|
| Quý 1 | 0.2369642 |
| Quý 2 | 0.3030790 |
| Quý 3 | 0.3056252 |
| Quý 4 | 0.2433382 |
Kết quả có thể cho thấy Quý 3 (mùa hè, mùa du lịch cao điểm) có tỷ lệ hủy cao hơn. Điều này có thể do khách hàng đặt phòng ở nhiều nơi để giữ chỗ và sau đó hủy bớt khi chốt kế hoạch. Hiểu được quy luật này, khách sạn có thể áp dụng chính sách hủy phòng chặt chẽ hơn trong Quý 3 so với các quý còn lại để bảo vệ doanh thu trong mùa kinh doanh quan trọng nhất.
Giả thuyết H₀: Việc hủy phòng không phụ thuộc vào quý. Tỷ lệ hủy phòng là như nhau ở tất cả các quý.
Công cụ: Kiểm định Chi-bình phương.
quarter_data_for_test <- analyzed %>%
mutate(quarter = case_when(
arrival_date_month %in% c("January", "February", "March") ~ "Quý 1",
arrival_date_month %in% c("April", "May", "June") ~ "Quý 2",
arrival_date_month %in% c("July", "August", "September") ~ "Quý 3",
TRUE ~ "Quý 4"
))
chi_table_quarter <- table(quarter_data_for_test$quarter, quarter_data_for_test$is_canceled)
chisq.test(chi_table_quarter)
##
## Pearson's Chi-squared test
##
## data: chi_table_quarter
## X-squared = 427.09, df = 3, p-value < 2.2e-16
Giá trị p-value rất nhỏ cho phép chúng ta bác bỏ giả thuyết H₀. Rủi ro hủy phòng thực sự có tính thời vụ và thay đổi một cách có hệ thống theo các quý trong năm. Sự gia tăng tỷ lệ hủy phòng trong mùa cao điểm (Quý 3) không phải là hiện tượng ngẫu nhiên. Điều này cung cấp bằng chứng vững chắc để ban lãnh đạo áp dụng các chính sách quản lý rủi ro linh hoạt theo mùa, chẳng hạn như yêu cầu đặt cọc chặt chẽ hơn hoặc áp dụng các điều khoản hủy phòng nghiêm ngặt hơn trong Quý 3 để bảo vệ doanh thu.
lead_time_data <- analyzed %>%
mutate(lead_time_group = case_when(
lead_time <= 7 ~ "Trong 1 tuần",
lead_time <= 90 ~ "Trong 3 tháng",
TRUE ~ "Trên 3 tháng"
))
lead_time_summary <- lead_time_data %>%
group_by(lead_time_group) %>%
summarise(
so_luong = n(),
adr_trung_binh = mean(adr),
ty_le_huy = mean(is_canceled)
)
kable(lead_time_summary, digits = 2)
| lead_time_group | so_luong | adr_trung_binh | ty_le_huy |
|---|---|---|---|
| Trong 1 tuần | 17271 | 96.62 | 0.08 |
| Trong 3 tháng | 38562 | 112.47 | 0.29 |
| Trên 3 tháng | 29753 | 110.43 | 0.37 |
Khách đặt càng sớm thì khả năng hủy càng cao.
Cụ thể, nhóm “Trên 3 tháng” có tỷ lệ hủy 37%, cao gấp 4.6 lần so với nhóm “Trong 1 tuần” (8%).
ADR trung bình tăng theo lead time.
Khách đặt càng sớm thường chọn phòng có giá cao hoặc đặt trong mùa cao điểm: ngày lễ, cuối tuần, dẫn đến giá phòng trung bình mỗi ngày cao hơn ( tăng khoảng 15% so với nhóm đặt cận ngày). Tuy nhiên doanh thu ở nhóm này lại thấp do tỷ lệ hủy cao.
Để tăng doanh thu đối với nhóm khách hàng đặt trước trên 3 tháng, khuyến nghị khách sạn nên áp dụng chính sách đặt cọc hoặc điều khoản hủy chặt hơn.
Phân tích mối quan hệ giữa thời gian đặt phòng và loại khách sạn mà khách lựa chọn (City Hotel hoặc Resort Hotel), nhằm xem liệu khách đặt sớm hay muộn có xu hướng chọn loại khách sạn khác nhau hay không.
lead_time_crosstab <- lead_time_data %>%
count(lead_time_group, hotel) %>%
group_by(lead_time_group) %>%
mutate(ty_le = n / sum(n))
kable(lead_time_crosstab, digits = 2)
| lead_time_group | hotel | n | ty_le |
|---|---|---|---|
| Trong 1 tuần | City Hotel | 9190 | 0.53 |
| Trong 1 tuần | Resort Hotel | 8081 | 0.47 |
| Trong 3 tháng | City Hotel | 25424 | 0.66 |
| Trong 3 tháng | Resort Hotel | 13138 | 0.34 |
| Trên 3 tháng | City Hotel | 17707 | 0.60 |
| Trên 3 tháng | Resort Hotel | 12046 | 0.40 |
Trong bảng phân tích có hai loại hình khách sạn gồm City Hotel- Nằm ở khu vực trung tâm thành phố, gần khu thương mại, văn phòng, bến xe, sân bay. Resort Hotel- Nằm ở khu du lịch nghỉ dưỡng (ven biển, vùng núi, khu sinh thái…). City Hotel chiếm tỷ trọng lớn hơn trong mọi nhóm thời gian đặt phòng, dao động từ 53% đến 66%.
Điều này cho thấy khách hàng có xu hướng chọn City Hotel nhiều hơn, đặc biệt khi đặt sớm (chiếm 66% trong nhóm “Trong 3 tháng”). Lý giải cho điều này là do đối tượng khách là doanh nhân hoặc người đi công tác, họ có lịch trình cố định nên dễ lập kế hoạch sớm.
Resort Hotel có xu hướng được đặt nhiều hơn khi gần ngày đến, thể hiện qua việc tỷ trọng tăng từ 34% lên 47% ở nhóm “Trong 1 tuần” vì Resort thường phục vụ nghỉ dưỡng ngắn hạn, khách thường quyết định sát ngày hơn (đặt cuối tuần, nghỉ ngắn, v.v.).
ggplot(lead_time_summary, aes(x = reorder(lead_time_group, ty_le_huy), y = ty_le_huy, fill = lead_time_group)) +
geom_bar(stat = "identity", show.legend = FALSE) +
geom_text(aes(label = scales::percent(ty_le_huy, accuracy = 1)), vjust = -0.5) +
labs(title = "Tỷ lệ hủy phòng tăng theo thời gian đặt trước",
x = "Nhóm Thời gian Đặt phòng", y = "Tỷ lệ Hủy phòng")
Thời gian đặt phòng trước càng sớm thì tỷ lệ hủy phòng càng cao.
price_data <- analyzed %>%
filter(adr > 0 & adr < 500) %>%
mutate(price_group = cut(adr,
breaks = c(0, 80, 150, 500),
labels = c("Giá rẻ", "Giá trung bình", "Giá cao")))
price_summary <- price_data %>%
group_by(price_group) %>%
summarise(
so_dem_tb = mean(stays_in_weekend_nights + stays_in_week_nights),
lead_time_tb = mean(lead_time)
)
kable(price_summary, digits = 2)
| price_group | so_dem_tb | lead_time_tb |
|---|---|---|
| Giá rẻ | 3.68 | 73.44 |
| Giá trung bình | 3.58 | 87.22 |
| Giá cao | 3.91 | 75.21 |
Phần lớn khách lưu trú ngắn ngày.
Nhóm “Giá cao” có thời gian ở dài nhất, phù hợp với đối tượng nghỉ dưỡng hoặc khách có ngân sách cao, muốn trải nghiệm lâu hơn.
Lead time trung bình (số ngày đặt trước) cao nhất ở nhóm “Giá trung bình”, cho thấy khách ở nhóm này có xu hướng lên kế hoạch sớm, có thể là gia đình hoặc nhóm khách du lịch phổ thông đặt qua đại lý du lịch trực tuyến .
price_crosstab <- price_data %>%
group_by(price_group) %>%
summarise(
so_luong = n(),
ty_le_khach_lap_lai = mean(is_repeated_guest)
)
kable(price_crosstab, digits = 2)
| price_group | so_luong | ty_le_khach_lap_lai |
|---|---|---|
| Giá rẻ | 27199 | 0.08 |
| Giá trung bình | 43277 | 0.02 |
| Giá cao | 15107 | 0.01 |
Nhóm giá rẻ có tỷ lệ khách lặp lại cao nhất (8%), gấp 4 lần nhóm giá trung bình và 8 lần nhóm giá cao.
Điều này cho thấy khách hàng ở phân khúc giá rẻ có xu hướng quay lại cùng khách sạn nhiều hơn.
Nhóm giá trung bình và giá cao có thể là khách du lịch hoặc nghỉ dưỡng ngắn hạn, thường thay đổi điểm đến và khách sạn, nên không hình thành thói quen quay lại.
Nhìn chung, mức giá càng cao thì khách hàng càng ít quay lại, phản ánh sự khác biệt giữa khách nghỉ dưỡng một lần và khách công tác định kỳ.
ggplot(price_data, aes(x = price_group, y = lead_time, fill = price_group)) +
geom_boxplot(show.legend = FALSE) +
labs(title = "Khách trả giá cao hơn có xu hướng đặt phòng sớm hơn",
x = "Nhóm Giá phòng", y = "Số ngày đặt trước (Lead Time)")
location_data <- analyzed %>%
mutate(customer_location = ifelse(country == "PRT", "Nội địa", "Quốc tế")) %>%
filter(!is.na(customer_location))
location_summary <- location_data %>%
group_by(customer_location) %>%
summarise(
lead_time_tb = mean(lead_time),
ty_le_huy = mean(is_canceled)
)
kable(location_summary, digits = 2)
| customer_location | lead_time_tb | ty_le_huy |
|---|---|---|
| Nội địa | 66.71 | 0.37 |
| Quốc tế | 86.84 | 0.24 |
Khách quốc tế có thời gian đặt phòng trung bình dài hơn (86.84 ngày) so với khách nội địa (66.71 ngày). Điều này phản ánh việc khách quốc tế thường lên kế hoạch sớm hơn do cần sắp xếp chuyến bay và lịch trình dài ngày.
Tuy nhiên, tỷ lệ hủy phòng của khách quốc tế (24%) lại thấp hơn so với khách nội địa (37%), cho thấy nhóm khách nội địa dễ thay đổi kế hoạch hoặc đặt cận ngày nên khả năng hủy cao hơn.
location_crosstab <- location_data %>%
count(customer_location, market_segment) %>%
group_by(customer_location) %>%
mutate(ty_le = n / sum(n)) %>%
filter(market_segment %in% c("Online TA", "Direct", "Offline TA/TO"))
kable(location_crosstab, digits = 2)
| customer_location | market_segment | n | ty_le |
|---|---|---|---|
| Nội địa | Direct | 5319 | 0.20 |
| Nội địa | Offline TA/TO | 4440 | 0.17 |
| Nội địa | Online TA | 10848 | 0.42 |
| Quốc tế | Direct | 6253 | 0.10 |
| Quốc tế | Offline TA/TO | 9187 | 0.15 |
| Quốc tế | Online TA | 40397 | 0.68 |
Khách quốc tế chủ yếu đặt qua Online TA (68%), trong khi khách nội địa sử dụng Online TA (42%) và Direct booking (20%) nhiều hơn.
Điều này phản ánh sự khác biệt hành vi: khách quốc tế phụ thuộc nhiều vào nền tảng trực tuyến, còn khách nội địa có xu hướng liên hệ trực tiếp hoặc đặt qua đại lý địa phương.
Tỷ trọng “Offline TA/TO” cũng cao hơn ở khách nội địa (17%), cho thấy thị trường nội địa vẫn duy trì kênh truyền thống nhất định.
location_data %>%
filter(adr > 0) %>%
group_by(customer_location, hotel) %>%
summarise(adr_tb = mean(adr)) %>%
ggplot(aes(x = hotel, y = adr_tb, fill = customer_location)) +
geom_bar(stat = "identity", position = "dodge") +
geom_text(aes(label = round(adr_tb, 1)),
position = position_dodge(width = 0.9), vjust = -0.5) +
labs(title = "Khách quốc tế thường trả giá cao hơn ở cả hai loại khách sạn",
x = "Loại Khách sạn", y = "Giá phòng trung bình (ADR)")
## `summarise()` has grouped output by 'customer_location'. You can override using
## the `.groups` argument.
Biểu đồ cho thấy khách quốc tế có ADR trung bình cao hơn ở cả City Hotel và Resort Hotel.
Điều này hợp lý vì khách quốc tế thường chọn loại phòng cao cấp hoặc kỳ nghỉ dài ngày, trong khi khách nội địa có xu hướng chọn phòng tiêu chuẩn hoặc ngắn ngày. Kết quả khẳng định thị trường quốc tế mang lại giá trị doanh thu cao hơn, là nhóm khách trọng điểm cần được duy trì.
season_data <- analyzed %>%
mutate(season = case_when(
arrival_date_month %in% c("June", "July", "August") ~ "Mùa hè",
arrival_date_month %in% c("December", "January", "February") ~ "Mùa đông",
TRUE ~ "Mùa chuyển tiếp"
))
season_summary <- season_data %>%
group_by(season) %>%
summarise(
so_luong = n(),
adr_tb = mean(adr),
ty_le_huy = mean(is_canceled)
)
kable(season_summary, digits = 2)
| season | so_luong | adr_tb | ty_le_huy |
|---|---|---|---|
| Mùa chuyển tiếp | 41471 | 98.84 | 0.26 |
| Mùa hè | 28648 | 139.30 | 0.32 |
| Mùa đông | 15467 | 77.71 | 0.24 |
Mùa hè là giai đoạn cao điểm, có ADR trung bình cao nhất (139.3€) và tỷ lệ hủy cao (32%), phản ánh áp lực nhu cầu và cạnh tranh giá.
Mùa đông là thời gian thấp điểm, với ADR thấp nhất (77.7€) và tỷ lệ hủy thấp (24%).
Mùa chuyển tiếp duy trì lượng đặt lớn nhất, chiếm hơn 40.000 booking, đóng vai trò “trung hòa” giữa hai mùa cực trị.
season_crosstab <- season_data %>%
filter(country %in% c("PRT", "GBR", "FRA")) %>%
count(season, country)
kable(season_crosstab)
| season | country | n |
|---|---|---|
| Mùa chuyển tiếp | FRA | 4486 |
| Mùa chuyển tiếp | GBR | 5541 |
| Mùa chuyển tiếp | PRT | 11740 |
| Mùa hè | FRA | 2617 |
| Mùa hè | GBR | 3430 |
| Mùa hè | PRT | 8702 |
| Mùa đông | FRA | 1682 |
| Mùa đông | GBR | 1389 |
| Mùa đông | PRT | 5584 |
Khách từ Bồ Đào Nha (PRT) luôn chiếm số lượng lớn nhất trong cả ba mùa, đặc biệt trong mùa chuyển tiếp và mùa hè.
Khách Anh (GBR) và Pháp (FRA) đóng vai trò quan trọng trong mùa hè – mùa du lịch chính của họ.
Cơ cấu này cho thấy nguồn khách chính của khách sạn mang tính khu vực, tập trung ở Tây Âu, và hoạt động mạnh nhất trong mùa du lịch châu Âu.
season_data %>%
count(season, hotel) %>%
ggplot(aes(x = season, y = n, fill = hotel)) +
geom_bar(stat = "identity", position = "dodge") +
geom_text(aes(label = n),
position = position_dodge(width = 0.9), vjust = -0.5, size = 3) +
labs(title = "Resort Hotel hoạt động mạnh vào mùa hè, City Hotel ổn định hơn",
x = "Mùa", y = "Số lượng Đặt phòng")
Biểu đồ cho thấy Resort Hotel hoạt động mạnh nhất trong mùa hè, trong khi City Hotel duy trì ổn định quanh năm.
Điều này phù hợp với đặc thù hoạt động: Resort phụ thuộc vào du lịch nghỉ dưỡng, còn City Hotel phục vụ khách công tác và hội nghị, ít chịu ảnh hưởng bởi mùa vụ.
segment_data <- analyzed %>%
mutate(segment_group = case_when(
market_segment == "Online TA" ~ "Online",
market_segment %in% c("Direct", "Corporate") ~ "Trực tiếp & DN",
TRUE ~ "Kênh khác"
))
segment_summary <- segment_data %>%
group_by(segment_group) %>%
summarise(
ty_le_huy = mean(is_canceled),
ty_le_khach_lap_lai = mean(is_repeated_guest)
)
kable(segment_summary, digits = 3)
| segment_group | ty_le_huy | ty_le_khach_lap_lai |
|---|---|---|
| Kênh khác | 0.182 | 0.018 |
| Online | 0.356 | 0.009 |
| Trực tiếp & DN | 0.141 | 0.135 |
Nhóm Online có tỷ lệ hủy cao nhất (35.6%) và tỷ lệ khách quay lại rất thấp (0.9%), phản ánh rủi ro từ các nền tảng đặt phòng trực tuyến.
Ngược lại, nhóm Trực tiếp & Doanh nghiệp có tỷ lệ hủy thấp nhất (14.1%) và tỷ lệ khách lặp lại cao vượt trội (13.5%) – đây là kênh mang tính ổn định và trung thành cao.
Nhóm Kênh khác (Offline TA/TO) nằm ở mức trung gian về cả hai chỉ số.
segment_crosstab <- segment_data %>%
count(segment_group, deposit_type) %>%
group_by(segment_group) %>%
mutate(ty_le = n / sum(n))
kable(segment_crosstab, digits = 2)
| segment_group | deposit_type | n | ty_le |
|---|---|---|---|
| Kênh khác | No Deposit | 17603 | 0.94 |
| Kênh khác | Non Refund | 946 | 0.05 |
| Kênh khác | Refundable | 84 | 0.00 |
| Online | No Deposit | 51213 | 1.00 |
| Online | Non Refund | 18 | 0.00 |
| Online | Refundable | 14 | 0.00 |
| Trực tiếp & DN | No Deposit | 15625 | 0.99 |
| Trực tiếp & DN | Non Refund | 74 | 0.00 |
| Trực tiếp & DN | Refundable | 9 | 0.00 |
Phần lớn các kênh đều áp dụng “No Deposit”, đặc biệt là Online (100%) và Trực tiếp & DN (99%), cho thấy chính sách hủy linh hoạt phổ biến.
Các loại “Non Refund” và “Refundable” chiếm tỷ lệ rất nhỏ (<1%), chủ yếu xuất hiện ở nhóm “Kênh khác”.
Điều này giải thích phần nào vì sao tỷ lệ hủy ở kênh Online cao – do khách dễ dàng hủy mà không chịu phí.
segment_data %>%
filter(adr > 0) %>%
group_by(segment_group) %>%
summarise(adr_tb = mean(adr)) %>%
ggplot(aes(x = reorder(segment_group, -adr_tb), y = adr_tb, fill = segment_group)) +
geom_bar(stat = "identity", show.legend = FALSE) +
geom_text(aes(label = round(adr_tb, 1)), vjust = -0.5) +
labs(title = "Kênh Trực tiếp & Doanh nghiệp mang lại giá trị cao nhất",
x = "Nhóm Kênh bán hàng", y = "Giá phòng trung bình (ADR)")
Kênh Trực tiếp & Doanh nghiệp có ADR trung bình cao nhất, thể hiện giá trị doanh thu ổn định và khách hàng chất lượng hơn.
Ngược lại, kênh Online tuy mang lại lượng đặt lớn nhưng ADR thấp hơn, do cạnh tranh giá và chính sách chiết khấu của OTA.
Kết quả gợi ý nên tăng cường chính sách ưu đãi riêng cho khách đặt trực tiếp, nhằm giảm phụ thuộc vào OTA.
stay_data <- analyzed %>%
mutate(total_nights = stays_in_weekend_nights + stays_in_week_nights) %>%
filter(total_nights > 0) %>%
mutate(stay_duration_group = case_when(
total_nights <= 2 ~ "Ngắn ngày",
total_nights <= 7 ~ "Trung bình",
TRUE ~ "Dài ngày"
))
stay_summary <- stay_data %>%
group_by(stay_duration_group) %>%
summarise(
adr_tb = mean(adr),
ty_le_huy = mean(is_canceled)
)
kable(stay_summary, digits = 2)
| stay_duration_group | adr_tb | ty_le_huy |
|---|---|---|
| Dài ngày | 108.84 | 0.34 |
| Ngắn ngày | 103.66 | 0.23 |
| Trung bình | 111.77 | 0.31 |
Khách trung bình (3–7 đêm) có ADR cao nhất (111.77€) nhưng tỷ lệ hủy tương đối cao (31%).
Nhóm dài ngày (>7 đêm) có tỷ lệ hủy cao nhất (34%), có thể do họ đặt sớm hơn và dễ thay đổi kế hoạch.
Nhóm ngắn ngày (≤2 đêm) có tỷ lệ hủy thấp nhất (23%), phản ánh hành vi “đặt gần – ở ngắn – ít rủi ro”.
stay_crosstab <- stay_data %>%
group_by(stay_duration_group) %>%
summarise(ty_le_co_yeu_cau = mean(total_of_special_requests > 0))
kable(stay_crosstab, digits = 2)
| stay_duration_group | ty_le_co_yeu_cau |
|---|---|
| Dài ngày | 0.50 |
| Ngắn ngày | 0.46 |
| Trung bình | 0.53 |
Khoảng 50% khách dài ngày và 53% khách trung bình có yêu cầu đặc biệt, cao hơn so với nhóm ngắn ngày (46%).
Điều này cho thấy khách ở lâu hơn thường kỳ vọng trải nghiệm cá nhân hóa (chọn phòng, giường, view, bữa ăn…), là nhóm cần chú trọng dịch vụ hậu mãi.
ggplot(stay_data, aes(x = stay_duration_group, fill = hotel)) +
geom_bar(position = "fill") +
scale_y_continuous(labels = scales::percent) +
labs(title = "Khách ở dài ngày có xu hướng chọn Resort Hotel",
x = "Nhóm Thời gian Lưu trú", y = "Tỷ lệ")
Khoảng 50% khách dài ngày và 53% khách trung bình có yêu cầu đặc biệt, cao hơn so với nhóm ngắn ngày (46%).
Điều này cho thấy khách ở lâu hơn thường kỳ vọng trải nghiệm cá nhân hóa (chọn phòng, giường, view, bữa ăn…), là nhóm cần chú trọng dịch vụ hậu mãi.
request_data <- analyzed %>%
mutate(has_request = ifelse(total_of_special_requests > 0, "Có yêu cầu", "Không yêu cầu"))
request_summary <- request_data %>%
group_by(has_request) %>%
summarise(
lead_time_tb = mean(lead_time),
adr_tb = mean(adr),
ty_le_huy = mean(is_canceled)
)
kable(request_summary, digits = 2)
| has_request | lead_time_tb | adr_tb | ty_le_huy |
|---|---|---|---|
| Có yêu cầu | 82.32 | 115.07 | 0.22 |
| Không yêu cầu | 79.12 | 102.08 | 0.34 |
Khách có yêu cầu đặc biệt có ADR trung bình cao hơn (115.07€) và tỷ lệ hủy thấp hơn (22%) so với nhóm không có yêu cầu (102.08€, 34%).
Điều này cho thấy nhóm khách có tương tác trước thường cam kết cao hơn và sẵn sàng chi trả nhiều hơn để đảm bảo trải nghiệm mong muốn.
request_crosstab <- request_data %>%
group_by(has_request) %>%
summarise(ty_le_khach_lap_lai = mean(is_repeated_guest))
kable(request_crosstab, digits = 3)
| has_request | ty_le_khach_lap_lai |
|---|---|
| Có yêu cầu | 0.030 |
| Không yêu cầu | 0.038 |
Tỷ lệ khách lặp lại giữa hai nhóm không chênh lệch lớn (3.0% vs 3.8%).
Điều này gợi ý rằng yêu cầu đặc biệt không phải yếu tố chính quyết định khách quay lại, mà chủ yếu liên quan đến mức độ hài lòng tổng thể và trải nghiệm sau lưu trú.
ggplot(request_summary, aes(x = has_request, y = ty_le_huy, fill = has_request)) +
geom_bar(stat = "identity", show.legend = FALSE) +
geom_text(aes(label = scales::percent(ty_le_huy, accuracy = 1)), vjust = -0.5) +
scale_y_continuous(labels = scales::percent) +
labs(title = "Khách không có yêu cầu đặc biệt có rủi ro hủy phòng cao hơn nhiều",
x = "", y = "Tỷ lệ Hủy phòng")
Biểu đồ khẳng định rõ: khách không có yêu cầu đặc biệt có tỷ lệ hủy cao hơn đáng kể.
Kết quả củng cố giả thuyết rằng mức độ tương tác và quan tâm của khách trước chuyến đi phản ánh mức độ gắn bó thực sự.
guest_data <- analyzed %>%
mutate(total_guests = adults + children) %>%
filter(total_guests > 0 & total_guests < 5) %>%
mutate(guest_group = case_when(
total_guests == 1 ~ "Một mình",
total_guests == 2 ~ "Cặp đôi",
TRUE ~ "Nhóm/Gia đình"
))
guest_summary <- guest_data %>%
group_by(guest_group) %>%
summarise(
so_dem_tb = mean(stays_in_weekend_nights + stays_in_week_nights),
adr_tb = mean(adr)
)
kable(guest_summary, digits = 2)
| guest_group | so_dem_tb | adr_tb |
|---|---|---|
| Cặp đôi | 3.87 | 103.47 |
| Một mình | 2.74 | 82.87 |
| Nhóm/Gia đình | 3.87 | 159.67 |
Cặp đôi và nhóm/gia đình đều ở trung bình 3.87 đêm, trong khi khách một mình chỉ ở 2.74 đêm.
Tuy nhiên, ADR trung bình của nhóm/gia đình cao nhất (159.67€), gần gấp đôi nhóm cặp đôi.
Điều này phản ánh nhu cầu sử dụng nhiều phòng hoặc dịch vụ bổ sung của nhóm khách đi đông.
guest_crosstab <- guest_data %>%
count(guest_group, reserved_room_type, sort = TRUE) %>%
group_by(guest_group) %>%
slice(1)
kable(guest_crosstab)
| guest_group | reserved_room_type | n |
|---|---|---|
| Cặp đôi | A | 38298 |
| Một mình | A | 13194 |
| Nhóm/Gia đình | D | 4020 |
Cả cặp đôi và khách một mình đều ưu tiên phòng loại A (phòng tiêu chuẩn), trong khi nhóm/gia đình chọn phòng loại D (diện tích lớn hơn).
Điều này phù hợp với nhu cầu về không gian và tiện nghi khi đi nhóm.
ggplot(guest_summary, aes(x = guest_group, y = so_dem_tb, fill = guest_group)) +
geom_bar(stat = "identity", show.legend = FALSE) +
geom_text(aes(label = round(so_dem_tb, 2)), vjust = -0.5) +
labs(title = "Nhóm/Gia đình và cặp đôi có xu hướng ở lại lâu hơn",
x = "Kích thước Nhóm khách", y = "Số đêm ở trung bình")
Biểu đồ cho thấy cặp đôi và nhóm/gia đình có xu hướng ở lâu hơn so với khách đi một mình.
Đây là nhóm mang lại giá trị lưu trú cao, khách sạn nên ưu tiên chương trình ưu đãi dài ngày hoặc combo gia đình.
loyalty_data <- analyzed %>%
mutate(loyalty_group = ifelse(is_repeated_guest == 1, "Khách lặp lại", "Khách mới"))
loyalty_summary <- loyalty_data %>%
group_by(loyalty_group) %>%
summarise(
lead_time_tb = mean(lead_time),
ty_le_huy = mean(is_canceled)
)
kable(loyalty_summary, digits = 2)
| loyalty_group | lead_time_tb | ty_le_huy |
|---|---|---|
| Khách lặp lại | 19.18 | 0.08 |
| Khách mới | 82.89 | 0.29 |
Khách lặp lại có lead_time ngắn hơn đáng kể (19.18 ngày) và tỷ lệ hủy thấp (8%) so với khách mới (82.89 ngày, 29%).
Điều này phản ánh sự chủ động và cam kết cao của khách quen – họ quen quy trình, tin tưởng dịch vụ và ít hủy.
loyalty_crosstab <- loyalty_data %>%
count(loyalty_group, market_segment, sort = TRUE) %>%
group_by(loyalty_group) %>%
mutate(ty_le = n / sum(n)) %>%
filter(market_segment %in% c("Online TA", "Direct", "Corporate"))
kable(loyalty_crosstab, digits = 2)
| loyalty_group | market_segment | n | ty_le |
|---|---|---|---|
| Khách mới | Online TA | 50765 | 0.61 |
| Khách mới | Direct | 10875 | 0.13 |
| Khách mới | Corporate | 2719 | 0.03 |
| Khách lặp lại | Corporate | 1417 | 0.49 |
| Khách lặp lại | Direct | 697 | 0.24 |
| Khách lặp lại | Online TA | 480 | 0.16 |
Khách lặp lại chủ yếu đến từ Corporate (49%) và Direct (24%), trong khi khách mới đến chủ yếu qua Online TA (61%).
Điều này cho thấy kênh trực tiếp và doanh nghiệp là nguồn chính tạo ra khách trung thành, còn kênh Online chỉ phù hợp để thu hút khách mới.
ggplot(loyalty_summary, aes(x = loyalty_group, y = ty_le_huy, fill = loyalty_group)) +
geom_bar(stat = "identity", show.legend = FALSE) +
geom_text(aes(label = scales::percent(ty_le_huy, accuracy = 1)), vjust = -0.5) +
scale_y_continuous(labels = scales::percent) +
labs(title = "Khách lặp lại có tỷ lệ hủy phòng thấp hơn đáng kể",
x = "", y = "Tỷ lệ Hủy phòng")
Biểu đồ khẳng định rõ: khách lặp lại có tỷ lệ hủy phòng thấp hơn nhiều so với khách mới.
Đây là nhóm khách ổn định và đáng tin cậy, cần được duy trì bằng chương trình ưu đãi dành riêng cho khách quen.
deposit_summary <- analyzed %>%
group_by(deposit_type) %>%
summarise(
lead_time_tb = mean(lead_time),
ty_le_huy = mean(is_canceled)
)
kable(deposit_summary, digits = 2)
| deposit_type | lead_time_tb | ty_le_huy |
|---|---|---|
| No Deposit | 79.03 | 0.27 |
| Non Refund | 211.30 | 0.95 |
| Refundable | 145.39 | 0.24 |
Non Refund có tỷ lệ hủy cực cao (95%) và lead_time rất dài (211.3 ngày), phản ánh nhóm khách đặt sớm nhưng thiếu cam kết nếu chính sách không được thực thi chặt chẽ. Ngược lại, nhóm Refundable có tỷ lệ hủy thấp nhất (24%) và No Deposit ở mức trung bình (27%).
Điều này cho thấy điều khoản đặt cọc ảnh hưởng mạnh đến hành vi hủy phòng.
deposit_crosstab <- analyzed %>%
count(deposit_type, hotel) %>%
group_by(deposit_type) %>%
mutate(ty_le = n / sum(n))
kable(deposit_crosstab, digits = 2)
| deposit_type | hotel | n | ty_le |
|---|---|---|---|
| No Deposit | City Hotel | 51461 | 0.61 |
| No Deposit | Resort Hotel | 32980 | 0.39 |
| Non Refund | City Hotel | 845 | 0.81 |
| Non Refund | Resort Hotel | 193 | 0.19 |
| Refundable | City Hotel | 15 | 0.14 |
| Refundable | Resort Hotel | 92 | 0.86 |
City Hotel chiếm phần lớn trong nhóm “No Deposit” và “Non Refund”, trong khi Resort Hotel chiếm tới 86% trong nhóm Refundable.
Điều này hợp lý vì Resort thường áp dụng chính sách hoàn tiền linh hoạt để thu hút khách du lịch, trong khi City Hotel hướng đến khách công tác – ít hoàn hủy.
ggplot(deposit_summary, aes(x = reorder(deposit_type, ty_le_huy), y = ty_le_huy, fill = deposit_type)) +
geom_bar(stat = "identity", show.legend = FALSE) +
geom_text(aes(label = scales::percent(ty_le_huy, accuracy = 1)), vjust = -0.5) +
scale_y_continuous(labels = scales::percent) +
labs(title = "Chính sách 'Non Refund' gần như loại bỏ hoàn toàn việc hủy phòng",
x = "Loại tiền Đặt cọc", y = "Tỷ lệ Hủy phòng")
Biểu đồ cho thấy “Non Refund” gần như loại bỏ hoàn toàn việc hủy phòng, khẳng định hiệu quả của chính sách không hoàn tiền.
Tuy nhiên, để tránh rủi ro mất khách, khách sạn nên kết hợp chính sách Non Refund với ưu đãi giá sớm (early-bird) nhằm cân bằng giữa doanh thu ổn định và trải nghiệm khách hàng.