Kaggle專案:酒店預訂需求(Hotel booking demand)詳細分析
本次“酒店預訂需求”專案主要透過Python做預處理,分析酒店的房型供給、不同時間段的需求變化、最核心的消費群體、影響退訂的因素,並利用分類演算法建立酒店訂單退訂的預測模型。
整體分析流程如下:
1。提出問題
2。檢視並理解資料
3。資料清洗
4。資料探勘及視覺化
5。資料建模
一、提出問題
該專案為酒店線上預訂業務的研究內容,從酒店運營角度,主要關心酒店的整體線上預訂量如何,退訂量有多少,造成這些退訂的因素有哪些,可以怎麼提升預訂入住率;從客戶體驗角度,主要關心哪些房型的預訂量較高(有所偏愛),客戶的復定率怎麼樣,客戶的預訂入住頻率、消費金額、消費近度怎樣,是否為重要價值客戶,重點跟進維護。
針對這個專案主要圍繞下面三個問題進行分析:
1。哪些房型的入住率最高,找出最受歡迎的房型,最佳化酒店房型分配
2。客戶入住晚數的需求分佈情況怎樣,哪些的需求量最大,並統計出這些使用者偏愛入住的房型
3。使用者為何取消預訂,哪些因素與此最相關,我們如何判斷一個預訂訂單被取消的可能性
二、檢視並理解資料
1.資料集本地連結:
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()
#以下整理出了對應欄位的解析
explain = pd。read_excel(r“C:\Users\LEE\Desktop\資料分析\資料分析資料\酒店預訂\酒店預訂欄位理解。xls”)
目前該資料集是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
資料集收錄了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)
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()
酒店的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=[“城市酒店”,“度假酒店”])
城市酒店的隨著季節變化比較明顯,春夏秋三季的入住量不斷攀升,在夏季達到高峰,而在冬季的客流量則有所縮減;度假酒店的話,則春夏秋季基本保持平穩,凜冬客流減少
問題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)
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”)
根據入住晚數的統計分析,酒店入住晚數大量分佈在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(“預訂入住訂單的餐型佔比”)
#退訂的訂單中,各餐型佔比情況
plt。pie(x=meal_data_y。values,labels=meal_data_y。index,autopct=“%。1f%%”)
plt。title(“退訂訂單的餐型佔比”)
對比退訂和預訂入住的餐型佔比,基本一致,沒有太大的出入,所以餐型對預訂退訂的影響不大,排除是因提供的餐食而導致的退訂
③從“分銷渠道”考慮,影響退訂的可能性
#分銷渠道資料
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
#不同渠道的訂單量
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
根據上表,客戶更偏愛無定金的預訂。但其中仍有大量預付定金的客戶選擇取消訂單,需進一步根據使用者畫像判斷該部分退訂客戶的屬性特徵,亦可結合退訂的時間分佈、位置分佈,分析退訂的原因
⑤從“提前預定的時間”考慮,影響退訂的可能性
#提前預定時長的資料
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)
根據資料分佈情況,整體有隨著“提前預定時長”的增加,“取消數”亦增加的規律,兩者呈現正相關。所以一般越早預定,也越容易取消。對於這部分影響,建議組織人員對預定日期為“淡季的客戶”提前再確認預定入住時間,同時根據優惠策略(提前入住時間越大,優惠越大)釋放讓利訊號,讓客戶主動提前預定入住的時間
⑥其他影響因素
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
上一篇:如何為狗狗找回食慾?