您當前的位置:首頁 > 體育

Kaggle專案:酒店預訂需求(Hotel booking demand)詳細分析

作者:由 GTO 發表于 體育時間:2020-10-24

本次“酒店預訂需求”專案主要透過Python做預處理,分析酒店的房型供給、不同時間段的需求變化、最核心的消費群體、影響退訂的因素,並利用分類演算法建立酒店訂單退訂的預測模型。

整體分析流程如下:

1。提出問題

2。檢視並理解資料

3。資料清洗

4。資料探勘及視覺化

5。資料建模

一、提出問題

該專案為酒店線上預訂業務的研究內容,從酒店運營角度,主要關心酒店的整體線上預訂量如何,退訂量有多少,造成這些退訂的因素有哪些,可以怎麼提升預訂入住率;從客戶體驗角度,主要關心哪些房型的預訂量較高(有所偏愛),客戶的復定率怎麼樣,客戶的預訂入住頻率、消費金額、消費近度怎樣,是否為重要價值客戶,重點跟進維護。

針對這個專案主要圍繞下面三個問題進行分析:

1。哪些房型的入住率最高,找出最受歡迎的房型,最佳化酒店房型分配

2。客戶入住晚數的需求分佈情況怎樣,哪些的需求量最大,並統計出這些使用者偏愛入住的房型

3。使用者為何取消預訂,哪些因素與此最相關,我們如何判斷一個預訂訂單被取消的可能性

二、檢視並理解資料

1.資料集本地連結:

Kaggle專案:酒店預訂需求(Hotel booking demand)詳細分析

hotel_bookings。csv

16。9M

·

百度網盤

不過還是建議去Kaggle官網,搜尋Hotel booking demand下載,也可以圍觀學習大神的分析方法,幫你更上一層

2.資料匯入、理解

#匯入相關的庫,方便後續呼叫

import numpy as np

import pandas as pd

import pandas_profiling as pp

import seaborn as sns

import matplotlib。pyplot as plt

plt。rcParams[“font。sans-serif”] = [“SimHei”]

plt。rcParams[“font。serif”] = [“SimHei”]

%matplotlib inline

#讀取資料

hotel_data = pd。read_csv(r‘C:\Users\LEE\Desktop\資料分析\資料分析資料\酒店預訂\hotel_bookings。csv’)

#檢視資料的基本資訊

hotel_data。info()

結果:

RangeIndex: 119390 entries, 0 to 119389

Data columns (total 32 columns):

# Column Non-Null Count Dtype

——- ———— ———————— ——-

0 hotel 119390 non-null object

1 is_canceled 119390 non-null int64

2 lead_time 119390 non-null int64

3 arrival_date_year 119390 non-null int64

4 arrival_date_month 119390 non-null object

5 arrival_date_week_number 119390 non-null int64

6 arrival_date_day_of_month 119390 non-null int64

7 stays_in_weekend_nights 119390 non-null int64

8 stays_in_week_nights 119390 non-null int64

9 adults 119390 non-null int64

10 children 119386 non-null float64

11 babies 119390 non-null int64

12 meal 119390 non-null object

13 country 118902 non-null object

14 market_segment 119390 non-null object

15 distribution_channel 119390 non-null object

16 is_repeated_guest 119390 non-null int64

17 previous_cancellations 119390 non-null int64

18 previous_bookings_not_canceled 119390 non-null int64

19 reserved_room_type 119390 non-null object

20 assigned_room_type 119390 non-null object

21 booking_changes 119390 non-null int64

22 deposit_type 119390 non-null object

23 agent 103050 non-null float64

24 company 6797 non-null float64

25 days_in_waiting_list 119390 non-null int64

26 customer_type 119390 non-null object

27 adr 119390 non-null float64

28 required_car_parking_spaces 119390 non-null int64

29 total_of_special_requests 119390 non-null int64

30 reservation_status 119390 non-null object

31 reservation_status_date 119390 non-null object

dtypes: float64(4), int64(16), object(12)

memory usage: 29。1+ MB

資料集總計有32個欄位,119389行資料,有比較明顯的缺失值company,另arrival_

date

_等可能需合併,並轉化為日期格式。

#檢視並理解資料

hotel_data。head()

Kaggle專案:酒店預訂需求(Hotel booking demand)詳細分析

#以下整理出了對應欄位的解析

