美文网首页python
酒店预订网客户流失分析

酒店预订网客户流失分析

作者: 西西弗斯wdw | 来源:发表于2019-10-02 11:30 被阅读0次

    阅读路线
    项目介绍:该项目对某酒店预订网在一段时间内的客户预定信息数据进行分析,其中着重对该网站整体消费情况和用户行为展开分析,找出高价值用户人群,对客户进行用户画像分析,从而为该网站的精细化营销提供相关建议。
    代码展示

    1.初始设置

    import pandas as pd
    import numpy as np
    import matplotlib.pyplot as plt
    import seaborn as sns
    %matplotlib inline
    

    2.查看数据

    rawdata=pd.read_csv('E:/userlostprob.txt',sep='\t')
    rawdata.head()
    
    相关数据

    从图中我们可以看到,原始数据有很多缺失值。

    # 查看数据维度
    rawdata.shape
    

    该数据维度为(689945, 51)。总共689945条样本数据,除去标签列和id列,总共49个字段。

    # 查看每列数据信息
    rawdata.info()
    

    可以看到,除了预定时间和入住时间2列为字符型之外,其余均为数值型,我们之后只需要将预定时间和入住时间处理为数值型。

    # 查看数据缺失情况
    rawdata.isnull().mean()
    

    我们可以看到,数据缺失值较多,特别是historyvisit_7ordernum缺失达到近88%。

    # 标签分布
    rawdata['label'].value_counts()
    

    我们可以看到,数据存在明显偏斜。

    3.相关数据特征的可视化

    # 解决坐标轴刻度负号乱码
    plt.rcParams['axes.unicode_minus'] = False
    # 解决中文乱码问题
    plt.rcParams['font.sans-serif'] = ['Simhei']
    # 访问日期和入住日期
    # 入住时间人数统计
    arrival=rawdata[['arrival']]
    arrival['counta']=1
    arrival=arrival.groupby('arrival').sum().reset_index()
    # 访问时间人数统计
    d=rawdata[['d']]
    d['countd']=1
    d=d.groupby('d').sum().reset_index()
    # 合并入住时间和访问时间人数
    time_table=pd.merge(arrival,d,left_on='arrival',right_on='d',how='left')
    time_table.fillna(0,inplace=True)
    del time_table['d']
    
    # 画出日期与人数的关系图
    
    plt.figure(figsize=(13, 5));
    plt.style.use('ggplot')
    
    x=range(len(time_table));
    y1=time_table['counta'].values;
    y2=time_table['countd'].values;
    z=time_table['arrival'].values;
    plt.plot(x,y1,c="r",label='入住人数');
    plt.bar(x,y2,align="center",label='预定人数');
    plt.xlabel('日期');
    plt.ylabel('人数');
    plt.xticks(x,z,fontsize=11,rotation=45);
    plt.title('访问和预定人数图',fontsize=20)
    plt.legend(fontsize=20)
    

    从上图中可以看到,520当天预定人数和入住人数都达到峰值,因为情侣会出门“过节”。而从此之后,预订人数为零,入住人数也会一路走低,到了周末,会有小的峰值。

    # 访问时间段
    plt.figure(figsize=(15, 6))
    plt.style.use('seaborn-colorblind')
    
    plt.hist(rawdata['h'].dropna(), bins = 50, edgecolor = 'k');
    plt.xlabel('访问时间'); 
    plt.ylabel('人数'); 
    plt.title('访问时间与人数分布图');
    

    从上图中,我们可以看到,凌晨四五点时的访问人数最少,之后,访问人数总体一路走高,并且23点时达到峰值,将近51000人。

    plt.figure(figsize=(20, 7))
    plt.style.use('seaborn-colorblind')
    x1=rawdata['customer_value_profit'].dropna()
    x2=rawdata['ctrip_profits'].dropna()
    plt.subplot(121)
    plt.plot(x1,linewidth=0.5)
    plt.title('客户近1年价值')
    plt.subplot(122)
    plt.plot(x2,linewidth=0.5)
    plt.title('客户价值')
    
    客户价值

    我们可以清晰地看到,客户近一年的价值图和客户价值图大体上很相似,大多数人分布在0~100的范围内,但不排除有些客户价值非常大,峰值达到了600,这些客户都可以在之后的分析中重点观察,因为他们是非常有“价值”的。

    ## 消费能力指数
    plt.figure(figsize=(10, 6))
    plt.style.use('seaborn-colorblind')
    plt.hist(rawdata['consuming_capacity'].dropna(), bins = 50, edgecolor = 'k');
    plt.xlabel('消费能力指数'); 
    plt.ylabel('人数'); 
    plt.title('消费能力指数图');
    

    从上图中,我们可以看到,消费能力指数的值范围是0-100。消费能力指数值基本呈现一个正态分布,平均消费能力在30附近,我们也能看到消费能力达到近100的人数也特别多,达到了21000多人,从这一点上,我们可以看到,酒店的入住客户中仍然存在较大群体的富裕人士。

    # 价格敏感指数
    plt.figure(figsize=(10, 6))
    plt.style.use('seaborn-colorblind')
    plt.hist(rawdata['price_sensitive'].dropna(), bins = 50, edgecolor = 'k');
    plt.xlabel('价格敏感指数'); 
    plt.ylabel('人数'); 
    plt.title('价格敏感指数图')
    

    在价格敏感指数图中,我们可以看到,出现两头存在极值现象,中间的分布也总体上呈现一个右偏正态分布,大部分人对价格并不敏感,也就是说,对于这些用户来说,价格不是考虑的最重要因素。当然,我们也会发现,价格敏感指数为100时的人数也并不少,针对这一部分客户,我们可以考虑用一些打折优惠的方式。

    # 酒店价格偏好
    plt.figure(figsize=(20, 7))
    plt.style.use('seaborn-colorblind')
    plt.subplot(121)
    plt.hist(rawdata['avgprice'].dropna(), bins = 50, edgecolor = 'k');
    plt.xlabel('酒店价格'); 
    plt.ylabel('偏好人数'); 
    plt.title('酒店价格偏好');
    plt.subplot(122)
    plt.hist(rawdata[rawdata['avgprice']<2000]['avgprice'].dropna(), bins = 50, edgecolor = 'k');
    plt.xlabel('酒店价格'); 
    plt.ylabel('偏好人数'); 
    plt.title('2000元以内酒店偏好');
    
    酒店价格偏好

    从上图中,我们可以看到,总体上,两个图还是呈现正态分布,左图是整体范围的价值偏好,我们可以看到大多数人的价值偏好在125~625元之间,在1500过后就没有什么人了。进一步,我们在更小范围的右图看到,平均价格偏好是在250元左右,在1000元之后就没什么了。

    # 酒店星级偏好
    plt.style.use('bmh')
    plt.figure(figsize=(10, 6))
    plt.hist(rawdata['starprefer'].dropna(), bins = 50, edgecolor = 'k');
    plt.xlabel('星级偏好程度'); plt.ylabel('选择人数'); 
    plt.title('酒店星级偏好');
    

    我们可以看到在酒店星级偏好方面,平均偏好程度是在70,并且也有相当多的人的偏好为40,60,80,说明这一部分用户也很看重酒店星级。

    # 用户年订单数
    plt.figure(figsize=(20, 7))
    plt.style.use('seaborn-colorblind')
    plt.subplot(121)
    plt.hist(rawdata['ordernum_oneyear'].dropna(), bins = 50, edgecolor = 'k');
    plt.xlabel('年订单数'); 
    plt.ylabel('人数'); 
    plt.title('客户年订单数分布');
    plt.subplot(122)
    plt.hist(rawdata[rawdata['ordernum_oneyear']<100]['ordernum_oneyear'].dropna(), bins = 50, edgecolor = 'k');
    plt.xlabel('年订单数'); 
    plt.ylabel('人数'); 
    plt.title('年订单数100单内的分布');
    
    # 会话描述
    # 生成会话表,flag是新客和老客的总人数,rate是新客和老客中最终预定的比率
    s_table=rawdata[['label','sid']]
    s_table['sid']=np.where(s_table['sid']==1,1,0)
    s_table['flag']=1
    s=s_table.groupby('sid').sum().reset_index()
    s['rate']=s['label']/s['flag']                       # flag求和刚好是sid为0和1的个数,label求和刚好是流失人数,相除则为流失率
    # 绘制柱状图
    plt.figure(figsize=(15, 7))
    plt.style.use('seaborn-colorblind')
    label=("老客","新访")
    plt.subplot(121)
    percent=[s['flag'][0]/s['flag'].sum(),s['flag'][1]/s['flag'].sum()]
    colors=['steelblue','lightskyblue']
    plt.pie(percent,autopct='%.2f%%',labels=label,colors=colors)
    plt.title('新老客户占比')
    plt.subplot(122)
    plt.bar(s['sid'],s['rate'],align="center",tick_label=label,hatch="///",edgecolor = 'k')
    plt.ylabel('流失率'); 
    plt.title('新老客户中的客户流失率')
    
    客户结构

    我们可以看到,众多客户中,94.42%的客户是老客户,新客只占5.58%,另外,老客的流失率达到28%,新客的流失率占20%,总体来说,我们应该采取措施,谨防用户流失。

    4.特征工程

    1.列值处理

    ## 增加列
    # 将两个日期变量由字符串转换为日期型格式
    rawdf=rawdata.copy()
    rawdf['arrival']=pd.to_datetime(rawdf['arrival'])
    rawdf['d']=pd.to_datetime(rawdf['d'])
    # 生成提前预定时间列
    rawdf['day_advanced']=(rawdf['arrival']-rawdf['d']).dt.days
    ## 删除列
    rawdf=rawdf.drop(['sampleid','d','arrival'],axis=1)
    

    2.异常值处理

    我们在之前数据可视化的过程中,发现有一些特征值中是存在异常值的,比如用户偏好价格会出现绝对值非常大的负值。因此,我们需要对这些异常值进行一定的处理。

    # 将customer_value_profit、ctrip_profits中的负值按0处理
    # 将delta_price1、delta_price2、lowestprice中的负值按中位数处理(之后可以试一试众数的效果)
    filter1=['customer_value_profit','ctrip_profits']
    filter2=['delta_price1','delta_price2','lowestprice']
    for f in filter1:
        rawdf.loc[rawdf[f]<0,f] = 0
    for f in filter2:
        rawdf.loc[rawdf[f]<0,f] = rawdf[f].median()
    

    3.缺失值处理

    我们之前发现,特征值中除了iforderpv_24h、sid、h、day_advanced之外,其他的44个特征都是存在缺失值的,并且大部分的缺失值都挺多的。

    # 定义删除空值行列的函数
    def nan_drop(df, axi, rate=0.5):
        df.dropna(axis=axi,thresh=df.shape[1-axi]*rate,inplace=True)
    # 删除缺失值比例大于80%的行和列
    print('删除空值前数据维度是:{}'.format(rawdf.shape))
    nan_drop(rawdf,axi=0,rate=0.2)
    nan_drop(rawdf,axi=1,rate=0.2)
    print('删除空值后数据维度是:{}'.format(rawdf.shape))
    

    删除空值前数据维度是:(689945, 49)
    删除空值后数据维度是:(689845, 48)
    可以看到,空值删除操作后,特征值少了一个,historyvisit_7ordernum这一列被删除了,因为这一列的缺失值比例高达88%,数据缺失过多,我们将其删除。

    # 缺失值填充
    def nan_fill(df):
        filter_mean=['businessrate_pre2','cancelrate_pre','businessrate_pre']
        for col in df.columns:
            if col in filter_mean:
                df[col]=df[col].fillna(df[col].mean())
            else:
                df[col]=df[col].fillna(df[col].median())
        return df
    rawdf=nan_fill(rawdf)
    

    以上,我们进行缺失值的填充。趋于正态分布的字段,使用均值填充:businessrate_pre2、cancelrate_pre、businessrate_pre;右偏分布的字段,使用中位数填充。

    4. 极值处理

    for col in rawdf.columns:
        percent1=np.percentile(rawdf[col],1)       # 该列的1%分位数
        percent99=np.percentile(rawdf[col],99)       # 该列的99%分位数
        
        rawdf.loc[rawdf[col]<percent1,col]=percent1    # 小于1%分位数的,用1%分位数填充
        rawdf.loc[rawdf[col]>percent99,col]=percent99    # 大于99%分位数的,用99%分位数填充
    

    5.相关性分析

    # 用户特征的相关性分析
    # 用户特征提取
    user_features=['visitnum_oneyear','starprefer','sid','price_sensitive','ordernum_oneyear','ordercanncelednum','ordercanceledprecent','lastpvgap',
                   'lasthtlordergap','landhalfhours','iforderpv_24h','historyvisit_totalordernum','historyvisit_avghotelnum','h',
                   'delta_price2','delta_price1','decisionhabit_user','customer_value_profit','ctrip_profits','cr','consuming_capacity','avgprice']
    # 生成用户特征的相关性矩阵
    corr_mat=rawdf[user_features].corr()
    
    # 绘制用户特征的相关性矩阵热度图
    fig,ax = plt.subplots(figsize=(18, 12))
    sns.heatmap(corr_mat, xticklabels=True, yticklabels=True, square=False, linewidths=.5, annot=True, cmap='Blues')
    
    用户行为特征相关性矩阵

    从上图中,我们可以清晰地看到用户行为数据中各个特征之间的相关性程度。我们可以看到,ordernum_oneyear和historyvisit_totalordernum的相关性高达0.93,因为它们都是表示用户1年内的订单数,我们选择其中名字更好识别的ordernum_oneyear作为用户年订单数的特征。除此之外,decisionhabit_user和historyvisit_avghotelnum相关性达到了0.89,说明也是高度相关的,说明用户的决策习惯更多由过去的访问记录决定。再就是customer_value_profit和ctrip_profits这两个特征之间相关性达到了0.85,这两个特征我们在上面的数据可视化中就有提到,表示的是不同时间长度下衡量的客户价值,必然是高度相关的,我们可以用PCA的方法提取出一个主成分来代表客户价值这么一个信息。此外,avgprice和consuming_capacity之间的相关性达到了0.91,starprefer与consuming_capacity相关性为0.71,starprefer与avgprice相关性0.66,都比较高。

    # 酒店信息特征的相关性分析
    hotel_features=['hotelcr','hoteluv','commentnums','novoters','cancelrate','lowestprice','cr_pre','uv_pre','uv_pre2','businessrate_pre',
              'businessrate_pre2','customereval_pre2','commentnums_pre','commentnums_pre2','cancelrate_pre','novoters_pre','novoters_pre2',
                    'deltaprice_pre2_t1','lowestprice_pre','lowestprice_pre2','firstorder_bu','historyvisit_visit_detailpagenum']
    # 生成用户特征的相关性矩阵
    corr_mat1=rawdf[hotel_features].corr()
    
    fig,ax = plt.subplots(figsize=(18, 12))
    sns.heatmap(corr_mat1, xticklabels=True, yticklabels=True, square=False, linewidths=.5, annot=True, cmap='Blues')
    
    酒店信息特征相关性矩阵

    我们可以看到,novoters和commentnums相关性高达0.99。前者是当前点评人数,后者是当前点评数,因此,我们将其作为酒店热度指标,novoters_pre和commentnums_pre相关性高达0.99,可以抽象出“24小时内浏览次数最多的酒店热度”指标;novoters_pre2和commentnums_pre2相关性高达0.99,可以抽象出“24小时内浏览酒店平均热度”指标。uv_pre和uv_pre2相关性高达0.9;businessrate_pre和businessrate_pre2相关性高达0.84;commentnums_pre和commentnums_pre2相关性高达0.82;novoters_pre和novoters_pre2相关性高达0.83。

    5.RFM模型分析与用户画像分析

    1.RFM模型分析

    根据美国数据库营销研究所Arthur Hughes的研究,客户数据库中有三个神奇的要素,这三个要素构成了数据分析最好的指标

    • 最近一次消费(Recency)
    • 消费频率(Frequency)
    • 消费金额(Monetary)


      RFM模型
    # 进行归一化
    # 数据标准化
    from sklearn.preprocessing import MinMaxScaler
    
    scaler = MinMaxScaler()
    scaler.fit(rfm)
    rfm = scaler.transform(rfm)
    rfm = rawdf[['lasthtlordergap','ordernum_oneyear','consume_level']]
    rfm.rename(columns={'lasthtlordergap':'recency','ordernum_oneyear':'frequency','consume_level':'monetary'},inplace=True)
    rfm.head()
    # 分箱
    rfm['R']=pd.qcut(rfm["recency"], 2)
    rfm['F']=pd.qcut(rfm["frequency"], 2)
    rfm['M']=pd.qcut(rfm["monetary"], 2)
    # 编码
    from sklearn.preprocessing import LabelEncoder
    le = LabelEncoder().fit(rfm['R'])
    rfm['R']=le.transform(rfm['R'])
    le = LabelEncoder().fit(rfm['F'])
    rfm['F']=le.transform(rfm['F'])
    le = LabelEncoder().fit(rfm['M'])
    rfm['M']=le.transform(rfm['M'])
    
    def get_label(r,f,m):
        if (r==0)&(f==1)&(m==1):
            return '高价值客户'
        if (r==1)&(f==1)&(m==1):
            return '重点保持客户'
        if((r==0)&(f==0)&(m==1)):
            return '重点发展客户'
        if (r==1)&(f==0)&(m==1):
            return '重点挽留客户'
        if (r==0)&(f==1)&(m==0):
            return '一般价值客户'
        if (r==1)&(f==1)&(m==0):
            return '一般保持客户'
        if (r==0)&(f==0)&(m==0):
            return '一般发展客户'
        if (r==1)&(f==0)&(m==0):
            return '潜在客户'
    
    def RFM_convert(df):
        df['Label of Customer']=df.apply(lambda x:get_label(x['R'],x['F'],x['M']),axis=1)
        
        df['R']=np.where(df['R']==0,'高','低')
        df['F']=np.where(df['F']==1,'高','低')
        df['M']=np.where(df['M']==1,'高','低')
        
        return df[['R','F','M','Label of Customer']]
    
    rfm0=RFM_convert(rfm)
    rfm0.head(10)
    
    rfm0

    下面我们可以看一下各类客户的占比:

    tmp = rfm0.groupby('Label of Customer').size()
    fig, ax = plt.subplots(figsize=(10,10))
    colors=['deepskyblue','steelblue','lightskyblue','aliceblue','skyblue','cadetblue','cornflowerblue','dodgerblue']
    ax.pie(tmp.values, radius=1,autopct='%1.1f%%',pctdistance=0.75,colors=colors)
    ax.pie([1], radius=0.6,colors='w')
    ax.set(aspect="equal", title='客户细分情况')
    plt.legend(tmp.index,bbox_to_anchor=(1, 1), loc='best', borderaxespad=0.)
    plt.show()
    
    各类客户占比细分图

    从上图,我们可以看到,五成左右的客户属于一般客户,高价值客户仅占一成,而重要保持客户、重要发展客户、重要挽留客户总和占两成左右,这一类客户需要重点培养。

    2.用户画像

    接下来,我们将用K-Means聚类的方法将用户分为3类,观察不同类别客户的特征,从而为精准营销指明方向。

    # 选取出几个刻画用户的重要指标
    user_feature = ['decisionhabit_user','ordercanncelednum','ordercanceledprecent','consume_level','starprefer','lasthtlordergap','lastpvgap','h','sid',
                    'c_value','landhalfhours','price_sensitive','price_prefer','day_advanced','historyvisit_avghotelnum','ordernum_oneyear']
    user_attributes = rawdf[user_feature]
    user_attributes.head()
    

    数据标准化

    # 数据标准化
    from sklearn.preprocessing import StandardScaler
    
    scaler = StandardScaler()
    scaler.fit(user_attributes)
    
    user_attributes = scaler.transform(user_attributes)
    

    进行Kmeans聚类

    from sklearn.cluster import KMeans
    Kmeans=KMeans(n_clusters=3,random_state=13)                                     # 建立KMean模型
    Kmeans.fit(user_attributes)                                                     # 训练模型
    k_char=Kmeans.cluster_centers_                                                  # 得到每个分类的质心
    personas=pd.DataFrame(k_char.T,index=user_feature,columns=['0类','1类','2类'])  # 用户画像表
    personas
    
    fig,ax = plt.subplots(figsize=(4, 8))
    sns.heatmap(personas, xticklabels=True, yticklabels=True, square=False, linewidths=.5, annot=True, cmap='Blues')
    

    上图中,我们可以看到,通过:KMEANS聚类出来的三个类,
    可以看到,2类中颜色普遍更深,它的R(lasthtlordergap)为-0.17非常小,F(ordernum_oneyear)为1.1比较高了,M(consume_level)为1.3也几乎是最高的。很明显,2类客户为我们的“高价值客户”;而1类中几乎都是白格子,尤其RFM中三个关键指标都很低,无论是客户价值还是消费水平值都是最低的,很明显,这一类我们将其归为“低价值客户”;剩下的0类我们将其称为“中等价值客户”。
    将上述三类用户可视化。

    plt.figure(figsize=(9,9))
    class_k=list(Kmeans.labels_)                          # 每个类别的用户个数
    percent=[class_k.count(0)/len(user_attributes),class_k.count(1)/len(user_attributes),class_k.count(2)/len(user_attributes)]   # 每个类别用户个数占比
    
    fig, ax = plt.subplots(figsize=(10,10))
    colors=['aliceblue','steelblue','lightskyblue']
    types=['中等群体','低价值用户','高价值用户']
    ax.pie(percent,radius=1,autopct='%.2f%%',pctdistance=0.75,colors=colors,labels=types)
    ax.pie([1], radius=0.6,colors='w')
    plt.show()
    
    不同价值用户构成图

    我们可以清楚地看到,在我们的众多客户中,“低价值客户”占近76%,而高价值用户占到17%,中等人群占比最小。

    3.用户分析

    3.1 高价值用户分析

    该类客户消费能力指数高,对于酒店而言,客户价值高,对酒店星级要求较高,访问频率和预定频率都较高,预定决策一般都比较迅速(日均访问数少),而且订单取消率较高,可以分析出这类客户商务属性偏重,因为可能随时要出差,因此不会提前预定。sid的值较大,说明高价值客户群体大多属于老客户,并且价格敏感度较高,说明也比较要求性价比。h值非常小,可能访问和预定时间多在凌晨。
    这一类客户对于我们来说是非常重要的,因此我们需要实行更加精细化营销:
    1、多推荐口碑好、性价比高的商务酒店。
    2、推荐时间集中在半夜或是清晨。

    3.2中等群体分析

    这一类客户消费水平和客户价值比较低,对酒店品质也不太追求,访问和预定频率也都较高,提前预定的时间是三类中最长的,最值得注意的是,0类客户中有两个颜色非常深的蓝色格子,是用户决策和近3个月的日均访问数。我们可以看出,这类客户通常很喜欢逛酒店界面,要花很长时间才能做出预定决策。我们可以合理推断,这一类客户,可能预定酒店的目的多为出门旅行。
    针对这部分客户,我们需要:
    1、尽可能多地进行推送,因为这一类客户花很长时间在浏览上。
    2、推送当地旅游资讯,因为这类客户旅游出行的概率较大。
    3、多推荐价格相对实惠的酒店。

    3.3 低价值用户分析

    这一类客户,消费水平和客户价值极低,对酒店品质不追求,偏好价格较低,对价格较为敏感,决策时间很短,访问和预定频率很低,sid值很低,说明新客户居多。
    针对这部分客户,我们需要:
    1、不建议花费过多营销成本,但因为新用户居多,属于潜在客户,可以维持服务推送。
    2、推送的内容应多为大减价、大酬宾、跳楼价之类的。
    3、此类用户占比居多,可进一步进行下沉分析,开拓新的时长。

    相关文章

      网友评论

        本文标题:酒店预订网客户流失分析

        本文链接:https://www.haomeiwen.com/subject/onhiectx.html