【数据分析报告】携程客户分析与流失预测

一、项目背景与目的

携程作为中国领先的综合性旅行服务公司,每天向超过2.5亿会员提供全方位的旅行服务,因此每天都会产生海量的用户行为数据,这些数据蕴含着丰富的信息资源。另外,客户是企业的重要资源,也是企业的无形资产,客户的流失,也就意味着资产的流失,因此客户流失率是考量业务成绩的一个非常关键的指标。

本项目致力于深入了解用户画像及行为偏好,找到最优算法,挖掘出影响用户流失的关键因素。从而能更好地完善产品设计、提升用户体验,针对不同类型的用户给出个性化运营策略。

本报告可以分为一下几个部分:

  1. 探索性分析
  2. 数据预处理与特征工程
  3. 用户流失预测
  4. RFM与用户画像分析

二、探索性分析

官方共提供2个数据集,分别为训练集userlostprob_train.txt和测试集userlostprob_test.txt。训练集为2016.05.15-2016.05.21期间一周的访问数据,测试集为2016.05.22-2016.05.28期间一周的访问数据。

本项目的评估标准官方用的是precision≥97%下,recall的最大值。我自己的话,选择准确度、AUC值。

精确度:(预测为流失且实际发生流失的样本数量)/(预测为流失的样本数量)
召回率:(预测为流失且实际发生流失的样本数量)/(实际流失的样本数量)

2.1 数据指标预览

查看数据集各特征字段,其中,label=1代表流失客户,label=0代表非流失客户。其他指标主要可以分为三种类型的数据指标:

  • 用户相关特征:访问时长、访问次数、访问酒店数、使用时间、价格偏好、星级偏好、消费能力、价格敏感指数、用户价值
  • 酒店相关特征:独立访问用户数、评论人数、评论数、历史取消率、酒店平均价格、最低价、商务属性指数等
  • 订单相关特征:历史订单数、取消率、下单距离时长、访问日期、入住日期等

请添加图片描述

2.2 数据概况

#导入基础包
%matplotlib inline
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt

import warnings
warnings.filterwarnings("ignore")


# 解决中文乱码问题
plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False #用来正常显示负号

#显示全部特征
pd.set_option('display.max_columns', None)

#读取数据
df = pd.read_csv('./data/userlostprob_train.txt',sep='\t')
df.head()

查看数据情况,数据总体有一些特点:

  • 除去标签id、label,一共有49个特征
  • 数据形状(689945, 51)
  • label流失用户有189357个,非流失用户有500588个。总体来看,流失用户占了27.4%

继续查看数据信息

# 查看每列数据
df.info()

# 查看数据形状
df.shape  # (689945, 51)

# label分布
df.label.value_counts()
# 0    500588
# 1    189357

可以看出:

  • 除了darrival列特征为离散型特征(字符),其余均为连续性的数值型特征
  • 时间格式:arrivald的时间格式可以进行转换,并增加衍生字段来获取间隔天数
  • 正负样本比例是:1: 2.7。有一点样本不平衡(因此评估指标加入AUC)

描述性统计

df.describe()

由描述性统计观察数据集的位置集中趋势(平均值、中位数、众数),离散程度(方差、标准差、离散系数、四分位差),数据分布的统计图形(偏斜系数)。

由描述性统计可以看出,数据存在以下问题:

  • 字段缺失,特征列存在不同程度的缺失情况,因此部分特征列的count数不全为689945,如historyvisit_7ordernum仅有82915条,存在数据缺失。后面进行缺失值填充的时候要注意分布的形态。
  • 不应为负的数据特征列存在负值的情况,如:delta_price1(用户偏好价格-24小时浏览最多酒店价格)、lowestpricedelta_price2customer_value_profit(客户近一年的价值),这些负值属于异常情况,后面需要对负值进行处理
  • 数据特征列存在极值情况,方差很大,这样的数据需要对其极值进行处理。
  • 数据分布形状呈现偏态,部分字段数据分布形态呈偏态状,如decisionhabit_user看出可能呈现右偏态的形式。后面对数据的缺失填充、异常值处理时,要结合偏态状况考虑。

查看缺失值比例

#查看缺失值比例
df.isnull().mean().sort_values(ascending=False)

看出字段缺失情况严重,其中historyvisit_7ordernum缺失值高达88%。除了arrival,d,h,sampleid,iforderpv_24h,sid,label外,其余44列字段各有不同程度缺失。因此后面要根据缺失情况,结合数据特征分布,选用合适的方法填充缺失值。