explain = pd。read_excel(r“C:\Users\LEE\Desktop\資料分析\資料分析資料\酒店預訂\酒店預訂欄位理解。xls”)

Kaggle專案:酒店預訂需求(Hotel booking demand)詳細分析

目前該資料集是32個欄位,根據Kaggle對各個欄位的解釋,整理出該表。

三、資料清洗

1.缺失值處理

# 統計缺失值

hotel_data。isnull()。sum()

結果:

hotel 0

is_canceled 0

lead_time 0

arrival_date_year 0

arrival_date_month 0

arrival_date_week_number 0

arrival_date_day_of_month 0

stays_in_weekend_nights 0

stays_in_week_nights 0

adults 0

children 4

babies 0

meal 0

country 488

market_segment 0

distribution_channel 0

is_repeated_guest 0

previous_cancellations 0

previous_bookings_not_canceled 0

reserved_room_type 0

assigned_room_type 0

booking_changes 0

deposit_type 0

agent 16340

company 112593

days_in_waiting_list 0

customer_type 0

adr 0

required_car_parking_spaces 0

total_of_special_requests 0

reservation_status 0

reservation_status_date 0

dtype: int64

#統計缺失率

hotel_data。isnull()。sum()/hotel_data。shape[0]

結果:

hotel 0。000000

is_canceled 0。000000

lead_time 0。000000

arrival_date_year 0。000000

arrival_date_month 0。000000

arrival_date_week_number 0。000000

arrival_date_day_of_month 0。000000

stays_in_weekend_nights 0。000000

stays_in_week_nights 0。000000

adults 0。000000

children 0。000034

babies 0。000000

meal 0。000000

country 0。004087

market_segment 0。000000

distribution_channel 0。000000

is_repeated_guest 0。000000

previous_cancellations 0。000000

previous_bookings_not_canceled 0。000000

reserved_room_type 0。000000

assigned_room_type 0。000000

booking_changes 0。000000

deposit_type 0。000000

agent 0。136862

company 0。943069

days_in_waiting_list 0。000000

customer_type 0。000000

adr 0。000000

required_car_parking_spaces 0。000000

total_of_special_requests 0。000000

reservation_status 0。000000

reservation_status_date 0。000000

dtype: float64

資料的缺失值主要存在於children,country,agent,company4個欄位,缺失最多的是company

第一,children缺失4個,且為數值型變數,所以用中位數填充

第二,country缺失488個,且為類別型變數,所以使用眾數填充

第三,agent缺失16340個,缺失率為13。6%,缺失數量較大,但agent表示預訂的旅行社,且缺失率小於20%,建議保留,並用0填充,表示沒有旅行社ID

第四,company缺失112593個,缺失率為94。3%>80%,不具備資訊價值有效性,所以直接刪除

#復刻樣本資料,不對資料來源做處理

data_new = hotel_data。copy()

# 處理company欄位,直接刪除

data_new。drop(“company”,axis=1,inplace=True)

# 處理children欄位,中位數填充

data_new。children。fillna(data_new。children。median(), inplace=True)

# 處理country欄位,眾數填充

data_new。country。fillna(data_new。country。mode()[0],inplace=True)

# 處理agent欄位,0值填充

data_new。agent。fillna(0, inplace=True)

注意每次做完資料清洗需要對該清洗進行檢驗,此處由於篇幅做省略

2.重複值處理

由於該資料集是酒店的每個預訂記錄,沒有對應主鍵,可能存在重複值,暫不做重複處理

備:

data_new。duplicated()。sum():查詢重複量

data_new[data_new。duplicated()==True]:查詢重複值

若有重複值,可以使用data_

new。drop

_duplicates(),刪除重複行

3.異常值處理

可以看出小孩的入住量、旅行社代理入住的量均不能是浮點數,需要統一規劃為整型資料

# children、agent欄位不可能為浮點數,需修改資料型別

data_new。children = data_new。children。astype(int)

data_new。agent = data_new。agent。astype(int)

meal裡總計有5種餐型,其中Undefined / SC –無餐套餐為一類,需統一替換為SC類

# 根據原資料集介紹,餐飲欄位中的Undefined / SC –無餐套餐為一類

data_new。meal。replace(“Undefined”, “SC”, inplace=True)

資料集中adults,children,babies欄位均為0,即同一訂單下,預訂入住的人數不能為0,需剔除處理

