python项目实战:酒店需求分析(hotel demand booking)

酒店预定需求分析报告

数据来源:https://www.kaggle.com/jessemostipak/hotel-booking-demand
包含了城市酒店与度假酒店的预定信息

目录

字段说明

数据处理

酒店自身运营情况

总营业额
  • 按年
  • 按月
人均每晚房价比较
  • 月份
  • 房间类型
  • 市场细分
入住率与取消率比较
  • 取消的影响因素
    • 当前预订前取消的先前预订的影响
    • 提前预订的天数
    • 付款方式的不同
    • 预定渠道的不同

客户行为分析

国籍
入住时间(按月)
提前预定时长(按月)
入住时长
餐型选择
预定渠道

1.字段说明

  • hotel:酒店(H1=度假酒店或H2=城市酒店)
  • is_canceled:值,表明预订是否取消(1)或不取消(0)
  • lead_time:输入预订日期至抵达日期之间的天数
  • arrival_date_year:抵达日期
  • arrival_date_month:抵达日期月份
  • arrival_date_week_number:到达日期的年份周数
  • arrival_date_day_of_month:抵达日期
  • stays_in_weekend_nights:周末(星期六或星期天)客人入住或预定入住酒店的次数
  • stays_in_week_nights:每周晚上(星期一至星期五)客人入住或预定入住酒店的次数
  • adults:成人人数
  • children:儿童人数
  • babies:婴儿数量
  • meal:预订的餐型。 类别以标准招待餐包提供:
    • 未定义/SC-无餐包;
    • BB-早餐;
    • HB-早餐和其他一顿饭-通常是晚餐);
    • FB-早餐、午餐和晚餐)
  • country:原籍国。 类别以ISO3155-3:2013格式表示
  • market_segment:市场细分名称。
  • distribution_channel:预订分销渠道。 「TA」一词指「旅行社」,「TO」指「旅游经营者」
  • is_repeated_guest:值,指示预订名称是否来自重复的客人(1)或不(0)
  • previous_cancellations:客户在当前预订前取消的先前预订数
  • previous_bookings_not_canceled:客户在本次预订前未取消的先前预订数
  • reserved_room_type:房间类型的代码保留。 代码是以匿名为由而不是指定的。
  • assigned_room_type:指定预订的房间类型代码。 有时,由于酒店经营的原因,指定的房间类型与预订的房间类型不同(例如。 超额预订)或客户要求。 代码是以匿名为由而不是指定的。
  • booking_changes:从预订在PMS系统中输入之日起至入住或取消之日止,对预订所作的更改/修改的数目
  • deposit_type:说明客户是否存款以保证预订。 这个变量可以假设三类:
    • Non Deposit-无预付保证金;
    • Non Refund-房价全额提前预付,取消不退款;
    • Refundable-部分房价预付,取消可退款。
  • agent:预订的旅行社的身份证
  • company:进行预订的公司/实体的ID或负责支付预订。 以身份证明而不是匿名为由指定
  • days_in_waiting_list:在客户确认预订前,预订在等待名单中的天数
  • customer_type:预订类型,假设四类之一:
    • 合同-当预订有分配或与之相关的其他类型的合同时;
    • 集团-当预订与一个集团相关联时;
    • 短暂-当预订不是一个集团或合同的一部分,并且与其他短暂预订无关时;
    • 短暂-当预订是短暂的,但至少与其他短暂预订有关时
  • ADR:每日平均收费,除以所有住宿交易之和以住宿夜总数
  • required_car_parking_spaces:客户要求的汽车停车位数量
  • total_of_special_requests:客户提出的特殊要求的数量(例如。 双人床或高层)
  • reservation_status:保留最后状态,假设三类之一:
    • 取消-预订被客户取消;
    • 退房-客户已入住,但已离开;
    • 不-展示-客户没有入住,并确实通知酒店为什么
  • reservation_status_date:设置最后状态的日期。 此变量可与预订状态一起使用,以了解预订何时取消或客户何时退房。

2.数据处理

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px
#可视化显示在页面 
%matplotlib inline
#中文字体
plt.rcParams['font.sans-serif']=['SimHei']
#负数正常显示
plt.rcParams['axes.unicode_minus']=False
#忽略警告
import warnings
warnings.filterwarnings('ignore')
#导入数据
df=pd.read_csv('hotel_booking_demand.csv',encoding='gbk')
df.head()

	hotel	is_canceled	lead_time	arrival_date_year	arrival_date_month	arrival_date_week_number	arrival_date_day_of_month	stays_in_weekend_nights	stays_in_week_nights	adults	...	deposit_type	agent	company	days_in_waiting_list	customer_type	adr	required_car_parking_spaces	total_of_special_requests	reservation_status	reservation_status_date