2.3 数据分布

查看数据分布情况,有助于特征工程根据数据分布选择合适的数据处理办法(包括缺失值、异常值处理,连续特征离散化),还有助于深入了解用户行为。

2.3.1 数据分布总览
# 数据分布偏态情况
df.skew().sort_values()

当数据呈左右对称分布时,偏度系数等于0。偏度系数大于1或小于-1,视为严重偏斜分布;偏度系数为0.5~1或-1~-0.5,视为中等偏斜分布;偏度系数为**-0.5~0或0~0.5**,视为轻微偏斜分布。

在这里插入图片描述
由上面可以看出,除了businessrate_pre2, businessrate_pre, customereval_pre2,其他数据基本都呈很大的偏态分布。

# 查看数据分布图
df.hist(figsize=(20,20))
plt.savefig('./images/data_distribution_raw.png')

请添加图片描述

2.3.2 预定日期和入住日期
# copy一份数据保存
cdf = df.copy()

# 构建访问时间和到达时间的表格
cdf_d = cdf.d.value_counts().to_frame().reset_index()
cdf_arrival = cdf.arrival.value_counts().to_frame().reset_index()
time_table = cdf_d.merge(cdf_arrival, how='outer', on='index')
time_table.fillna(0, inplace=True)
time_table.set_index('index',inplace=True)
time_table.sort_index(inplace=True)

# 获取字段
x = time_table.index
y1 = time_table.arrival
y2 = time_table.d

# 画图
plt.figure(figsize=(14,5))
plt.style.use('seaborn-colorblind')
plt.plot(x, y1, c='orange', label='入住人数')
plt.bar(x, y2, align='center', label='预定人数')
plt.title('访问和入住人数图',fontsize=20)
plt.xticks(rotation=45,fontsize=12)
plt.yticks(fontsize=14)
plt.xlabel('日期',fontsize=14)
plt.ylabel('人数', fontsize=14)
plt.legend(fontsize=14)

在这里插入图片描述
由图看出,520前预定人数和入住人数逐渐攀升,在520当天达到峰值,过了521,入住人数断崖式下降,随后酒店入住人数较为稳定,后面的两个下波峰是由于周末的原因。

2.3.3 访问时间段
plt.figure(figsize=(15,6))
plt.hist(cdf.h.dropna(), bins=48, align='mid')  # 由于是24h,所以分箱48,使得中间有间隔。
plt.title('访问时间段',fontsize=20);
plt.xticks(fontsize=14)
plt.yticks(fontsize=14)
plt.xlabel('访问时间',fontsize=14); 
plt.ylabel('人数',fontsize=14);

在这里插入图片描述
由访问时间段可以看出,在 凌晨四五点的时候访问人数最少,此时大多数人在睡觉,因此这符合人的作息。随后访问人数在白天总体呈上升趋势,在17点-19点时稍微回落,因为此时是人的下班通勤时间或者晚饭时间,过了这段时间访问人数又开始逐渐上升,在22时达到峰值。

2.3.4 客户价值
plt.figure(figsize=(12, 4))
plt.style.use('bmh')

# 看看customer_value_profit 和 ctrip_profits 两者分布
plt.subplot(121)
plt.plot(cdf.index, cdf.customer_value_profit,linewidth=0.5)
plt.title('客户近1年价值')

plt.subplot(122)
plt.plot(df.index,df.ctrip_profits,linewidth=0.5)
plt.title('客户价值')

plt.savefig('./images/客户价值.png')

请添加图片描述

  • 客户近一年的价值图和客户价值图大体上很相似,大多数人分布在0~100的范围内。另外,由于两个特征分布上非常接近,后面可以对customer_value_profitctrip_profits进行相关性分析验证,如果相关系数很大,可以考虑进行PCA降维。
  • 不排除有些客户价值非常大,峰值达到了600,这些客户都可以在之后的分析中重点观察,因为他们是非常有“价值”的。但是这些峰值过大的客户,数据可能存在极值点过大的情况,因此需要对数据进一步处理。
  • 另外,可以看出,两个字段都存在部分数据的客户价值为负,这些是异常值,需要处理。
2.3.5 消费能力指数
plt.figure(figsize=(12, 4))

plt.hist(df.consuming_capacity,bins=50,edgecolor='k')
plt.xlabel('消费能力指数')
plt.ylabel('人数')
plt.title('消费能力指数图')
plt.savefig('./images/消费能力指数图.png')

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