#檢視異常值

data_new。describe()

#刪除異常值的行

zero_guests = list(data_new[“adults”] + data_new[“children”] + data_new[“babies”] == 0)

data_new。drop(data_new。index[zero_guests],inplace=True)

酒店平均每日收費有一個大於5000的離群值,會嚴重影響描述性統計,需刪除離群值

#核實adr變數的離群值情況

sns。boxplot(x=data_new[‘adr’])

#刪除離群值

data_new = data_new[data_new[“adr”]<5000]

四、資料探勘及視覺化

考慮資料集中部分欄位結構冗餘,整體資料量較大,需做統一合併處理

arrival_date_month抵達的月份為英文格式,與抵達的年、日格式不符

#修改arrival_date_month的英文月份為中文月份

import calendar

month = []

for i in data_new。arrival_date_month:

mon = list(calendar。month_name)。index(i)

month。append(mon)

data_new。insert(4,“arrival_month”,month)

arrival_

date

_抵達的年月日,結構單一,建議合併

#增加一列預訂到店的年月日arrival_date

data_new[[“arrival_date_year”,“arrival_month”,“arrival_date_day_of_month”]] = data_new[[“arrival_date_year”,“arrival_month”,“arrival_date_day_of_month”]]。apply(lambda x:x。astype(str))

date = data_new。arrival_date_year。str。cat([data_new。arrival_month,data_new。arrival_date_day_of_month],“。”)

data_new。insert(3,“arrival_date”,date)

統計stays_in_ _nights入住的晚數

#增加一列總住宿晚數stays_nights_total

nums_stays = data_new。stays_in_weekend_nights + data_new。stays_in_week_nights

data_new。insert(9,“stays_nights_total”,nums_stays)

統計每單入住人數adults+children+babies

#增加一列住宿人數number_of_people

nums_peoples = data_new。adults + data_new。children + data_new。babies

data_new。insert(12,“number_of_people”,nums_peoples)

1.指標分析

(1)預定量怎麼樣

#不同酒店的預訂數

book_amount = data_new。groupby(“hotel”)[“is_canceled”]。count()。reset_index()。rename(columns={“is_canceled”:“amount”})

book_amount[“book_rate”] = round(book_amount。amount/book_amount。amount。sum(),4)

book_amount

Kaggle專案:酒店預訂需求(Hotel booking demand)詳細分析

資料集收錄了2015。7。1至2017。8。29期間的所有預定資料,劃分為City Hotel(城市酒店)和Resort Hotel(度假酒店)。在不考慮酒店退訂的情況下,城市酒店的預訂量佔比66。41%,是度假酒店預訂量的2倍

(2)預定入住率怎麼樣

#核實總的預訂入住率

check_in_rate = str(round(data_new。groupby(“is_canceled”)[“is_canceled”]。count()[0]/data_new。shape[0]*100,2))+“%”

canceled_rate = str(round(data_new。groupby(“is_canceled”)[“is_canceled”]。count()[1]/data_new。shape[0]*100,2))+“%”

print(“該資料的總預訂入住率:”,check_in_rate)

print(“相應的總預訂取消率:”,canceled_rate)

結果:

該資料的總預訂入住率: 62。96%

相應的總預訂取消率: 37。04%

統計整體的預訂入住情況,總的預訂入住率還是隻停留在62。96%,取消情況會在後續的影響因素分析中進一步挖掘

#度假酒店預訂入住情況

rh_iscanceled_count = data_new[data_new[“hotel”]==“Resort Hotel”]。groupby(“is_canceled”)[“hotel”]。count()。reset_index()。rename(columns={“hotel”:“amount”})

rh_cancel_data = pd。DataFrame({“hotel”:“度假酒店”,

“is_canceled”:rh_iscanceled_count。is_canceled,

“count”:rh_iscanceled_count。amount,

“iscanceled_rate”:rh_iscanceled_count。amount/rh_iscanceled_count。amount。sum()})

#城市酒店預訂入住情況

ch_iscanceled_count = data_new[data_new[“hotel”]==“City Hotel”]。groupby(“is_canceled”)[“hotel”]。count()。reset_index()。rename(columns={“hotel”:“amount”})

ch_cancel_data = pd。DataFrame({“hotel”:“城市酒店”,

“is_canceled”:ch_iscanceled_count。is_canceled,

“count”:ch_iscanceled_count。amount,

“iscanceled_rate”:ch_iscanceled_count。amount/ch_iscanceled_count。amount。sum()})

