阅读路线
项目介绍:该项目对某酒店预订网在一段时间内的客户预定信息数据进行分析,其中着重对该网站整体消费情况和用户行为展开分析,找出高价值用户人群,对客户进行用户画像分析,从而为该网站的精细化营销提供相关建议。
代码展示
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.特征工程
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)
4.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()
4.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.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%分位数填充
4.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模型分析与用户画像分析
5.1 RFM模型分析
根据美国数据库营销研究所Arthur Hughes的研究,客户数据库中有三个神奇的要素,这三个要素构成了数据分析最好的指标
- 最近一次消费(Recency)
- 消费频率(Frequency)
- 消费金额(Monetary)
# 进行归一化
# 数据标准化
from sklearn.preprocessing import MinMaxScalerscaler = 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)
下面我们可以看一下各类客户的占比:
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()
从上图,我们可以看到,五成左右的客户属于一般客户,高价值客户仅占一成,而重要保持客户、重要发展客户、重要挽留客户总和占两成左右,这一类客户需要重点培养。
5.2 用户画像
(1)K-Means聚类
接下来,我们将用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 StandardScalerscaler = 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%,中等人群占比最小。
(2)用户分析
(a)高价值用户分析
该类客户消费能力指数高,对于酒店而言,客户价值高,对酒店星级要求较高,访问频率和预定频率都较高,预定决策一般都比较迅速(日均访问数少),而且订单取消率较高,可以分析出这类客户商务属性偏重,因为可能随时要出差,因此不会提前预定。sid的值较大,说明高价值客户群体大多属于老客户,并且价格敏感度较高,说明也比较要求性价比。h值非常小,可能访问和预定时间多在凌晨。
这一类客户对于我们来说是非常重要的,因此我们需要实行更加精细化营销:
1、多推荐口碑好、性价比高的商务酒店。
2、推荐时间集中在半夜或是清晨。
(b)中等群体分析
这一类客户消费水平和客户价值比较低,对酒店品质也不太追求,访问和预定频率也都较高,提前预定的时间是三类中最长的,最值得注意的是,0类客户中有两个颜色非常深的蓝色格子,是用户决策和近3个月的日均访问数。我们可以看出,这类客户通常很喜欢逛酒店界面,要花很长时间才能做出预定决策。我们可以合理推断,这一类客户,可能预定酒店的目的多为出门旅行。
针对这部分客户,我们需要:
1、尽可能多地进行推送,因为这一类客户花很长时间在浏览上。
2、推送当地旅游资讯,因为这类客户旅游出行的概率较大。
3、多推荐价格相对实惠的酒店。
(c) 低价值用户分析
这一类客户,消费水平和客户价值极低,对酒店品质不追求,偏好价格较低,对价格较为敏感,决策时间很短,访问和预定频率很低,sid值很低,说明新客户居多。
针对这部分客户,我们需要:
1、不建议花费过多营销成本,但因为新用户居多,属于潜在客户,可以维持服务推送。
2、推送的内容应多为大减价、大酬宾、跳楼价之类的。
3、此类用户占比居多,可进一步进行下沉分析,开拓新的时长。