R語言機器學習預測酒店預訂取消

數聽說明:

本分析報告中的數據來源於R社區的 tidytuesday 項目中的數據。這個項目在每週二會發布一份數據,供數據科學社區分析,並在twitter上相互分享分析結果,交流學習。node

image

業務問題:

對於酒店行業來講,預訂有兩方面的意義:ios

  1. 酒店能夠經過預訂信息,對將來的需求有充分的準備,這是預訂對業務有益的一個方面;
  2. 可是同時,若是以前的預訂產生大量的取消的狀況,則會對酒店業務產生不利的影響,若是大量被預訂的房間被撤銷,不少時候,空餘出來的酒店房間並不能及時被租出去,則會對酒店的利用率產生不利的影響,於是,若是可以對酒店的預訂狀況做出比較準確的預測,對於那些取消風險高的客房,酒店則能夠提早作出準備,避免或下降對業務損失。

數據獲取與概覽:

全部tidytuesday的數據均可以經過兩個方式獲取,能夠安裝tidytueday的R包或者直接經過read_csv從github上直接下載。若是你對以往的歷史數據集感興趣,能夠訪問下面的Tidytuesday at Github.git

1. 數據集下載:

library(tidyverse)
library(skimr)
library(lubridate)
library(stringr)
library(ggthemr)
ggthemr("flat")
hotel_bookings <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2020/2020-02-11/hotels.csv')

咱們如今已經將數據存儲在了hotel_bookings這個DF中,下面咱們能夠經過skimr包中的skim函數對整個數據集有一個初步的把握:github

skim(hotel_bookings)
-- Data Summary ------------------------
                           Values        
Name                       hotel_bookings
Number of rows             119390        
Number of columns          32            
_______________________                  
Column type frequency:                   
  character                13            
  Date                     1             
  numeric                  18            
________________________                 
Group variables            None          

-- Variable type: character ------------------------------------------------------------------------------------------------------
# A tibble: 13 x 8
   skim_variable        n_missing complete_rate   min   max empty n_unique whitespace
 * <chr>                    <int>         <dbl> <int> <int> <int>    <int>      <int>
 1 hotel                        0             1    10    12     0        2          0
 2 arrival_date_month           0             1     3     9     0       12          0
 3 meal                         0             1     2     9     0        5          0
 4 country                      0             1     2     4     0      178          0
 5 market_segment               0             1     6    13     0        8          0
 6 distribution_channel         0             1     3     9     0        5          0
 7 reserved_room_type           0             1     1     1     0       10          0
 8 assigned_room_type           0             1     1     1     0       12          0
 9 deposit_type                 0             1    10    10     0        3          0
10 agent                        0             1     1     4     0      334          0
11 company                      0             1     1     4     0      353          0
12 customer_type                0             1     5    15     0        4          0
13 reservation_status           0             1     7     9     0        3          0

-- Variable type: Date -----------------------------------------------------------------------------------------------------------
# A tibble: 1 x 7
  skim_variable           n_missing complete_rate min        max        median     n_unique
* <chr>                       <int>         <dbl> <date>     <date>     <date>        <int>
1 reservation_status_date         0             1 2014-10-17 2017-09-14 2016-08-07      926

-- Variable type: numeric --------------------------------------------------------------------------------------------------------
# A tibble: 18 x 11
   skim_variable                  n_missing complete_rate       mean       sd      p0    p25    p50   p75  p100 hist 
 * <chr>                              <int>         <dbl>      <dbl>    <dbl>   <dbl>  <dbl>  <dbl> <dbl> <dbl> <chr>
 1 is_canceled                            0          1       0.370     0.483     0       0      0       1     1 ▇▁▁▁▅
 2 lead_time                              0          1     104.      107.        0      18     69     160   737 ▇▂▁▁▁
 3 arrival_date_year                      0          1    2016.        0.707  2015    2016   2016    2017  2017 ▃▁▇▁▆
 4 arrival_date_week_number               0          1      27.2      13.6       1      16     28      38    53 ▅▇▇▇▅
 5 arrival_date_day_of_month              0          1      15.8       8.78      1       8     16      23    31 ▇▇▇▇▆
 6 stays_in_weekend_nights                0          1       0.928     0.999     0       0      1       2    19 ▇▁▁▁▁
 7 stays_in_week_nights                   0          1       2.50      1.91      0       1      2       3    50 ▇▁▁▁▁
 8 adults                                 0          1       1.86      0.579     0       2      2       2    55 ▇▁▁▁▁
 9 children                               4          1.00    0.104     0.399     0       0      0       0    10 ▇▁▁▁▁