#不同酒店型別預訂入住率

iscancel_data = pd。concat([rh_cancel_data,ch_cancel_data],ignore_index=True)

iscancel_data

結果:

hotel is_canceled count iscanceled_rate

0 度假酒店 0 28938 0。722366

1 度假酒店 1 11122 0。277634

2 城市酒店 0 46228 0。582738

3 城市酒店 1 33101 0。417262

城市酒店的總預訂量大,但同時預訂取消率也不低,主要是因為城市酒店的主要使用者群是商務差旅的使用者,往往具有緊急性及未規劃性,酒店的預訂在未規劃及深入瞭解酒店狀態情況下,容易盲目預訂、退訂,所以退訂率高,建議在在渠道平臺增加“附近優選”功能,透過輸入地址,自動篩選推薦附近城市酒店的入住率高、覆住率高、評價高等高品質回饋的城市酒店,一方面能為使用者提供更高效便捷的推薦服務,另一方面也促使平臺渠道最佳化服務內容

(3)老客戶的復定率怎麼樣

#核實總的復定率

reversal_book_rate = str(round(data_new。groupby(“is_repeated_guest”)[“is_repeated_guest”]。count()[1]/data_new。shape[0]*100,2))+“%”

print(“該資料的總復定率:”,reversal_book_rate)

結果:

該資料的總復定率: 3。19%

#不同酒店型別的覆住率

data_rb = data_new。groupby([“hotel”,“is_repeated_guest”])[“hotel”]。count()

print(“城市酒店的復定率:”,data_rb[“City Hotel”,1]/data_rb[“City Hotel”]。sum())

print(“城市酒店的復定率:”,data_rb[“Resort Hotel”,1]/data_rb[“Resort Hotel”]。sum())

結果:

城市酒店的復定率: 0。02561484450831348

城市酒店的復定率: 0。04438342486270594

城市酒店和度假酒店的復定率均不高,在3%左右,均是自然客流帶來的業務,沒有較強的粘性。建議:梳理預訂入住流程及服務質量反饋渠道,改善提升服務質量;同時在預訂分銷渠道(如小程式、小影片、各大軟文平臺、官網)平臺植入相關軟文,增加曝光量,相應的收集分析看官們的資訊抵達率、資訊點選率、優惠參與率、軟文-消費頁面跳轉率、最終消費轉化率等系列資料,歸納分類高價值渠道、高價值客戶,根據活動針對性的提升高價值渠道、高價值客戶的投入佔比。

2.問題分析

#返回酒店列的統計值

data_new[“hotel”]。value_counts()

#劃分城市酒店及度假酒店的資料集

rh = data_new[(data_new[“hotel”]==“Resort Hotel”) & (data_new[“is_canceled”]==0)]

ch = data_new[(data_new[“hotel”]==“City Hotel”) & (data_new[“is_canceled”]==0)]

由於後續均是針對不同酒店進行比較,所以這裡會劃分度假酒店和城市酒店的資料,同時剔除已經取消的訂單記錄

問題1:哪些房型的入住率最高,找出最受歡迎的房型,最佳化酒店房型分配

#統計不同酒店、不同房型的入住情況

rh_room = rh。groupby(“assigned_room_type”)[“hotel”]。count()。reset_index()。rename(columns={“hotel”:“room_counts”})

ch_room = ch。groupby(“assigned_room_type”)[“hotel”]。count()。reset_index()。rename(columns={“hotel”:“room_counts”})

#酒店標識

rh_room[“hotel”] = “度假酒店”

ch_room[“hotel”] = “城市酒店”

#合併

all_room = pd。concat([ch_room,rh_room],ignore_index=True)

Kaggle專案:酒店預訂需求(Hotel booking demand)詳細分析

plt。figure(figsize=(16,8))

sns。barplot(x=“assigned_room_type”,y=“room_counts”,hue=“hotel”,data=all_room,hue_order=[“城市酒店”,“度假酒店”])

plt。title(“不同房型的入住數量”,fontsize=16)

plt。xlabel(“房型”,fontsize=16)

plt。ylabel(“入住數”,fontsize=16)

plt。legend(loc=“upper right”)

plt。show()

Kaggle專案:酒店預訂需求(Hotel booking demand)詳細分析