2.3.6 价格敏感指数分布
plt.figure(figsize=(12, 6))
plt.hist(df['price_sensitive'].dropna(),bins = 50, edgecolor = 'k')
plt.xlabel('价格敏感指数') 
plt.ylabel('人数') 
plt.title('价格敏感指数分布')
plt.savefig('./images/价格敏感指数分布.png')
plt.show()

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

2.3.6 入住酒店平均价格
plt.figure(figsize=(12, 4))
plt.subplot(121)
plt.hist(df.avgprice.dropna(),bins=50,edgecolor = 'k')
plt.xlabel('酒店价格')
plt.ylabel('偏好人数')
plt.title('酒店价格偏好')


# 由于酒店价格主要在2000以内,因此针对这个区间进行进一步可视化查看
plt.subplot(122)
plt.hist(df[df.avgprice<2000]['avgprice'].dropna(), bins = 50, edgecolor = 'k')
plt.xlabel('酒店价格')
plt.ylabel('偏好人数')
plt.title('2000元以内酒店偏好')

plt.savefig('./images/酒店偏好.png')

请添加图片描述
看出酒店价格偏好呈现一个正太分布微左偏态的分布,大多数人的价值偏好在150~600元之间,,在1500过后就没有什么人了。平均价格在250左右。

2.3.7 酒店星级偏好
plt.figure(figsize=(10, 4))
plt.hist(df.starprefer.dropna(), bins = 50, edgecolor = 'k')
plt.xlabel('星级偏好程度')
plt.ylabel('选择人数')
plt.title('酒店星级偏好')
plt.savefig('./images/酒店星级偏好.png')

请添加图片描述
分布规律性没有酒店价格偏好强,在40、60、80、100的分段存在极值情况,后面可以这些极值情况进行数据预处理。但总体来看,星级偏好主要集中在60~80之间。

2.3.8 订单取消率
plt.figure(figsize=(10, 4))
plt.hist(df.ordercanceledprecent.dropna(),bins=50,edgecolor = 'k')
plt.xlabel('订单取消率')
plt.ylabel('人数')
plt.title('订单取消率')

plt.savefig('./images/订单取消率.png')

请添加图片描述
存在大量的用户订单取消率为0的情况,说明大多数用户订了酒店后就会入住。而同时也存在部分极端用户,订单取消率为1的情况。订单取消率为0.5的用户第三多。

2.3.9 用户年订单数分布
plt.figure(figsize=(12, 6))
plt.hist(df[df["ordernum_oneyear"]<100]["ordernum_oneyear"].dropna(),bins = 50, edgecolor = 'k')
plt.xlabel('用户年订单数') 
plt.ylabel('数量') 
plt.title('用户年订单数100内的分布')
plt.savefig('./images/用户年订单数100内的分布.png')
plt.show()

请添加图片描述
绝大部分用户年订单数是小于50的,订单数在5次之内的人数占比比较大

2.3.10 新老客户流失率
# 新老客户,可以由sid来判断。流失与否,用label来判断
# 计算新老用户流失率
s_table = cdf[['label','sid']]
s_table['sid'] = np.where(s_table['sid']==1, 1, 0)  # 将sid处理为0和1两种情况,对应新客户和老客户
s_table['flag'] = 1  # 
s = s_table.groupby('sid').sum().reset_index()  # 按照新老用户区分,label是流失和没流失的人数,flag是新、老用户数
s['rate'] = s['label'] / s['flag']  # 新老用户流失率

# 画图
# 新老客户占比
plt.figure(figsize=(12, 5))
plt.subplot(121)
percent=[s['flag'][0]/s['flag'].sum(), s['flag'][1]/s['flag'].sum()]
# color=['steelblue','lightskyblue']
label=['老客','新客']
plt.pie(percent,autopct='%.2f%%',labels=label)
plt.title('新老客户占比')

# 流失率
plt.subplot(122)
plt.bar(s.sid, s.rate,align='center',tick_label=label,edgecolor = 'k')
plt.ylabel('流失率')
plt.title('新老客户中的客户流失率');

请添加图片描述
我们可以看到,众多客户中,94.42%的客户是老客户,新客只占5.58%,另外,老客的流失率达到28%,新客的流失率占20%,总体来说,我们应该采取措施,谨防用户流失,并且采取拉新手段获取更多新客户。