0	Resort Hotel	0	342	2015	July	27	1	0	0	2	...	No Deposit	NaN	NaN	0	Transient	0.0	0	0	Check-Out	2015-07-01
1	Resort Hotel	0	737	2015	July	27	1	0	0	2	...	No Deposit	NaN	NaN	0	Transient	0.0	0	0	Check-Out	2015-07-01
2	Resort Hotel	0	7	2015	July	27	1	0	1	1	...	No Deposit	NaN	NaN	0	Transient	75.0	0	0	Check-Out	2015-07-02
3	Resort Hotel	0	13	2015	July	27	1	0	1	1	...	No Deposit	304.0	NaN	0	Transient	75.0	0	0	Check-Out	2015-07-02
4	Resort Hotel	0	14	2015	July	27	1	0	2	2	...	No Deposit	240.0	NaN	0	Transient	98.0	0	1	Check-Out	2015-07-03

df.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

总共有119389条数据,32个观测指标,存在缺失数据。

###查找缺失数据
df.isnull().sum()[df.isnull().sum()!=0]
children         4
country        488
agent        16340
company     112593
dtype: int64

发现children,country,agent,company中存在缺失数据。

其中children很可能是因为没有儿童入住,所以可以用0填补缺失值;

country则可以用众数取代;

而agent和company缺失值过多,可以删除该观测指标。

#缺失值处理
df['children']=df['children'].fillna(0)
df['country']=df['country'].fillna(value=df.country.mode()[0])
df.drop(['agent'],axis=1,inplace=True)
df.drop(['company'],axis=1,inplace=True)
#检查
df.isnull().sum()[df.isnull().sum()!=0]
Series([], dtype: int64)
#更改数据类型(更改reservation_status_date为日期型)
df['reservation_status_date']=df['reservation_status_date'].astype('datetime64[ns]')
#添加一列为抵达日期'arrival_date',并令其数据类型为日期型
df['arrival_date']=df['arrival_date_year'].map(str)+'/'+df['arrival_date_month'].map(str)+'/'+df['arrival_date_day_of_month'].map(str)
df['arrival_date']=df['arrival_date'].astype('datetime64[ns]')
#查看修改后的数据类型
df.info()