酒店的A和D房型的預訂入住量均明顯高於其他房型,是後續主推的房型,也是主要的房型最佳化物件;其餘B、C、F、G、H、L房型均沒有太多的預訂入住量,可以適當調整房型佔比,最大化入住率;另度假酒店E房型的的預訂入住量數量也不少,可能是酒店的高階套房(帶觀景臺)

結合時間序列,劃分為四季,統計分析四季不同酒店型別的入住量變化情況

#對月份進行對映,得到對應的季節

season = {“January”:“冬季”,“February”:“春季”,“March”:“春季”,“April”:“春季”,“May”:“夏季”,“June”:“夏季”,“July”:“夏季”,“August”:“秋季”,“September”:“秋季”,“October”:“秋季”,“November”:“冬季”,“December”:“冬季”}

rh[“seasons”] = rh[“arrival_date_month”]。map(season)

ch[“seasons”] = ch[“arrival_date_month”]。map(season)

#結合時序分析不同酒店、房型在四季的入住情況

rh_room_season = rh。groupby([“reserved_room_type”,“seasons”])[“hotel”]。count()。reset_index()。rename(columns={“hotel”:“room_counts”})

ch_room_season = ch。groupby([“reserved_room_type”,“seasons”])[“hotel”]。count()。reset_index()。rename(columns={“hotel”:“room_counts”})

rh_room_season[“hotel”] = “度假酒店”

ch_room_season[“hotel”] = “城市酒店”

all_room_season = pd。concat([ch_room_season,rh_room_season],axis=0,ignore_index=True)。sort_values(“seasons”)

plt。figure(figsize=(30,30))

sns。catplot(x=“reserved_room_type”,y=“room_counts”,hue=“hotel”,col=“seasons”,data=all_room_season,kind=“bar”,col_order=[“春季”,“夏季”,“秋季”,“冬季”],hue_order=[“城市酒店”,“度假酒店”])

Kaggle專案:酒店預訂需求(Hotel booking demand)詳細分析

城市酒店的隨著季節變化比較明顯,春夏秋三季的入住量不斷攀升,在夏季達到高峰,而在冬季的客流量則有所縮減;度假酒店的話,則春夏秋季基本保持平穩,凜冬客流減少

問題2:客戶入住晚數的需求分佈情況怎樣,哪些的需求量最大,並統計出這些使用者偏愛入住的房型

#統計不同酒店型別每一單的入住居住晚數

rh[“total_nights”] = rh[“stays_in_weekend_nights”] + rh[“stays_in_week_nights”]

ch[“total_nights”] = ch[“stays_in_weekend_nights”] + ch[“stays_in_week_nights”]

#統計不同入住晚數的計量

rh_nights_count = rh[“total_nights”]。value_counts()。reset_index()。rename(columns={“index”:“入住晚數”,“total_nights”:“入住計量”})

ch_nights_count = ch[“total_nights”]。value_counts()。reset_index()。rename(columns={“index”:“入住晚數”,“total_nights”:“入住計量”})

rh_nights_count[“hotel”] = “度假酒店”

ch_nights_count[“hotel”] = “城市酒店”

#合併

all_nights_count = pd。concat([rh_nights_count,ch_nights_count],ignore_index=True)

Kaggle專案:酒店預訂需求(Hotel booking demand)詳細分析

plt。figure(figsize=(18,6))

sns。barplot(x=“入住晚數”,y=“入住計量”,hue=“hotel”,data=all_nights_count,hue_order=[“城市酒店”,“度假酒店”])

plt。title(“住房天數分佈”,fontsize=16)

plt。xlabel(“入住晚數”,fontsize=16)

plt。ylabel(“入住晚數計量”,fontsize=16)

plt。legend(loc=“upper right”)

Kaggle專案:酒店預訂需求(Hotel booking demand)詳細分析

根據入住晚數的統計分析,酒店入住晚數大量分佈在1-3晚;同時度假酒店在“7晚預訂”上也比較受歡迎,可能是優惠策略帶來的正面影響

#度假酒店“7晚預訂”的客戶偏愛的房型

rh[rh[“total_nights”]==7]。groupby(“reserved_room_type”)[“hotel”]。count()

結果:

reserved_room_type

A 2112

C 110

D 1248

E 655

F 158

G 113

H 39

Name: hotel, dtype: int64

