数据来源:https://www.kaggle.com/jessemostipak/hotel-booking-demand
包含了城市酒店与度假酒店的预定信息
1.字段说明
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()
#将月份英文转换成数值
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()
发现,7,8月份度假酒店的月营业额高于城市酒店,其余月份的销售额均是城市酒店高于度假酒店,且两家酒店月营业额走势一致。
#按月份两家酒店日均收费比较
df.pivot_table(values='adr',index='arrival_date_month',columns='hotel',aggfunc='mean').plot()
可以看出,度假酒店按月的日均收费变化较大,七八月份的日均收费较高,这可能是其此时月营业额高于城市酒店的原因;
大多数时候城市酒店的日均收费高于度假酒店。
#两家酒店各类型房间日均收费比较
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()
度假酒店普遍低价位且房间类型较多,价位普遍在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()
从箱线图可以看出两家酒店大多是从线上预定或者直接现场预订,与其价格相对合理有关,而航空公司的价格高,通过该市场预订的也就少。
#两家酒店预定是否取消
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()
#提前预订时长对取消的影响
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()
从散点图可以发现,取消率与提前预订时长有一定的关系,提前预定时长越长,取消率越高。
#不同付款方式对取消率的影响
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()
#不同预定渠道对取消率的影响
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()
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()
#入住时间(按月)
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()
城市酒店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()
(客户抵达酒店)城市酒店的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()
城市酒店入住时长绝大多数在五天以内;
度假酒店入住时长绝大多是在七天以内。
#餐型选择
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()
城市酒店与度假酒店餐型选择大体一致,大多数选择仅早餐(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()