一、 选题的背景
当今已经是大数据的时代,随着数据分析工具和技术的不断改进,越来越多的公司和组织开始采用大数据分析来改善业务流程、降低成本、提高效率等。因此,掌握大数据分析技能也可以为个人的职业发展带来很大的好处。
我分析的目标是酒店预订需求数据分析,这其中包括了解市场趋势、优化决策过程、改善业务流程、降低成本、提高效率等。
二、 大数据分析设计方案
1、本数据集的数据内容与数据特征分析
列名 | 数据类型 | 含义 |
hotel | object | 包含H1和H2,H1指假日酒店;H2指城市酒店 |
is_canceled | int64 | 1代表取消,O代表不取消 |
lead_time | int64 | 入住前多长时间预定 |
arrival_date_year | int64 | 入住年 |
arrival_date_month | object | 入住月份 |
arrival_date_week_number | int64 | 在第几周入住 |
arrival_date_day_of_month | int64 | 在月份中几号入住 |
stays_in_weekend_nights | int64 | 在周末住几晚 |
stays_in_week_nights | int64 | 在一周之内住几晚 |
adults | int64 | 成年人预订人数 |
children | float64 | 儿童预订人数 |
babies | int64 | 婴儿预订人数 |
meal | object | 不同种类餐食预订情况 |
country | object | 不同国家简写 |
market_segment | object | 市场划分,分为旅行社代理人和包价旅游承包 |
distribution_channel | object | 分布渠道,同market_segment |
is_repeated_guest | int64 | 以前是否预订过 |
previous_cancellations | int64 | 这次预订以前取消的次数 |
previous_bookings_not_canceled | int64 | 这次预订以前没有取消的次数 |
reserved_room_type | object | 预订房型,类别较多,以字母A-P划分 |
assigned_room_type | object | 入住的房型,同reserved_room_type |
booking_changes | int64 | 变更预订次数 |
deposit_type | object | 押金类型 |
agent | object | 代理商ID |
company | object | 公司ID |
days_in_waiting_list | int64 | 证实预订前等待的天数 |
customer_type | object | 顾客类别 |
adr | float64 | 平均每日价格 |
required_car_parking_spaces | int64 | 需要的停车位 |
total_of_special_requests | int64 | 特殊需求数 |
reservation_status | object | 订单状态 |
reservation_status_date | object | 显示订单状态日期 |
2、数据分析的课程设计方案概述
1) 数据收集:如何收集大量数据。
解决方案:通过和鲸社区和Kaggle查找需要的数据。
2) 大数据分析:如何结合大数据生成的图表进行分析
解决方案:结合实际情况,如地理位置,活动节日等进行实际推断。
三、 数据分析步骤
1、 数据源
该数据集来自kaggle
网址:https://www.kaggle.com/code/swetarajsinha/hotel-bookings/data
2、 数据清洗
检查是否能完整显示各行各列的数据
1 #设置显示所有列
2 pd.set_option('display.max_columns', None)
3
4 #读取文件,并显示所有的数据
5 data=pd.read_csv('hotel_bookings.csv')
6 df=data.copy()
7 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 | children | babies | meal | country | market_segment | distribution_channel | is_repeated_guest | previous_cancellations | previous_bookings_not_canceled | reserved_room_type | assigned_room_type | booking_changes | 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 | 0.0 | 0 | BB | PRT | Direct | Direct | 0 | 0 | 0 | C | C | 3 | 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 | 0.0 | 0 | BB | PRT | Direct | Direct | 0 | 0 | 0 | C | C | 4 | 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 | 0.0 | 0 | BB | GBR | Direct | Direct | 0 | 0 | 0 | A | C | 0 | 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 | 0.0 | 0 | BB | GBR | Corporate | Corporate | 0 | 0 | 0 | A | A | 0 | 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 | 0.0 | 0 | BB | GBR | Online TA | TA/TO | 0 | 0 | 0 | A | A | 0 | No Deposit | 240.0 | NaN | 0 | Transient | 98.0 | 0 | 1 | Check-Out | 2015-07-03 |
检查数据中是否有空数据
1 '''
2 #检查数据中缺少的值
3 #1、先用 isnull 函数会返回一个布尔型数据框,其中每个元素都表示对应的数据是否为空。
4 #2、接着使用 sum 函数对数据为空的所有元素进行求和。
5 #3、使用布尔索引判断大于 0 的值,就能得到数据中存在缺失的值。
6 '''
7 df.isnull().sum()[df.isnull().sum()>0]
显示结果如下:
对有空数据的列进行数据清洗,并检查是否还存在空数据
1 #进行数据清洗
2 #将 agent 列中选择所有缺失值的值。并将这些值更新为 'No agent'
3 df.loc[df.agent.isnull()==False,'agent']='Agent'
4 df.agent.fillna('No agent',inplace=True)
5 #使用 Unknown (未知)来填充 country (国家)列缺少的值
6 df.country.fillna('Unknown',inplace=True)
7 #使用 0 来填充 children (儿童预定人数)列缺少的值
8 df.children.fillna(0,inplace=True)
9 '''
10 #对 company (公司ID)列进行更新
11 #某些行中,细分市场或分销渠道被指定为公司,其中公司行为空,将用 Corporate (公司)填充这些空值
12 #将在公司栏中填写公司的非缺失值
13 #用 Individuals (个人)填充公司列中剩余的缺失值
14 '''
15 df.loc[((df.market_segment=='Corporate') | (df.distribution_channel=='Corporate')) & (df.company.isnull()),'company']='Corporate'
16 df.loc[df.company.isnull()==False,'company']='Corporate'
17 df.company.fillna('Individuals',inplace=True)
18
19 #查看数据之中是否还存在缺失值
20 df.isnull().sum()
显示结果如下:
删除重复冗余的数据,并对每个国家进行归纳整理,将每个国家归入对应的大陆上
1 #删除重复数据
2 df=df.drop_duplicates(keep='first').reset_index().drop(columns='index')
3 #将数据类型更正为 object 方便进行变量分析
4 df.arrival_date_year=df.arrival_date_year.astype(object)
5 #列出数据中所有国家的简称
6 df.country.unique()
7
8 #将每个国家归入对应的大陆上
9
10 europe=['PRT','GBR','ESP','IRL','FRA','ROU','NOR','POL','DEU','BEL','CHE','GRC','ITA','NLD','DNK','RUS','SWE','EST',
11 'CZE','FIN','LUX','SVN','ALB','UKR','SMR','LVA','SRB','AUT','BLR','LTU','TUR','HUN','HRV','GEO','AND','SVK',
12 'MKD','BIH','BGR','MLT','ISL','MCO','LIE','MNE']
13
14 north_a=['USA','MEX','PRI','CRI','CUB','HND','NIC','GAB','PAN','SLV','GTM']
15
16 south_a=['ARG','BRA','CHL','URY','COL','VEN','SUR','PER','ECU','BOL','PRY','GUY']
17
18 asia=['OMN','CN','IND','CHN','ISR','KOR','ARE','HKG','IRN','CYP','KWT','MDV','KAZ','PAK','IDN','LBN','PHL','AZE','BHR',
19 'THA','MYS','ARM','JPN','LKA','JOR','SYR','SGP','SAU','VNM','QAT','UZB','NPL','MAC','TWN','IRQ','KHM','BGD','TJK',
20 'TMP','MMR','LAO']
21
22 africa=['MOZ','BWA','MAR','ZAF','AGO','ZMB','ZWE','DZA','TUN','CAF','NGA','SEN','SYC','CMR','MUS','COM','UGA','CIV',
23 'BDI','EGY','MWI','MDG','TGO','DJI','STP','ETH','RWA','BEN','TZA','GHA','KEN','GNB','BFA','LBY','MLI','NAM',
24 'MRT','SDN','SLE']
25
26 australia=['AUS']
27
28 Others=['CYM','CPV','JAM','GIB','JEY','GGY','FJI','NZL','DOM','PLW','BHS','KNA','IMN','VGB','GLP','UMI','MYT','FRO',
29 'BRB','ABW','AIA','DMA','PYF','LCA','ATA','ASM','NCL','KIR','ATF']
30
31 unk=['Unknown']
32
33 #将国家/地区列放入二进制
34 def country_bin(x):
35 if x in europe:
36 return 'Europe'
37 elif x in north_a:
38 return 'North America'
39 elif x in south_a:
40 return 'South America'
41 elif x in asia:
42 return 'Asia'
43 elif x in africa:
44 return 'Africa'
45 elif x in australia:
46 return 'Australia'
47 elif x in Others:
48 return 'Others'
49 elif x in unk:
50 return 'Unknown'
51 df.country=df.country.apply(country_bin)
52
53 #所有国家分配成各个地区
54 df.country.unique()
3、大数据分析过程
通过pandas.crosstab()函数来生成数据表,方便查看数据间的关系。
#绘制酒店取消预订数量表
1 tbl=pd.crosstab(df.hotel,df.is_canceled,margins=True)
2 tbl
显示结果如下:
is_canceled | 0 | 1 | All |
hotel | | | |
City Hotel | 37373 | 16047 | 53420 |
Resort Hotel | 25985 | 7974 | 33959 |
All | 63358 | 24021 | 87379 |
#添加一列 cancel_percent (取消预订的百分比)来显示取消的预订占总预订的百分比
1 tbl['cancel_percent']=tbl[1]*100/tbl['All']
2 tbl
显示结果如下:
is_canceled | 0 | 1 | All | cancel_percent |
hotel | | | | |
City Hotel | 37373 | 16047 | 53420 | 30.039311 |
Resort Hotel | 25985 | 7974 | 33959 | 23.481257 |
All | 63358 | 24021 | 87379 | 27.490587 |
#绘制酒店取消预订柱状图
1 pd.crosstab(df.hotel, df.is_canceled, values=df.is_canceled, aggfunc='count', normalize=True).plot.bar()
2 plt.title('酒店取消预订情况')
3 plt.ylabel('取消预订比例')
4 plt.xlabel('酒店类型')
5 plt.show()
显示结果如下:
分析:这里可以看出来,City Hotel(城市酒店)取消的人数更多,相比较于Resort Hotel(假日酒店)多出了6.5个百分点,除去正常超额预订的情况,City Hotel的取消率还是过高,可能是因为城市酒店位置优越,方便前往商务场所或者旅游景点。此外,城市酒店的价格可能比假日酒店的价格更容易接受。
#通过 lead_time (入住前多长时间预订)列绘制直方图
1 sns.distplot(df.lead_time)
2 plt.show()
显示结果如下:
#绘制城市酒店和假日酒店的盒图
1 #通过 is_canceled (是否取消预订)列和 lead_time (入住前多长时间预订)列绘制城市酒店的盒图
2 sns.boxplot(x='is_canceled',y='lead_time',data=df.loc[df.hotel=='City Hotel'])
3 plt.show()
4
5
6 #通过 is_canceled (是否取消预订)列和 lead_time (入住前多长时间预订)列绘制假日酒店的盒图
7 sns.boxplot(x='is_canceled',y='lead_time',data=df.loc[df.hotel=='Resort Hotel'])
8 plt.show()
显示结果如下:
分析:通过lead_time (入住前多长时间预订)与酒店取消预订绘制两个盒图,发现Resort Hotel(右)比City Hotel(左)取消预订最长提前期更高,但City Hotel的平均取消预订更高,可能是因为City Hotel的客户群更为多样化,其中可能包括许多商务旅客。这些商务旅客可能会因为工作原因或其他突发事件而不得不取消预订。
#绘制两个酒店每年预订的柱状图
1 plt.rcParams['figure.figsize']=[8,5]
2 sns.countplot(x='arrival_date_year',data=df,hue='hotel')
3 plt.xlabel('年份')
4 plt.ylabel('预订数量')
5 plt.title('每年的预订')
6 plt.show()
显示结果如下:
分析:从图中可以看出2016年的预订量是最高的,而2015年是最少的,可能2015年后市场需求增加、2015年后酒店更新和建立数量增加等。
# 显示全年取消率的表
1 tbl=pd.crosstab(df.arrival_date_year,df.is_canceled,margins=True)
2 tbl
3 #添加一列 cancel-percent (取消预订的百分比)来显示取消的预订占总预订的百分比
4 tbl['cancel-percent']=tbl[1]*100/tbl['All']
5 tbl
显示结果如下:
is_canceled | 0 | 1 | All | cancel-percent |
arrival_date_year | | | | |
2015 | 10605 | 2702 | 13307 | 20.305103 |
2016 | 31178 | 11207 | 42385 | 26.440958 |
2017 | 21575 | 10112 | 31687 | 31.912141 |
All | 63358 | 24021 | 87379 | 27.490587 |
#绘制全年取消率柱状图
1 tbl.drop('All',axis=0)['cancel-percent'].plot.bar()
2 plt.xticks(rotation=0)
3 plt.xlabel('Year')
4 plt.ylabel('Cancellation %')
5 plt.title('2015-2017年取消率')
6 plt.show()
显示结果如下:
#绘制每个年份的取消预订的数量所占所有取消预订的总数的比例。
df[df.is_canceled==1]['arrival_date_year'].value_counts()/len(df[df.is_canceled==1])
显示结果如下:
分析:根据图标显示发现,酒店的预订数量和取消率连年增加,说明随着年份增加,酒店市场需求也再增加,随着市场的增加客户的选择更多,当发现有更加好的酒店时,就更容易取消预订过的酒店。也有可能是随着预订数量的增加,酒店进行了房间超售,导致客户不满或无房可住,导致预订取消率增加。
#绘制每月的预订数量
1 plt.rcParams['figure.figsize']=[8,5]
2 sns.countplot(x='arrival_date_month',data=df,order=df.arrival_date_month.value_counts().index)
3 plt.title('每月预订数量 ')
4 plt.xlabel('预订月份')
5 plt.ylabel('预订数量')
6 plt.xticks(rotation=60,ha='right')
7 plt.show()
显示结果如下:
#绘制每月预订数量
1 plt.rcParams['figure.figsize']=[8,5]
2 sns.countplot(x='arrival_date_month',data=df,order=df.arrival_date_month.value_counts().index,hue='hotel')
3 plt.title('每月预订数量 ')
4 plt.xlabel('Booking Month')
5 plt.ylabel('Number of Bookings')
6 plt.xticks(rotation=60,ha='right')
7 plt.show()
显示结果如下:
#显示每个月预订数量
1 tbl=pd.crosstab(df.arrival_date_month,df.is_canceled,margins=True)
2 tbl['cancel_percent']=tbl[1]*100/tbl['All']
3 tbl
显示结果如下:
is_canceled | 0 | 1 | All |
arrival_date_month | | | |
April | 5498 | 2409 | 7907 |
August | 7633 | 3621 | 11254 |
December | 3751 | 1378 | 5129 |
February | 4682 | 1415 | 6097 |
January | 3654 | 1038 | 4692 |
July | 6858 | 3198 | 10056 |
June | 5411 | 2353 | 7764 |
March | 5681 | 1830 | 7511 |
May | 5912 | 2442 | 8354 |
November | 3939 | 1053 | 4992 |
October | 5292 | 1642 | 6934 |
September | 5047 | 1642 | 6689 |
All | 63358 | 24021 | 87379 |
#将上表中的取消预订的百分比进行绘制
1 tbl.drop('All',axis=0)['cancel_percent'].plot.bar()
2 plt.title('每个月取消预订百分比')
3 plt.xlabel('月份')
4 plt.ylabel('取消预订百分比(%)')
5 plt.show()
显示结果如下:
分析:与其他月份相比,8月和7月的预定量是最高的,这期间一般是学校假期,学生群体更容易去其他地方旅游度假或参加聚会活动,所以假期也是假期预订量的关键。但通过后续的取消预订百分比图发现,7、8月份也是取消率最高的月份,根据上一个酒店会超售的分析,再次表明了,在节假日期间,酒店的预订量会过多和匆忙。
#绘制第几周入住的柱状图
1 plt.figure(figsize=(10,15))
2 sns.countplot(x='arrival_date_week_number',data=df)
3 plt.title('每周入住数量')
4 plt.xticks(rotation='vertical')
5 plt.xlabel('每年的周数')
6 plt.ylabel('数量')
7 plt.show()
显示结果如下:
分析:不难发现,第33周的预订量是最高的,推算一下是在8月份附近,进一步支持了8月份节假日举行活动和旅游度假的说法。还看到临近年底与过年前夕,预订数量明显减少,特别是在51周,是预订量最低的一周,根据推算,是在12月份中旬,在国外,这个日子最接近圣诞节,说明人们在这一周会进行圣诞节的准备活动,不会过多的在意酒店预订。
#绘制周末入住持续天数
1 plt.figure(figsize=(7,7))
2 sns.distplot(df.stays_in_weekend_nights)
3 plt.title('周末入住持续天数')
4 plt.xlabel('周末入住天数')
5 plt.ylabel('密度')
6 plt.show()
显示结果如下:
分析:大多数人在酒店停留时间较短,通常是0-3天(0天是酒店的钟点房)。由此可以说明酒店的受众类型是短期旅行度假和临时居住的类型,也有一些记录是停留时间更长,这可能是因为人们出差或进行比较长时间的项目。
#通过 hotel (酒店)列和stays_in_weekend_nights(周末入住天数)列绘制盒图
1 plt.figure(figsize=(7,7))
2 sns.boxplot(x='hotel',y='stays_in_weekend_nights',data=df)
3 plt.show()
显示结果如下:
分析:通过盒图发现,两个酒店在周末入住天数其实差不多,所以,周末在City Hotel 和Resort Hotel 住的人只是进行短住,一般持续一两天,长期居住的人较少。
#绘制成年人预定人数入住图
1 plt.figure(figsize=(5,5))
2 sns.distplot(df.loc[(df.stays_in_week_nights==0) & (df.stays_in_weekend_nights==0)]['adults'])
3 plt.title('成年人预订人数图')
4 plt.ylabel('密度')
5 plt.xlabel('成年人预订人数')
6 plt.show()
显示结果如下:
分析:由图片可以得知,预订大部分人是单人和双人,只有少数的未成年人和极少数有三个人的预订。人数最多的是双人预订,大部分可能是夫妻和情侣入住,大多可能是以休闲或旅游为主。
#绘制婴儿预订入住图
1 plt.figure(figsize=(5,5))
2 sns.distplot(df.babies)
3 plt.title('婴儿预订入住图')
4 plt.ylabel('密度')
5 plt.xlabel('婴儿数量')
6 plt.show()
显示结果如下:
分析:由婴儿预订入住图可以明确的看出,基本上入住的都是没有婴儿的人(排除可能将婴儿放置在亲戚或其他的可能),只有极少数人会带领一个婴儿入住酒店。表明,来入住酒店的人群多是公司旅行、单人旅行、情侣旅行等,只有极少数由婴儿的家庭来旅行。
#绘制国家地区频率分布图
1 plt.figure(figsize=(8,8))
2 sns.countplot(x='country',data=df)
3 plt.title('国家地区频率分布')
4 plt.xticks(rotation=60,ha='right')
5 plt.ylabel('数量')
6 plt.xlabel('国家地区')
7 plt.show()
显示结果如下:
#生成国家地区是否取消预订表
1 tbl=pd.crosstab(df.country,df.is_canceled,margins=True)
2 #添加一列 cancel-percent (取消预订的百分比)来显示取消的预订占总预订的百分比
3 tbl['cancel_percent']=tbl[1]*100/tbl['All']
4 tbl
显示结果如下:
is_canceled | 0 | 1 | All |
country | | | |
Africa | 559 | 436 | 995 |
Asia | 2286 | 1141 | 3427 |
Australia | 286 | 92 | 378 |
Europe | 56573 | 20944 | 77517 |
North America | 1531 | 474 | 2005 |
Others | 106 | 53 | 159 |
South America | 1599 | 847 | 2446 |
Unknown | 418 | 34 | 452 |
All | 63358 | 24021 | 87379 |
分析:由图可以得出,大部分来酒店的是欧洲地区的人,可以推断,此数据来源于欧洲,也有少数亚洲和其他地区的人来旅游入住。酒店的消费主力是欧洲人,可以对酒店进行针对性改造,更加吸引欧洲人入住。
#绘制国家地区取消预订百分比图
1 plt.figure(figsize=(7,7))
2 tbl.drop('All',axis=0)['cancel_percent'].plot.bar()
3 plt.title('国家地区取消预订百分比图')
4 plt.xticks(rotation=60,ha='right')
5 plt.ylabel('取消预订百分比(%)')
6 plt.xlabel('国家地区')
7 plt.show()
显示结果如下:
分析:从图中可以看出,Africa (非洲)的取消率是最高的,应该是源于签证的问题,因为欧盟有一些严格的规定(欧洲的国家取消比例要低很多),在上图中可以看出,除了未知地区,North America (北美)客户的取消率是最低的,这可能是北约的原因。
#绘制市场划分图
1 plt.rcParams['figure.figsize']=[7,7]
2 sns.countplot(x='market_segment',data=df)
3 plt.xticks(rotation=60,ha='right')
4 plt.title('市场划分图')
5 plt.ylabel('数量')
6 plt.xlabel('市场划分')
7 plt.show()
显示结果如下:
#显示市场划分数量表
1 tbl=pd.crosstab(df.market_segment,df.is_canceled,margins=True)
2 #添加一列 cancel-percent (取消预订的百分比)来显示取消的预订占总预订的百分比
3 tbl['cancel_percent']=tbl[1]*100/tbl['All']
4 tbl
显示结果如下:
is_canceled | 0 | 1 | All |
market_segment | | | |
Aviation | 182 | 45 | 227 |
Complementary | 614 | 88 | 702 |
Corporate | 3698 | 510 | 4208 |
Direct | 10067 | 1737 | 11804 |
Groups | 3606 | 1335 | 4941 |
Offline TA/TO | 11822 | 2060 | 13882 |
Online TA | 33369 | 18244 | 51613 |
Undefined | 0 | 2 | 2 |
All | 63358 | 24021 | 87379 |
#绘制市场取消率图
1 tbl.drop('All',axis=0)['cancel_percent'].plot.bar()
2 plt.title('市场取消率')
3 plt.xticks(rotation=60,ha='right')
4 plt.ylabel('取消预订百分比(%)')
5 plt.xlabel('市场划分')
6 plt.show()
显示结果如下:
分析:由市场划分图可以发现,绝大多数的记录是通过Online TA(在线旅行代理商)获得的,这表明在线旅行代理商在欧盟拥有强大的网络,在网络上有完善的预约流程。还发现有一些 Complementary(补充)的记录,这表示可能是酒店方面有报销或补偿的政策。从市场取消率可以发现 Undefined(未定义)的取消率是最高的,达到了百分百。这很不正常,查看市场划分数量表后发现,未定义数量有2条记录,这可能是由于系统错误,导致多了两条错误,并全部让其取消了。排除Undefined的记录后发现,Online TA的取消率是最高的,这表明,人们在看到网络上的酒店可能跟实际不符,可能导致预订取消的情况。
#绘制市场分布渠道图
1 sns.countplot(x='distribution_channel',data=df)
2 plt.title('市场分布渠道图')
3 plt.ylabel('数量')
4 plt.xlabel('市场分布渠道')
5 plt.show()
显示结果如下:
#显示市场分布渠道数量表
1 tbl=pd.crosstab(df.distribution_channel,df.is_canceled,margins=True)
2 ##添加一列 cancel-percent (取消预订的百分比)来显示取消的预订占总预订的百分比
3 tbl['cancel_percent']=tbl[1]*100/tbl['All']
4 tbl
显示结果如下:
is_canceled | 0 | 1 | All | cancel_percent |
distribution_channel | | | | |
Corporate | 4429 | 648 | 5077 | 12.763443 |
Direct | 11063 | 1925 | 12988 | 14.821374 |
GDS | 145 | 36 | 181 | 19.889503 |
TA/TO | 47720 | 21408 | 69128 | 30.968638 |
Undefined | 1 | 4 | 5 | 80.000000 |
All | 63358 | 24021 | 87379 | 27.490587 |
#绘制市场分布渠道百分比图
1 tbl.drop('All',axis=0)['cancel_percent'].plot.bar()
2 plt.title('市场分布渠道百分比图')
3 plt.ylabel('取消预订百分比(%)')
4 plt.xlabel('分布渠道')
5 plt.show()
显示结果如下:
分析:在市场分布渠道图中可以发现,TA/TO(旅行代理商/旅行社)的预订量是最高的,说明酒店与TA/TO的联合合作。在市场分布百分比图中发现Undefined(未定义)的取消率是最高的,查看市场分布渠道数量表后发现,Undefined的数量为5条。将Undefined排除后,发现TA/TO的取消率是最高的,Corporate(公司)的取消率是最低的。这可能是因为公司预订是为了业务目的安排的,因此取消的风险可能较小,公司旅行还可能会有更多的提前安排和计划,这可能会使取消的可能性更小。
4、 完整代码
1 #导入所需要的库
2 import numpy as np
3 import pandas as pd
4 from matplotlib import pyplot as plt
5 import seaborn as sns
6 import warnings
7 warnings.filterwarnings('ignore')
8 import scipy.stats as stats
9 from sklearn.model_selection import train_test_split
10 from sklearn.ensemble import RandomForestClassifier
11 from lightgbm import LGBMClassifier
12 from sklearn.metrics import accuracy_score,classification_report,confusion_matrix
13 from sklearn.metrics import ConfusionMatrixDisplay,f1_score,recall_score,precision_score
14
15 #设置显示所有列
16 pd.set_option('display.max_columns', None)
17
18 #读取文件,并显示所有的数据
19 data=pd.read_csv('hotel_bookings.csv')
20 df=data.copy()
21 df.head()
22
23 #显示摘要信息
24 df.info()
25
26 #对数据进行统计
27 df.describe()
28
29 #计算每对数据列的相关系数
30 df.corr()
31 '''
32 #检查数据中缺少的值
33 #1、先用 isnull 函数会返回一个布尔型数据框,其中每个元素都表示对应的数据是否为空。
34 #2、接着使用 sum 函数对数据为空的所有元素进行求和。
35 #3、使用布尔索引判断大于 0 的值,就能得到数据中存在缺失的值。
36 '''
37 df.isnull().sum()[df.isnull().sum()>0]
38
39 #对 Agent (代理商ID)列进行数据清洗
40 #将 agent 列中选择所有缺失值的值。并将这些值更新为 'No agent'
41 df.loc[df.agent.isnull()==False,'agent']='Agent'
42 df.agent.fillna('No agent',inplace=True)
43
44 #检查数据中缺少的值
45 df.isnull().sum()[df.isnull().sum()>0]
46
47 #使用 Unknown (未知)来填充 country (国家)列缺少的值
48 df.country.fillna('Unknown',inplace=True)
49 #检查数据中缺少的值
50 df.isnull().sum()[df.isnull().sum()>0]
51
52 #使用 0 来填充 children (儿童预定人数)列缺少的值
53 df.children.fillna(0,inplace=True)
54 #检查数据中缺少的值
55 df.isnull().sum()[df.isnull().sum()>0]
56
57 '''
58 #对 company (公司ID)列进行更新
59 #某些行中,细分市场或分销渠道被指定为公司,其中公司行为空,将用 Corporate (公司)填充这些空值
60 #将在公司栏中填写公司的非缺失值
61 #用 Individuals (个人)填充公司列中剩余的缺失值
62 '''
63 df.loc[((df.market_segment=='Corporate') | (df.distribution_channel=='Corporate')) & (df.company.isnull()),'company']='Corporate'
64 df.loc[df.company.isnull()==False,'company']='Corporate'
65 df.company.fillna('Individuals',inplace=True)
66
67 #查看数据之中是否存在缺失值
68 df.isnull().sum()
69
70 #删除重复数据
71 df=df.drop_duplicates(keep='first').reset_index().drop(columns='index')
72 #将数据类型更正为 object 方便进行变量分析
73 df.arrival_date_year=df.arrival_date_year.astype(object)
74 #列出数据中所有国家的简称
75 df.country.unique()
76
77 #将每个国家归入对应的大陆上
78
79 europe=['PRT','GBR','ESP','IRL','FRA','ROU','NOR','POL','DEU','BEL','CHE','GRC','ITA','NLD','DNK','RUS','SWE','EST',
80 'CZE','FIN','LUX','SVN','ALB','UKR','SMR','LVA','SRB','AUT','BLR','LTU','TUR','HUN','HRV','GEO','AND','SVK',
81 'MKD','BIH','BGR','MLT','ISL','MCO','LIE','MNE']
82
83 north_a=['USA','MEX','PRI','CRI','CUB','HND','NIC','GAB','PAN','SLV','GTM']
84
85 south_a=['ARG','BRA','CHL','URY','COL','VEN','SUR','PER','ECU','BOL','PRY','GUY']
86
87 asia=['OMN','CN','IND','CHN','ISR','KOR','ARE','HKG','IRN','CYP','KWT','MDV','KAZ','PAK','IDN','LBN','PHL','AZE','BHR',
88 'THA','MYS','ARM','JPN','LKA','JOR','SYR','SGP','SAU','VNM','QAT','UZB','NPL','MAC','TWN','IRQ','KHM','BGD','TJK',
89 'TMP','MMR','LAO']
90
91 africa=['MOZ','BWA','MAR','ZAF','AGO','ZMB','ZWE','DZA','TUN','CAF','NGA','SEN','SYC','CMR','MUS','COM','UGA','CIV',
92 'BDI','EGY','MWI','MDG','TGO','DJI','STP','ETH','RWA','BEN','TZA','GHA','KEN','GNB','BFA','LBY','MLI','NAM',
93 'MRT','SDN','SLE']
94
95 australia=['AUS']
96
97 Others=['CYM','CPV','JAM','GIB','JEY','GGY','FJI','NZL','DOM','PLW','BHS','KNA','IMN','VGB','GLP','UMI','MYT','FRO',
98 'BRB','ABW','AIA','DMA','PYF','LCA','ATA','ASM','NCL','KIR','ATF']
99
100 unk=['Unknown']
101
102 #将国家/地区列放入二进制
103 def country_bin(x):
104 if x in europe:
105 return 'Europe'
106 elif x in north_a:
107 return 'North America'
108 elif x in south_a:
109 return 'South America'
110 elif x in asia:
111 return 'Asia'
112 elif x in africa:
113 return 'Africa'
114 elif x in australia:
115 return 'Australia'
116 elif x in Others:
117 return 'Others'
118 elif x in unk:
119 return 'Unknown'
120 df.country=df.country.apply(country_bin)
121
122 #所有国家分配成各个地区
123 df.country.unique()
124
125 #酒店类型分布饼图
126 plt.rcParams['font.sans-serif'] = [u'SimHei']
127 plt.rcParams['axes.unicode_minus'] = False
128 plt.rcParams['figure.figsize']=[5,5]
129 plt.pie(df.hotel.value_counts().values,explode=[0,0.2],labels=df.hotel.value_counts().index,autopct='%.2f%%')
130 plt.title('酒店类型分布')
131 plt.show()
132
133 #绘制酒店取消预订柱状图
134 pd.crosstab(df.hotel, df.is_canceled, values=df.is_canceled, aggfunc='count', normalize=True).plot.bar()
135 plt.title('酒店取消预订情况')
136 plt.ylabel('取消预订比例')
137 plt.xlabel('酒店类型')
138 plt.show()
139
140 #绘制酒店取消预订数量表
141 tbl=pd.crosstab(df.hotel,df.is_canceled,margins=True)
142 tbl
143
144 #添加一列 cancel_percent (取消预订的百分比)来显示取消的预订占总预订的百分比
145 tbl['cancel_percent']=tbl[1]*100/tbl['All']
146 tbl
147
148 #绘制不同酒店类型的取消率
149 tbl.drop('All',axis=0)['cancel_percent'].plot.bar()
150 plt.title('不同酒店类型的取消率')
151 plt.xlabel('酒店类型')
152 plt.xticks(rotation=0)
153 plt.ylabel('取消数量百分比(%)')
154 plt.show()
155
156 #通过 lead_time (入住前多长时间预订)列绘制直方图
157 sns.distplot(df.lead_time)
158 plt.show()
159
160 #通过 is_canceled (是否取消预订)列和 lead_time (入住前多长时间预订)列绘制城市酒店的盒图
161 sns.boxplot(x='is_canceled',y='lead_time',data=df.loc[df.hotel=='City Hotel'])
162 plt.show()
163
164 #通过 is_canceled (是否取消预订)列和 lead_time (入住前多长时间预订)列绘制假日酒店的盒图
165 sns.boxplot(x='is_canceled',y='lead_time',data=df.loc[df.hotel=='Resort Hotel'])
166 plt.show()
167
168 #绘制两个酒店每年预订的柱状图
169 plt.rcParams['figure.figsize']=[8,5]
170 sns.countplot(x='arrival_date_year',data=df,hue='hotel')
171 plt.xlabel('年份')
172 plt.ylabel('预订数量')
173 plt.title('每年的预订')
174 plt.show()
175
176 # 显示全年取消率的表
177 tbl=pd.crosstab(df.arrival_date_year,df.is_canceled,margins=True)
178 tbl
179
180 #添加一列 cancel-percent (取消预订的百分比)来显示取消的预订占总预订的百分比
181 tbl['cancel-percent']=tbl[1]*100/tbl['All']
182 tbl
183
184 #绘制全年取消率柱状图
185 tbl.drop('All',axis=0)['cancel-percent'].plot.bar()
186 plt.xticks(rotation=0)
187 plt.xlabel('年份')
188 plt.ylabel('取消数量百分比(%)')
189 plt.title('2015-2017年取消率')
190 plt.show()
191
192 #绘制每个年份的取消预订的数量所占所有取消预订的总数的比例。
193 df[df.is_canceled==1]['arrival_date_year'].value_counts()/len(df[df.is_canceled==1])
194
195 #绘制每月的预订数量
196 plt.rcParams['figure.figsize']=[8,5]
197 sns.countplot(x='arrival_date_month',data=df,order=df.arrival_date_month.value_counts().index)
198 plt.title('每月预订数量 ')
199 plt.xlabel('预订月份')
200 plt.ylabel('预订数量')
201 plt.xticks(rotation=60,ha='right')
202 plt.show()
203
204 #绘制每月预订数量
205 plt.rcParams['figure.figsize']=[8,5]
206 sns.countplot(x='arrival_date_month',data=df,order=df.arrival_date_month.value_counts().index,hue='hotel')
207 plt.title('每月预订数量 ')
208 plt.xlabel('Booking Month')
209 plt.ylabel('Number of Bookings')
210 plt.xticks(rotation=60,ha='right')
211 plt.show()
212
213 #显示每个月预订数量
214 tbl=pd.crosstab(df.arrival_date_month,df.is_canceled,margins=True)
215 tbl
216
217 #添加一列 cancel-percent (取消预订的百分比)来显示取消的预订占总预订的百分比
218 tbl['cancel_percent']=tbl[1]*100/tbl['All']
219 tbl
220
221 #将上表中的取消预订的百分比进行绘制
222 tbl.drop('All',axis=0)['cancel_percent'].plot.bar()
223 plt.title('每个月取消预订百分比')
224 plt.xlabel('月份')
225 plt.ylabel('取消预订百分比(%)')
226 plt.show()
227
228 #绘制第几周入住的柱状图
229 plt.figure(figsize=(10,15))
230 sns.countplot(x='arrival_date_week_number',data=df)
231 plt.title('每周入住数量')
232 plt.xticks(rotation='vertical')
233 plt.xlabel('每年的周数')
234 plt.ylabel('数量')
235 plt.show()
236
237 #绘制周末入住持续天数
238 plt.figure(figsize=(7,7))
239 sns.distplot(df.stays_in_weekend_nights)
240 plt.title('周末入住持续天数')
241 plt.xlabel('周末入住天数')
242 plt.ylabel('密度')
243 plt.show()
244
245 #通过 hotel (酒店)列和stays_in_weekend_nights(周末入住天数)列绘制盒图
246 plt.figure(figsize=(7,7))
247 sns.boxplot(x='hotel',y='stays_in_weekend_nights',data=df)
248 plt.show()
249
250 #绘制成年人预定人数入住图
251 plt.figure(figsize=(5,5))
252 sns.distplot(df.loc[(df.stays_in_week_nights==0) & (df.stays_in_weekend_nights==0)]['adults'])
253 plt.title('成年人预订人数图')
254 plt.ylabel('密度')
255 plt.xlabel('成年人预订人数')
256 plt.show()
257
258 #绘制婴儿预订入住图
259 plt.figure(figsize=(5,5))
260 sns.distplot(df.babies)
261 plt.title('婴儿预订入住图')
262 plt.ylabel('密度')
263 plt.xlabel('婴儿数量')
264 plt.show()
265
266 #绘制国家地区频率分布图
267 plt.figure(figsize=(8,8))
268 sns.countplot(x='country',data=df)
269 plt.title('国家地区频率分布')
270 plt.xticks(rotation=60,ha='right')
271 plt.ylabel('数量')
272 plt.xlabel('国家地区')
273 plt.show()
274
275 #生成国家地区是否取消预订表
276 tbl=pd.crosstab(df.country,df.is_canceled,margins=True)
277 tbl
278
279 #添加一列 cancel-percent (取消预订的百分比)来显示取消的预订占总预订的百分比
280 tbl['cancel_percent']=tbl[1]*100/tbl['All']
281 tbl
282
283 #绘制国家地区取消预订百分比图
284 plt.figure(figsize=(7,7))
285 tbl.drop('All',axis=0)['cancel_percent'].plot.bar()
286 plt.title('国家地区取消预订百分比图')
287 plt.xticks(rotation=60,ha='right')
288 plt.ylabel('取消预订百分比(%)')
289 plt.xlabel('国家地区')
290 plt.show()
291
292 #绘制市场划分图
293 plt.rcParams['figure.figsize']=[7,7]
294 sns.countplot(x='market_segment',data=df)
295 plt.xticks(rotation=60,ha='right')
296 plt.title('市场划分图')
297 plt.ylabel('数量')
298 plt.xlabel('市场划分')
299 plt.show()
300
301 #显示市场划分数量表
302 tbl=pd.crosstab(df.market_segment,df.is_canceled,margins=True)
303 tbl
304
305 #添加一列 cancel-percent (取消预订的百分比)来显示取消的预订占总预订的百分比
306 tbl['cancel_percent']=tbl[1]*100/tbl['All']
307 tbl
308
309 #绘制市场取消率图
310 tbl.drop('All',axis=0)['cancel_percent'].plot.bar()
311 plt.title('市场取消率')
312 plt.xticks(rotation=60,ha='right')
313 plt.ylabel('取消预订百分比(%)')
314 plt.xlabel('市场划分')
315 plt.show()
316
317 #绘制市场分布渠道图
318 sns.countplot(x='distribution_channel',data=df)
319 plt.title('市场分布渠道图')
320 plt.ylabel('数量')
321 plt.xlabel('市场分布渠道')
322 plt.show()
323
324 #显示市场分布渠道数量表
325 tbl=pd.crosstab(df.distribution_channel,df.is_canceled,margins=True)
326 tbl
327
328 ##添加一列 cancel-percent (取消预订的百分比)来显示取消的预订占总预订的百分比
329 tbl['cancel_percent']=tbl[1]*100/tbl['All']
330 tbl
331
332 #绘制市场分布渠道百分比图
333 tbl.drop('All',axis=0)['cancel_percent'].plot.bar()
334 plt.title('市场分布渠道百分比图')
335 plt.ylabel('取消预订百分比(%)')
336 plt.xlabel('分布渠道')
337 plt.show()
四、 总结
1、结论
通过大数据分析,发现城市酒店的预定量比假日酒店的预定量多。可能是因为城市酒店位置优越,方便前往商务场所或者旅游景点。此外,城市酒店的价格可能比假日酒店的价格更容易接受。然而,城市酒店的预订取消率可能比假日酒店的高,可能是因为城市酒店的客户群更为多样化,其中可能包括许多商务旅客。这些商务旅客可能会因为工作原因或其他突发事件而不得不取消预订。而且,城市酒店的客户群还可能包括许多自由行旅客,这些旅客可能会因为个人原因而取消预订。相比之下,假日酒店的客户群可能更加稳定,因为假日酒店通常是为度假或休闲而安排的,因此客户可能不太可能因为工作或其他突发事件而取消预订。
根据国家地区的分析,酒店具有较多的欧盟客户,说明此数据集是来自于欧盟的酒店数据。这也说明了欧盟国家之间的贸易和经济联系较为紧密,欧盟的人们也更愿意前往邻国旅游。除欧盟本地外,还有不少其他国家和地区的客户,这可能是因为欧盟国家在旅游业方面具有较高的吸引力,包括丰富的文化、历史和自然风光。此外,欧盟国家的交通和通信设施也可能较为发达,使得前往这些国家旅游更加方便。根据分析还发现,酒店的客户中,北美洲的客户取消率普遍偏低而非洲的取消率较高,这说明了北美国家的经济和稳定性较高,因此可能会有较少的突发事件导致客户取消预订,另一方面,非洲国家可能存在较多的政治、经济和社会不稳定因素,这可能会导致客户更容易取消预订。例如,非洲国家可能存在较多的冲突和动乱,这可能会使客户担心前往这些国家的安全。此外,非洲国家的经济可能不太稳定,这也可能会使客户担心旅行的费用是否会发生变化。
在酒店的市场分布分析中发现,旅行社的数量较高,同时取消率也是最高的,而公司的取消率是最低,这说明了,在旅行酒店住宿方面,旅行社承担了大量的旅行组织和安排工作,并且需要在较短的时间内安排较多的旅行。因此,旅行社可能会受到更多的压力,以确保所有的旅行安排都能顺利进行。另一方面,公司的数量可能较少,但是取消率可能较低。这可能是因为公司旅行通常是为了公司的业务目的而安排的,因此取消的风险可能较小。公司旅行还可能会有更多的提前安排和计划,这可能会使取消的风险更小。
2、收获
在这次设计过程中让我更加了解了什么是数据,了解了数据之间的关系,并让我更加熟悉了,pandas库、seaborn库、pyplot等方法的使用。这些可以帮助我对未来的趋势和变化进行预测,让我可以更好的了解市场与客户,可以通过大数据分析来提高业绩,这可以为我带来更多的竞争优势。
在这次的设计过程中,需要修改进步的地方还有很多,比如,可以更改绘图功能,也就是优化数据可视化工具,以便更好的呈现数据。如果可能,可以考虑引入机器学习,以提高效率。