度假酒店“7晚預訂”的客戶還是比較偏愛A、D、E房型,整體分佈沒有太大變化

問題3:使用者為何取消預訂,哪些因素與此最相關,我們如何判斷一個預訂訂單被取消的可能性

① 從“預訂房型與提供房型是否一樣”考慮,影響退訂的可能性

#預訂房型和給定房型的資料

assigned_data = data_new[[“is_canceled”,“reserved_room_type”,“assigned_room_type”]]

#判斷預訂房型和給定房型是否一致

assigned_data[“equal”] = np。where(assigned_data[“reserved_room_type”]==assigned_data[“assigned_room_type”],1,0)

#統計一致量

assigned_data[“equal”]。value_counts()

結果:

1 104413

0 14796

Name: equal, dtype: int64

在預訂房型和給定房型的差別上,仍有14796單無法滿足客戶入住需求

#取消訂單與房型一致性的關係

assigned_data。groupby([“is_canceled”,“equal”])[“reserved_room_type”]。count()

結果:

is_canceled equal

0 0 13995

1 61016

1 0 801

1 43397

Name: reserved_room_type, dtype: int64

在取消的訂單中,預訂房型與提供房型不一致的數量有802單,佔比取消訂單總數的1%,佔比如此低,所以不是客戶退訂的主要因素

②從“提供的餐型”考慮,影響退訂的可能性

#餐型資料

meal_data = data_new[[“is_canceled”,“meal”]]

#y表示取消訂單,n表示未取消訂單,不同餐型的計量

meal_data_y = meal_data[meal_data[“is_canceled”]==1]。groupby(“meal”)[“meal”]。count()

meal_data_n = meal_data[meal_data[“is_canceled”]==0]。groupby(“meal”)[“meal”]。count()

meal_data_n

結果:

meal

BB 57730

FB 320

HB 9475

SC 7486

Name: meal, dtype: int64

#預訂入住的訂單中,各餐型佔比情況

plt。pie(x=meal_data_n。values,labels=meal_data_n。index,autopct=“%。1f%%”)

plt。title(“預訂入住訂單的餐型佔比”)

Kaggle專案:酒店預訂需求(Hotel booking demand)詳細分析

#退訂的訂單中,各餐型佔比情況

plt。pie(x=meal_data_y。values,labels=meal_data_y。index,autopct=“%。1f%%”)

plt。title(“退訂訂單的餐型佔比”)

Kaggle專案:酒店預訂需求(Hotel booking demand)詳細分析

對比退訂和預訂入住的餐型佔比,基本一致,沒有太大的出入,所以餐型對預訂退訂的影響不大,排除是因提供的餐食而導致的退訂

③從“分銷渠道”考慮,影響退訂的可能性

#分銷渠道資料

channel_data = data_new[[“is_canceled”,“distribution_channel”,“hotel”]]

#取消訂單與分銷渠道的關係

cancel_channel_data = channel_data。groupby([“is_canceled”,“distribution_channel”])[“hotel”]。count()。reset_index()。rename(columns={“hotel”:“is_cancel_counts”})

cancel_channel_data

Kaggle專案:酒店預訂需求(Hotel booking demand)詳細分析

#不同渠道的訂單量

all_channel_data = cancel_channel_data。groupby(“distribution_channel”)[“is_cancel_counts”]。sum()

all_channel_data

結果:

distribution_channel

Corporate 6651

Direct 14611

GDS 193

TA/TO 97749

Undefined 5

Name: is_cancel_counts, dtype: int64

for i in all_channel_data。index:

cancel_num = cancel_channel_data[(cancel_channel_data[“is_canceled”]==1) & (cancel_channel_data[“distribution_channel”]==i)][“is_cancel_counts”]

all_num = all_channel_data[i]

cancel_rate = cancel_num / all_num * 100

print(“%s渠道的退訂佔比:%。1f%%” % (i,cancel_rate))

結果:

Corporate渠道的退訂佔比:22。1%

Direct渠道的退訂佔比:17。5%

GDS渠道的退訂佔比:19。2%

TA/TO渠道的退訂佔比:41。1%

Undefined渠道的退訂佔比:80。0%

對比不同渠道的退訂佔比,發現“TA/TO”渠道的退訂佔比高,預訂入住量及取消量一半一半,可能是渠道分銷獎勵機制問題,導致該渠道的預訂資料有較大的水分,需進一步核實“TA/TO”渠道的分銷流程,及其中存在的問題