三、数据预处理

根据探索性分析中观察到的结果,我们需要对数据进行一系列预处理,包括数据格式转换、缺失值处理、异常值处理。

rawdf = df.copy()

3.1 去除不需要的字段与重复字段

sampleid列表示的是每一条的样本记录,firstorder_bu列表示首笔订单的bu,从实际意义来看,对用户是否流失影响不大。另外,label列也要去掉。

drop_columns = ['sampleid', 'firstorder_bu' ]
rawdf.drop(drop_columns, axis=1, inplace=True)

rawdf.drop_duplicates(inplace=True)

3.2 数据类型转换

时间特征处理
访问日期d和入住日期arrival字段为字符串类型,从实际意义来看,将其转换为星期几的int类型是种更好的方式。

另外是否为周末对实际的用户行为影响颇大,需新增判断是否为周末的特征列。

另外预定与入住间隔时间越久,用户的决策受影响的风先越大,所以也新构造关于间隔天数的特征列。

# 将两个日期变量由字符串转换为日期格式
rawdf['arrival'] = pd.to_datetime(rawdf['arrival'] )
rawdf['d'] = pd.to_datetime(rawdf['d'])

# 生成提前预定天数(衍生变量)(到达日期-访问日期间隔)(看提前多少天订)
rawdf['day_advanced'] = (rawdf['arrival']-rawdf['d']).dt.days

# 时间格式
rawdf['d'] = pd.to_datetime(df['d'], format = '%Y-%m-%d')
rawdf['arrival'] = pd.to_datetime(df['arrival'], format='%Y-%m-%d')

# 用户周几入住
rawdf['arrival_weekday'] = rawdf['arrival'].map(lambda x:x.weekday())

# 用户入住那天是否为休息日
def is_weekend(a):
    if int(a) in [0,1,2,3,4]:
        return 0 # 0代表是工作日
    else:
        return 1 # 1代表是周末

rawdf['is_arrival_weekend'] = rawdf['arrival_weekday'].map(lambda x: is_weekend(x))

rawdf.drop(labels=['d','arrival'], axis=1, inplace=True)

3.3 异常值处理

3.3.1负数处理

结合探索性分析中观察到的可视化图,

  • delta_price1(用户偏好价格-24h浏览最多酒店价格)、delta_price2(用户偏好价格-24h浏览酒店平均价格)、lowestprice(当前酒店可定最低价格)三者理论上酒店价格不可能为负,并且由可视化图观察到数据分布比较集中,因此负值采取中位数处理。
  • customer_value_profit(客户价值_近1年)、ctrip_profits(客户价值)也不应该为负值,结合可视化数据分布图看出它们的分布较为分散,因此将其填充为0
filter_one=['customer_value_profit','ctrip_profits']
filter_two=['delta_price1','delta_price2','lowestprice']

for f in filter_one:
    rawdf.loc[rawdf[f]<0, f] = 0

for f in filter_two:
    rawdf.loc[rawdf[f]<0, f] = rawdf[f].median()
    
rawdf[['customer_value_profit','ctrip_profits','delta_price1','delta_price2','lowestprice']].describe()

在这里插入图片描述

3.3.2 极值处理

由探索性分析中数据分布情况害能看出,较多特征有极大和极小的异常值,比如上文可视化的customer_value_profit,ctrip_profits,starprefer等。因此对所有字段使用1%和99%分位数替换超过上下线的值。

for i in rawdf.columns:
    value_1_percent =  np.percentile(rawdf[i], 1)  # # 1%的值
    value_99_percent = np.percentile(rawdf[i], 99) # 99%的值
    
    rawdf.loc[rawdf[i]<value_1_percent, i] = value_1_percent
    rawdf.loc[rawdf[i]>value_99_percent, i] = value_99_percent

# 查看表现
rawdf.skew().sort_values()

在这里插入图片描述

3.4 缺失值处理

常用缺失值处理方法

  • 不处理(针对类似 XGBoost 等树模型);
  • 删除(缺失数据太多);
  • 插值补全,包括均值/中位数/众数/建模预测/多重插补/压缩感知补全/矩阵补全等;
  • 分箱,缺失值一个箱;
3.4.1 删除

对于缺失率>80%的特征,删除对应的字段与特征

print('原来数据维度是:{}'.format(rawdf.shape))

# 定义删除空值行列的函数
def nan_drop(df,axi, rate=0.5):<
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值