RangeIndex: 119390 entries, 0 to 119389
Data columns (total 31 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                        119390 non-null  float64       
 11  babies                          119390 non-null  int64         
 12  meal                            119390 non-null  object        
 13  country                         119390 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  days_in_waiting_list            119390 non-null  int64         
 24  customer_type                   119390 non-null  object        
 25  adr                             119390 non-null  float64       
 26  required_car_parking_spaces     119390 non-null  int64         
 27  total_of_special_requests       119390 non-null  int64         
 28  reservation_status              119390 non-null  object        
 29  reservation_status_date         119390 non-null  datetime64[ns]
 30  arrival_date                    119390 non-null  datetime64[ns]
dtypes: datetime64[ns](2), float64(2), int64(16), object(11)
memory usage: 28.2+ MB

3.酒店自身运营情况

#不同年份两家酒店总营业额比较
df['total_adr']=(df['stays_in_weekend_nights']+df['stays_in_week_nights'])*df['adr']
df.pivot_table(values='total_adr',index='arrival_date_year',columns='hotel',aggfunc='sum').plot.bar()
plt.show()

python项目实战:酒店需求分析(hotel demand booking)_第1张图片
发现除2015年,城市酒店的年总销售额均比度假酒店高。

#将月份英文转换成数值
df['arrival_date_month']=df['arrival_date_month'].map(
                                                      {'July':7,'August':8,'September':9,'October':10,'November':11,'December':12,
                                                       'January':1,'February':2,'March':3,'April':4,'May':5,'June':6,})
#不同月份两家酒店营业额比较
df.pivot_table(values='total_adr',index='arrival_date_month',columns='hotel',aggfunc='sum').plot.bar()
plt.show()

python项目实战:酒店需求分析(hotel demand booking)_第2张图片
发现,7,8月份度假酒店的月营业额高于城市酒店,其余月份的销售额均是城市酒店高于度假酒店,且两家酒店月营业额走势一致。

#按月份两家酒店日均收费比较
df.pivot_table(values='adr',index='arrival_date_month',columns='hotel',aggfunc='mean').plot()

python项目实战:酒店需求分析(hotel demand booking)_第3张图片
可以看出,度假酒店按月的日均收费变化较大,七八月份的日均收费较高,这可能是其此时月营业额高于城市酒店的原因;

大多数时候城市酒店的日均收费高于度假酒店。

#两家酒店各类型房间日均收费比较
sns.factorplot(x='reserved_room_type',y='adr',hue='hotel',data=df.query('adr<1000'),size=6,kind='box',aspect=1)
plt.title("不同类型房间日均收费", fontsize=20)
plt.xlabel("房间类型", fontsize=16)
plt.ylabel("日均收费", fontsize=16)
plt.show()

python项目实战:酒店需求分析(hotel demand booking)_第4张图片
度假酒店普遍低价位且房间类型较多,价位普遍在100左右,受极小值影响;

城市酒房型消费较度假酒店高,中位数处于中间水平,极值影响小。

#不同市场细分下房间日均收费的比较
sns.factorplot(x='market_segment',y='adr',hue='hotel',data=df.query('adr<400'),size=6,kind='box',aspect=1)
plt.title("不同市场细分下人均每晚价格", fontsize=20)
plt.xlabel("市场细分", fontsize=16)
plt.ylabel("人均每晚价格", fontsize=16)
plt.show()

python项目实战:酒店需求分析(hotel demand booking)_第5张图片
从箱线图可以看出两家酒店大多是从线上预定或者直接现场预订,与其价格相对合理有关,而航空公司的价格高,通过该市场预订的也就少。

#两家酒店预定是否取消
a=df[df['is_canceled']==0].groupby('hotel').is_canceled.count()
b=df[df['is_canceled']==1].groupby('hotel').is_canceled.count()
data=pd.DataFrame({'hotel':a.index,
                 '0':a.values,
                 '1':b.values
                  })
data['未取消比']=data['0']/(data['0']+data['1'])
data['取消比']=data['1']/(data['0']+data['1'])
data
	hotel	            0	    1	 未取消比	     取消比
0	City Hotel	    46228	33102	0.582730	  0.417270
1	Resort Hotel	28938	11122	0.722366      0.277634

相对而言,整体上城市酒店的取消率高于度假酒店。

#当前预订前取消的先前预订对预订取消率的影响
plt.figure(figsize=(10,6))
df1=df.groupby('previous_cancellations')['is_canceled'].describe()
sns.regplot(x=df1.index,y=df1['mean']*100,data=df1,color='g')
plt.title('当前预订前取消的先前预订对预订取消率的影响',fontsize=20)
plt.xlabel('当前预订前取消的先前预订数',fontsize=16)
plt.ylabel('取消率(%)',fontsize=16)
plt.show()

python项目实战:酒店需求分析(hotel demand booking)_第6张图片
可以发现当前预定前取消的先前预定数越多,预订取消率越高。

#提前预订时长对取消的影响
plt.figure(figsize=(10,6))
df2=df.groupby('lead_time')['is_canceled'].describe()
sns.regplot(x=df2.index,y=df2['mean']*100,data=df2,color='g')
plt.title('提前预定时长对取消的影响',fontsize=20)
plt.xlabel('提前预定时长',fontsize=16)
plt.ylabel('取消率(%)',fontsize=16)
plt.show()

python项目实战:酒店需求分析(hotel demand booking)_第7张图片
从散点图可以发现,取消率与提前预订时长有一定的关系,提前预定时长越长,取消率越高。

#不同付款方式对取消率的影响
df3=df.groupby('deposit_type')['is_canceled'].describe()
plt.figure(figsize=(8,6))
sns.barplot(x=df3.index,y=df3['mean']*100,data=df3)
plt.title("不同付款方式对取消率的影响", fontsize=20)
plt.xlabel("付款方式", fontsize=16)
plt.ylabel("取消率(%)", fontsize=16)
plt.show()

python项目实战:酒店需求分析(hotel demand booking)_第8张图片
可以发现,Non Refund付款方式的取消率最高。

#不同预定渠道对取消率的影响
df4=df.groupby('distribution_channel')['is_canceled'].describe()
plt.figure(figsize=(8,6))
sns.barplot(x=df4.index,y=df4['mean']*100,data=df4)
plt.title("不同预定渠道对取消率的影响", fontsize=20)
plt.xlabel("预定渠道", fontsize=16)
plt.ylabel("取消率(%)", fontsize=16)
plt.show()

python项目实战:酒店需求分析(hotel demand booking)_第9张图片
在Undefined渠道下预订,客户的取消率较高。

4.客户行为分析

#客户国籍分布
df5=pd.DataFrame({'客户总数':df['country'].value_counts()})
px.choropleth(df5,
        locations=df5.index,
        color='客户总数',
        hover_name='客户总数',
        color_continuous_scale=px.colors.sequential.Plasma,
        title="客户分布").show()

python项目实战:酒店需求分析(hotel demand booking)_第10张图片
客户主要集中在欧洲地区。

#入住时间(按月)
plt.figure(figsize=(16,5))
plt.subplot(1,2,1)
df.query("hotel=='City Hotel'").arrival_date_month.value_counts().sort_index().plot.bar()
plt.title("城市酒店入住时间(按月)", fontsize=20)
plt.xlabel("月份", fontsize=16)
plt.ylabel("频率", fontsize=16)
plt.subplot(1,2,2)
df.query("hotel=='Resort Hotel'").arrival_date_month.value_counts().sort_index().plot.bar()
plt.title("度假酒店入住时间(按月)", fontsize=20)
plt.xlabel("月份", fontsize=16)
plt.ylabel("频率", fontsize=16)
plt.show()

python项目实战:酒店需求分析(hotel demand booking)_第11张图片
城市酒店4-10月酒店入住的频率变化不大,1、2、11、12月为入住淡季;
度假酒店7、8月份为入住旺季,很可能与此时为旅游旺季有关。

#按月提前预定时长
sns.factorplot(x='arrival_date_month',y='lead_time',hue='hotel',data=df,size=6)
plt.title("提前预定时长(按月)", fontsize=20)
plt.xlabel("月份", fontsize=16)
plt.ylabel("预定时长", fontsize=16)
plt.show()

python项目实战:酒店需求分析(hotel demand booking)_第12张图片
(客户抵达酒店)城市酒店的7、8月份,度假酒店的5、6、9月份客户提前预定的时长较长。

#入住时长
df['total_stay']=df['stays_in_weekend_nights']+df['stays_in_week_nights']
plt.figure(figsize=(12,5))
plt.subplot(1,2,1)
df.query("total_stay<30&hotel=='City Hotel'").total_stay.plot.hist(bins=15,color='r')
plt.title("城市酒店入住时长", fontsize=20)
plt.xlabel("天数(区间)", fontsize=16)
plt.ylabel("频率", fontsize=16)
plt.subplot(1,2,2)
df.query("total_stay<30&hotel=='Resort Hotel'").total_stay.plot.hist(bins=15)
plt.title("度假酒店入住时长", fontsize=18)
plt.xlabel("天数(区间)", fontsize=16)
plt.ylabel("频率", fontsize=16)
plt.show()

python项目实战:酒店需求分析(hotel demand booking)_第13张图片
城市酒店入住时长绝大多数在五天以内;
度假酒店入住时长绝大多是在七天以内。

#餐型选择
plt.figure(figsize=(12,6))
plt.subplot(1,2,1)
plt.pie(df.query("hotel=='City Hotel'").meal.value_counts(),labels=df.query("hotel=='City Hotel'").meal.value_counts().index,autopct='%.2f%%')
plt.title("城市酒店不同餐型选择占比(%)", fontsize=20)
plt.legend()
plt.subplot(1,2,2)
plt.pie(df.query("hotel=='Resort Hotel'").meal.value_counts(),labels=df.query("hotel=='Resort Hotel'").meal.value_counts().index,autopct='%.2f%%')
plt.title("度假酒店不同餐型选择占比(%)", fontsize=20)
plt.legend()
plt.show()

python项目实战:酒店需求分析(hotel demand booking)_第14张图片
城市酒店与度假酒店餐型选择大体一致,大多数选择仅早餐(BB)。

#预定渠道
plt.figure(figsize=(12,6))
plt.subplot(1,2,1)
plt.pie(df.query("hotel=='City Hotel'").distribution_channel.value_counts(),labels=df.query("hotel=='City Hotel'").distribution_channel.value_counts().index,autopct='%.2f%%')
plt.title("城市酒店预订渠道选择占比(%)", fontsize=20)
plt.legend()
plt.subplot(1,2,2)
plt.pie(df.query("hotel=='Resort Hotel'").distribution_channel.value_counts(),labels=df.query("hotel=='Resort Hotel'").distribution_channel.value_counts().index,autopct='%.2f%%')
plt.title("度假酒店预定渠道选择占比(%)", fontsize=20)
plt.legend()
plt.show()

python项目实战:酒店需求分析(hotel demand booking)_第15张图片
城市酒店与度假酒店预订渠道的选择大体一致,通过旅行社和旅游经营者预订的占绝大多数。

你可能感兴趣的:(python项目实战:酒店需求分析(hotel demand booking))