④從“預付定金”考慮,影響退訂的可能性

#預付定金資料

deposit_data = data_new[[“is_canceled”,“deposit_type”,“hotel”]]

#取消訂單與不同預付定金的關係

cancel_deposit_data = deposit_data。groupby([“is_canceled”,“deposit_type”])[“hotel”]。count()。reset_index()。rename(columns={“hotel”:“is_cancel_counts”})

cancel_deposit_data

Kaggle專案:酒店預訂需求(Hotel booking demand)詳細分析

根據上表,客戶更偏愛無定金的預訂。但其中仍有大量預付定金的客戶選擇取消訂單,需進一步根據使用者畫像判斷該部分退訂客戶的屬性特徵,亦可結合退訂的時間分佈、位置分佈,分析退訂的原因

⑤從“提前預定的時間”考慮,影響退訂的可能性

#提前預定時長的資料

lead_data = data_new。groupby(“lead_time”)[“is_canceled”]。describe()。sort_values(by=“mean”,ascending=False)

#剔除提前預定時長小於10的資料

lead_data_10 = lead_data[lead_data[“count”]>10]

#提前預定時長

x = lead_data_10。index

#對應取消數(按比例歸納值100以內)

y = round(lead_data_10[“mean”],4)*100

plt。figure(figsize=(12,6))

sns。regplot(x=x,y=y)

plt。title(“提前預定時長對取消的影響”,fontsize=16)

plt。xlabel(“提前預定時長”,fontsize=16)

plt。ylabel(“取消數(%)”,fontsize=16)

Kaggle專案:酒店預訂需求(Hotel booking demand)詳細分析

根據資料分佈情況,整體有隨著“提前預定時長”的增加,“取消數”亦增加的規律,兩者呈現正相關。所以一般越早預定,也越容易取消。對於這部分影響,建議組織人員對預定日期為“淡季的客戶”提前再確認預定入住時間,同時根據優惠策略(提前入住時間越大,優惠越大)釋放讓利訊號,讓客戶主動提前預定入住的時間

⑥其他影響因素

cancel_corr = data_new。corr()[“is_canceled”]。abs()。sort_values(ascending=False)

cancel_corr

結果:

is_canceled 1。000000

lead_time 0。292885

total_of_special_requests 0。234871

required_car_parking_spaces 0。195700

booking_changes 0。144847

previous_cancellations 0。110141

is_repeated_guest 0。083744

adults 0。058180

previous_bookings_not_canceled 0。057365

days_in_waiting_list 0。054303

adr 0。047601

agent 0。046764

number_of_people 0。044826

babies 0。032568

stays_in_week_nights 0。025551

stays_nights_total 0。018565

arrival_date_week_number 0。008327

children 0。004853

stays_in_weekend_nights 0。001313

Name: is_canceled, dtype: float64

一般來說,相關係數大於0。8時,兩者間的相互影響越大

根據上表,影響取消數的主要屬性是:

lead_time:提前預定天數

total_of_special_requests:客戶提出的特殊要求的數量

required_car_parking_spaces:客戶要求的停車位數

booking_changes:對預訂所作的更改/修改的數目

previous_cancellations:客戶在當前預訂前取消的先前預訂數

五、資料建模

由於酒店的服務大多是預訂服務,需提前預約保留預約資訊。如果酒店的預約被取消,則可能存在隱性的問題,這時需要我們提前對退訂的訂單做預測,提前發現存在的問題

from sklearn。feature_extraction import DictVectorizer #標稱型特徵向量化

from sklearn。compose import ColumnTransformer #特徵列處理

from sklearn。pipeline import Pipeline #資料轉換、模型多步驟處理

from sklearn。impute import SimpleImputer #填充缺失值

from sklearn。preprocessing import StandardScaler, OneHotEncoder #標準化、向量化

from sklearn。model_selection import train_test_split, GridSearchCV #劃分訓練集、網路檢索(引數調優)

from sklearn。tree import DecisionTreeClassifier #決策樹

from sklearn。ensemble import RandomForestClassifier #隨機森林

from sklearn。linear_model import LogisticRegression #邏輯迴歸

其中建模的流程主要:

①特徵提取及預處理

②劃分樣本特徵和樣本結果

③劃分訓練集和測試集

④模型搭建並評估