10 babies                                 0          1       0.00795   0.0974    0       0      0       0    10 ▇▁▁▁▁
11 is_repeated_guest                      0          1       0.0319    0.176     0       0      0       0     1 ▇▁▁▁▁
12 previous_cancellations                 0          1       0.0871    0.844     0       0      0       0    26 ▇▁▁▁▁
13 previous_bookings_not_canceled         0          1       0.137     1.50      0       0      0       0    72 ▇▁▁▁▁
14 booking_changes                        0          1       0.221     0.652     0       0      0       0    21 ▇▁▁▁▁
15 days_in_waiting_list                   0          1       2.32     17.6       0       0      0       0   391 ▇▁▁▁▁
16 adr                                    0          1     102.       50.5      -6.38   69.3   94.6   126  5400 ▇▁▁▁▁
17 required_car_parking_spaces            0          1       0.0625    0.245     0       0      0       0     8 ▇▁▁▁▁
18 total_of_special_requests              0          1       0.571     0.793     0       0      0       1     5 ▇▁▁▁▁

skim函數的輸出結果來看,這個數據集包含了近12萬條的酒店預訂記錄以及32個變量,其中13個爲文本變量,18個位數值型變量,以及一個日期型變量。數據集比較整潔完整,只有children這個變量中包含了4個缺失值。這對於咱們來講,是一件很是好的事情,能夠節省大量的數據整理的時間。畢竟,在數據科學家中間,有一我的盡皆知的笑話:算法

數據科學家的工做,80%的時間都是用於整理數據,剩下的20%的時間,都用來抱怨數據有多糟糕。

2. 數據的預處理

雖然這個數據集是相對比較整潔的,可是爲了建模的須要,咱們仍是須要對數據進行一些處理。
具體而言,有幾個類別型變量的值過多,須要咱們對其進行整合,不然則會致使咱們的數據模型中每一個類別的樣本量都比較少。具體而言:機器學習

  1. country變量包含了178個不一樣的值,而且分佈很不均勻;
  2. agent變量包含了334個不一樣的值,也是分佈很不均勻;
  3. company變量包含了353個不一樣的值。

tidyverse中包含了能夠對類別型變量進行重組的函數fct_reorder能夠很方便地讓咱們對以上三個變量進行重組:分佈式

hotel_bookings <- hotel_bookings %>% 
  mutate(agent=fct_lump(agent,prop = 0.008))
hotel_bookings <- hotel_bookings %>% 
mutate(company=if_else(company=="NULL","Individual","Corporate"))

除此以外,咱們還有另一些變量須要進行一些處理:函數

hotel_bookings <- hotel_bookings %>% 
  select(is_canceled,everything()) %>% 
  mutate(is_canceled=factor(if_else(is_canceled==1,"Canceled","Not_Canceled"))) %>% 
  select(-reservation_status_date,-arrival_date_year,-arrival_date_day_of_month,-arrival_date_week_number)

hotel_bookings <- hotel_bookings %>% 
  mutate(with_kids=babies+children
  )

hotel_bookings <- hotel_bookings %>% 
  filter(!is.na(with_kids)) %>% 
  select(-babies,-children)

hotel_bookings <- hotel_bookings %>% 
  select(-reservation_status)   ## this is another indicator for reservation, won't use in the model due to data leakage

這裏尤爲要提到的是我在原始數據中刪除了reservation_status這個變量,由於這個變量實際上產生了所謂的data leakage的問題,由於在我第一次的建模過程當中,我發現最終的測試預測準確率達到了100%!!做爲一個有多年預測建模經驗的人,本能的第一反應就是數據中存在信息泄露的問題,實際上這個reservation_status變量和最終咱們要預測的是否取消是等同的。於是,爲了不這樣的問題致使模型沒有最終的實際業務價值,咱們必須將其從原始數據中刪除。學習

3. 數據的不均衡問題診斷:

在一些特定的機器學習問題中,好比銀行違約這類的數據,每每存在不均衡的問題,畢竟違約的是不多數的。而這個不均衡的問題,對於機器學習模型會產生很大的影響。測試

hotel_bookings %>% 
  count(is_canceled,sort = TRUE) %>% 
  mutate(Percent_of_Total=n/sum(n)) %>% 
  kableExtra::kable()

image.png
從數據上看,預定取消的比例大概有40%不到(比我想象的高出很多)。可是另外一方面,也說明咱們沒有太大的class imbalance的問題。

4. 數據類型的轉換

由於咱們須要使用到的是h2o來進行預測建模,所以,咱們須要將原始數據中的全部文本類型的數據轉換爲factor,這很容易經過tidyverse中的mutate_if函數來輕鬆實現。

hotel_bookings <- hotel_bookings %>% 
  mutate_if(is.character,factor)

經過h2o中的automl來進行預測建模:

h2o簡介:

h2o是美國一家公司開發的開源機器學習項目,由斯坦福大學的幾位世界知名的統計學教授做爲技術指導,同時提供了R和Python的包,以超高的預測準確率聞名。具體信息能夠點擊h2o官網

h2o的主要優勢有如下幾個方面:

  1. 爲大數據準備,能夠利用分佈式計算在電腦的多核以及集羣上運行;
  2. 速度快,h2o實際是用Java編寫,彌補了R在計算速度上的缺陷;
  3. API界面統一,學習週期短;
  4. 數據預處理要求比較低,不少數據預處理的工做均可以自動完成。

1.啓動h2o:

library(h2o)
h2o.init(nthreads = 6,max_mem_size = "36g")

h2o的一大優勢就是你能夠本身定義爲本項目能夠利用的計算機處理器和內存的使用量,能夠保證你在運行模型時,不影響其餘電腦正在運行的任務。個人電腦是8核的處理器,有48GB的內存,因此我爲其餘任務保留了必定的計算能力。

Connection successful!

R is connected to the H2O cluster: 
    H2O cluster uptime:         1 hours 6 minutes 
    H2O cluster timezone:       Asia/Shanghai 
    H2O data parsing timezone:  UTC 
    H2O cluster version:        3.28.0.2 
    H2O cluster version age:    1 month and 14 days  
    H2O cluster name:           H2O_started_from_R_chn-fzj_tff946 
    H2O cluster total nodes:    1 
    H2O cluster total memory:   35.80 GB 
    H2O cluster total cores:    8 
    H2O cluster allowed cores:  6 
    H2O cluster healthy:        TRUE 
    H2O Connection ip:          localhost 
    H2O Connection port:        54321 
    H2O Connection proxy:       NA 
    H2O Internal Security:      FALSE 
    H2O API Extensions:         Amazon S3, Algos, AutoML, Core V3, TargetEncoder, Core V4 
    R Version:                  R version 3.6.2 (2019-12-12)

若是你看到和以上相似的信息,就說明你的設置已經成功了。

2.數據框的轉換與分割:

response <- "is_canceled"
predictors <- setdiff(names(hotel_bookings),response)

bookings_h2o <- as.h2o(hotel_bookings)


data_split <- h2o.splitFrame(bookings_h2o,ratios = c(0.9,0.05))

bookings_train <- data_split[[1]]
bookings_valid <- data_split[[2]]
bookings_test <- data_split[[3]]

在使用h2o進行建模以前咱們須要將R中的dataframe轉換成h2o的數據框,而後將咱們的數據分割成訓練集,驗證集和測試集。在本案例中,由於咱們的原始數據量仍是比較大的,於是我是按照90%的訓練集,5%的驗證集和5%的測試集。

automl_model <- h2o.automl(x=predictors,
                           y=response,
                           training_frame = bookings_train,
                           validation_frame = bookings_valid,
                           nfolds = 20,
                           max_runtime_secs = 600,
                           max_models = 20,
                           stopping_metric = "AUC",
                           stopping_tolerance = 0.005,
                           seed = 1234,
                           project_name = "First_AutoML_Model")

我使用的是automl的算法,本質上來講就是一種ensemble的方法,能夠同時隨即森林,GBM等方法訓練出模型並從中選擇出表現最好的模型。
另外,我規定了模型訓練的時間爲600秒,這是一個很是有用的特性,能夠避免咱們平時須要等待幾個小時才能看到模型的結果這樣的情形出現。

3. 模型的表現對比:

前文說過,automl會產生多個不一樣的算法模型,經過提取leaderboard的信息,咱們對模型的結果有一個比較準確的認識:

image.png
從這些模型中,咱們直接選擇出表現最好的模型,並使用這個模型對測試集的數據進行預測,並驗證最終的結果表現,確保沒有過擬合現象的出現:

automl_best <- automl_model@leader

h2o.confusionMatrix(automl_best)

image.png

predictions_with_bestmodel <- h2o.predict(automl_best,bookings_test)

test_df <- as.data.frame(bookings_test)
predictions_with_bestmodel <- as.data.frame(predictions_with_bestmodel)
predicted <- predictions_with_bestmodel %>% 
  pull(predict)
testing_results <- data.frame(
  actual=test_df$is_canceled,
  predictions=predicted
  
)
testing_results %>% 
  count(actual,predictions)

image.png

咱們花費了10分鐘訓練出來的模型的預測準確率達到了88%,這個結果是至關不錯的。

下面還應該作些什麼?

上面的內容只是一個最簡單的案例,在實際業務過程當中,咱們還須要:

  1. 使用更多的模型和參數;
  2. 訓練更長的時間;
  3. 探究模型是否是在某一些方面的預測能力存在問題,可能須要對預測變量進行更多的處理;
  4. 最終,模型完成驗證以後,須要進行部署。
相關文章
相關標籤/搜索