⑤模型引數調優

①特徵提取、向量化、標準化

由於數值型特徵的單位量綱均不一樣,模型擬合時容易偏擬合,所以需要做歸一化處理,統一量綱,並保留資料規律

#數值型特徵標準化過程

num_feature = [“lead_time”,“stays_nights_total”,“stays_in_weekend_nights”,“stays_in_week_nights”,“number_of_people”,“adults”,“children”,“babies”,“is_repeated_guest”,“previous_cancellations”,“previous_bookings_not_canceled”,“booking_changes”,“agent”,“days_in_waiting_list”,“adr”,“required_car_parking_spaces”,“total_of_special_requests”]

num_transformer = Pipeline(steps=[

(‘imputer’, SimpleImputer(strategy=‘median’)),

(‘scaler’, StandardScaler())])

在模型擬合時,模型無法對複雜的標稱型特徵做擬合,所以需要優先對該類資料進行向量化處理,轉化為模型容易辨識的數值型特徵

#標稱型特徵向量化過程

cat_feature = [“hotel”,“meal”,“country”,“market_segment”,“distribution_channel”,“reserved_room_type”,“assigned_room_type”,“deposit_type”,“customer_type”]

cat_transformer = Pipeline(steps=[

(‘imputer’, SimpleImputer(strategy=‘constant’, fill_value=‘missing’)),

(‘onehot’, OneHotEncoder(handle_unknown=‘ignore’))])

注意:由於DictVectorizer只能處理標稱型特徵變數,且處理量有限,不建議使用

數值型變數和標稱型變數同步於一個預處理器。

#數值型變數和標稱型變數分別做標準化、向量化預處理

preprocessor = ColumnTransformer(

transformers=[

(‘num’, num_transformer, num_feature),

(‘cat’, cat_transformer, cat_feature)]) #預處理器

②劃分樣本特徵和樣本結果

feature = num_feature + cat_feature

#樣本特徵

X = data_new。drop(“is_canceled”,axis=1)[feature]

#樣本結果

y = data_new[“is_canceled”]

③劃分訓練集和測試集

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0。2)

④模型搭建並評估

由於電腦配置有限,暫時以決策樹、隨機森林、邏輯迴歸演算法做分類預測

base_models = [(“DT_model”, DecisionTreeClassifier(random_state=42)),

(“RF_model”, RandomForestClassifier(random_state=42,n_jobs=-1)),

(“LR_model”, LogisticRegression(random_state=42,n_jobs=-1))] #模型

for name,model in base_models:

clf = Pipeline(steps=[(‘preprocessor’, preprocessor),

(‘classifier’, model)])

clf。fit(X_train, y_train)

score = clf。score(X_test, y_test)

print(“%s score: %。3f” % (name,score))

結果:

DT_model score: 0。855

RF_model score: 0。889

LR_model score: 0。813

模型選擇了決策樹、隨機森林和邏輯迴歸,分別對模型擬合效果評分,顯然“隨機森林”的評分0。889,擬合效果更好

⑤模型引數調優

引數調優可以使用GridSearchCV,但在引數數量選擇上,不建議太多,否則資料處理量太多,速度會很慢。對應該模型,引數選擇“n_estimators”:決策樹的量;“max_depth”:決策樹的深度(預剪枝);“max_features”:選擇的最大特徵量

rf = RandomForestClassifier()

#引數選擇

param_dict = {“n_estimators”:[100,150,200],“max_depth”:[3,5,8,10,15],“max_features”:[“auto”,“log2”]}

#網路搜尋調優器

rf_model = GridSearchCV(rf,param_grid=param_dict,cv=3)

#模型擬合

CLF = Pipeline(steps=[(‘preprocessor’, preprocessor),

(‘classifier’, rf_model)])

CLF。fit(X_train, y_train)

不同模型引數下,最好的評分及其引數

CLF。best_score_

CLF。best_params_

由於處理速度有限,本次只使用一下引數調優

rf_model = RandomForestClassifier(n_estimators=160,

max_features=0。4,

min_samples_split=2,

n_jobs=-1,

random_state=0)

CLF = Pipeline(steps=[(‘preprocessor’, preprocessor),

(‘classifier’, rf_model)])

CLF。fit(X_train, y_train)

CLF。score(X_test, y_test)

結果:

0。88562201157621

標簽: data  NEW  Hotel  酒店  room