阅读路线
项目介绍:该项目对国外某一CD网站的用户购买数据进行分析,主要围绕平台整体消费情况和用户行为展开分析叙述,通过分析该网站基本消费情况,找出高价值用户人群,及用户留存、活跃、流失等情况,为平台指定策略提供数据分析支持和建议。
代码展示:
1.初始设置
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
plt.style.use('seaborn-colorblind')
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus']=False
2.导入数据,查看信息
Column=['user_id','order_dt','order_products','order_amount']
df=pd.read_table('E:/CDNOW_master.txt',sep='\s+',names=Column)
df.info()
1.png
原始数据共有4个字段,共69659行,数据没有缺失值,下面是各字段含义:
user_id:用户id
order_dt:订单时间
order_products:订单购买产品数
order_amount:订单消费金额
以上,order_dt为整数类型,显然需要进行数据类型转化,而且为方便后续对月份的分析,需要增加一个新的字段month,表示订单的月份
3.整体消费情况
2.png
从上图可以得出:
订单金额:平均每笔订单的消费金额约36元,中位数约26元,标准差约36元,说明存在极值的干扰。
订单产品数:平均每笔订单购买产品数为2.4件,中位数为2件,75%的订单产品数为3件,说明大多数用户单笔订单都是2或3件,存在极值干扰。
3.png
group_month=df.groupby('month')
fig,axes=plt.subplots(2,2,figsize=(20,10))
axes0,axes1,axes2,axes3=axes.flatten()
# 每月销售金额
group_month.order_amount.agg('sum').plot(marker='o',ax=axes0)
axes0.set_title('每月消费金额')
axes0.set_ylabel('消费金额')
# 每月销售产品数
group_month.order_products.sum().plot(marker='o',ax=axes1)
axes1.set_title('每月销售产品数')
axes1.set_ylabel('销售产品数')
# 每月订单数
group_month.order_products.count().plot(marker='o',ax=axes2)
axes2.set_title('每月订单数')
axes2.set_ylabel('订单数')
group_month.user_id.unique().apply(lambda x:len(x)).plot(marker='o',ax=axes3)
axes3.set_title('每月客户数')
axes3.set_ylabel('客户数')
4.png
由上述图表可得,平台的销售金额、销售产品数和订单数都是比较高的,且都是在3月份左右出现峰值,单月销售金额最高达到40万美元、销售产品数最高达到2.7万件、订单数最高达到1.2万单、购买客户数在2月份左右就达到峰值9500人左右。但从第4个月开始,平台各项销售指标出现严重的下滑,顾客流失现象严重。
fig,axes=plt.subplots(1,2,figsize=(15,6))
axes0,axes1=axes.flatten()
# 每月客单价
group_month_pct=group_month.order_amount.sum()/group_month.user_id.unique().apply(lambda x:len(x))
group_month_pct.plot(marker='o',ax=axes0)
axes0.set_title('每月客单价')
axes0.set_ylabel('客单价')
# 每月件单价
group_month_up=group_month.order_amount.sum()/group_month.order_products.sum()
group_month_up.plot(ax=axes1,marker='o')
axes1.set_title('每月件单价')
axes1.set_ylabel('件单价')
5.png
从图中可以看出,前四月份的客单价稳步提升的,后几个月在46~57之间波动,前几个月客单价低可能和商家给的优惠政策相关。
件单价分析:前四月份,件单价相对较高,后几个月波动较大,尤其是在九月份和第二年的四月份。
group_user=df.groupby('user_id')
group_user_des=group_user[['order_amount','order_products','order_dt',]].agg({"order_amount":'sum','order_products':'sum','order_dt':'count'}).describe()
group_user_des.rename(columns={'order_dt':'消费次数','order_amount':'消费总金额','order_products':'产品购买数(件)'})
6.png
1、用户消费总金额:用户平均消费金额为106元,中位数为43元,标准差约为241,存在极值干扰;
2、用户消费次数:用户平均消费次数为3次,中位数为1次,标准差为4.7,同样,也存在着极值干扰;
3、用户产品购买数:用户平均购买产品数为7件,中位数为3件,标准差为16,同样也是有相当大的极值干扰。
fig,axes=plt.subplots(1,3,figsize=(16,4))
axes0,axes1,axes2=axes.flatten()
# 用户消费金额的分布情况
group_user.sum().query('order_amount<1000').order_amount.plot.hist(bins=20,ax=axes0)
axes0.set_title('用户消费金额的分布情况')
axes0.set_xlabel('消费金额')
axes0.set_ylabel('人数')
# 用户消费次数的分布情况
group_user.sum().query('order_products<100').order_products.plot.hist(bins=20,ax=axes1)
axes1.set_title('用户消费次数的分布情况')
axes1.set_xlabel('消费次数')
axes1.set_ylabel('人数')
# 用户购买产品数量的分布情况
group_user.count().query('order_dt<20').order_dt.plot.hist(bins=20,ax=axes2)
axes2.set_title('用户购买产品数量的分布情况')
axes2.set_xlabel('购买产品数量')
axes2.set_ylabel('人数')
7.png
从上图可以看出,用户购买金额普遍偏低,消费频次也是普遍偏低,当然这也符合销售数据的一般规律。
group_user.sum().query('order_amount<4000').plot.scatter(x='order_amount',y='order_products',title='用户消费金额与产品购买数量散点图',figsize=(6,4))
8.png
由上图可得,我们可以清楚的发现用户消费金额与产品购买数量之间存在一定的线性关系,当然,这也并不奇怪,因为该网站的产品单一,价格波动不大,由此产生了这样的线性关系。
group_user_cum=group_user.sum().sort_values('order_amount').apply(lambda x:x.cumsum()/x.sum())
group_user_cum.reset_index().order_amount.plot(figsize=(6,4))
plt.title('用户消费金额占比')
plt.xlabel('用户数量')
plt.ylabel('消费金额占比')
9.png
从上图可以明显的看出,该CD网站的用户消费金额存在着典型的“二八分布”,大约16%的头部用户贡献了60%的销售额,而超过60%的用户仅仅贡献了不到20%的销售额。
group_user=df.groupby('user_id')
fig,axes=plt.subplots(1,2,figsize=(12,4))
axes0,axes1=axes.flatten()
# 用户第一次购买的时间分布
group_user.order_dt.min().value_counts().plot(ax=axes0)
axes0.set_title('用户首购时间分布')
axes0.set_ylabel('人数')
# 用户最后一次购买的时间分布
group_user.order_dt.max().value_counts().plot(ax=axes1)
axes1.set_title('用户最后一次购买的时间分布')
axes1.set_ylabel('人数')
10.png
我们可以看出,大多数用户在前四个月就流失了,因此,也验证了此前提出的与首购的优惠有关的可能性。
group_user_diff=df.sort_values('order_dt').groupby('user_id').apply(lambda x:(x.order_dt-x.order_dt.shift())/np.timedelta64(1,'D'))
group_user_diff.describe()
11.png
我们可以看出,用户平均购买周期约为69天,中位数为31天,可以根据用户的购买周期对用户进行定期召回。
group_user_lifecycle=group_user.order_dt.apply(lambda x:x.max()-x.min())/np.timedelta64(1,'D')
# 用户生命周期为0天的用户占比
lifecycle_0=len(group_user_lifecycle[group_user_lifecycle==0])/len(df.user_id.unique())
lifecycle_not0=len(group_user_lifecycle[group_user_lifecycle>0])/len(df.user_id.unique())
values=[lifecycle_0,lifecycle_not0]
label=['用户只消费一次','用户消费不止一次']
fig = plt.figure(figsize=(8,8))
plt.pie(values,labels=label,autopct='%1.1f%%')
plt.title('用户消费次数占比')
plt.show()
12.png
我们再一次清楚地看到,大多数用户仅仅消费了一次,很有必要对用户进行定期召回。
rfm=group_user['order_products','order_amount'].sum()
rfm['R']=-(group_user.order_dt.max()-df.order_dt.max())/np.timedelta64(1,"D")
rfm.rename(columns={'order_products':'F','order_amount':'M'},inplace=True)
rfm=rfm[['R',"F",'M']]
def level_label(x):
level=x.apply(lambda x:'1' if x> 0 else '0')
model=level.R+level.F+level.M
dic={
"111":"重要价值客户",
'101':'重要发展客户',
"011":"重要维护客户",
'001':'重要挽留客户',
'001':'重要挽留客户',
'110':'一般价值客户',
'100':'一般发展客户',
'010':'一般维持客户',
'000':'一般挽留客户',
}
return dic[model]
rfm['label']=rfm.apply(lambda x:x-x.mean()).apply(level_label,axis=1)
fig,axes=plt.subplots(1,3,figsize=(14,4))
axes0,axes1,axes2=axes.flatten()
rfm_per=rfm.groupby('label').M.agg(['count','sum']).apply(lambda x :x/x.sum())
rfm_per['avg']=rfm_per['sum']/rfm_per['count']
rfm_per['count'].plot.bar(ax=axes0)
axes0.set_title('用户分层人数占比')
axes0.set_xlabel('人数占比')
rfm_per['sum'].plot.bar(ax=axes1)
axes1.set_title('用户分层消费金额占比')
axes1.set_xlabel('消费金额占比')
rfm_per['avg'].plot.bar(ax=axes2)
axes2.set_title('用户分层平均消费金额')
axes2.set_xlabel('平均消费金额')
13.png
从上图可以看出,接近60%的用户属于一般发展客户,这一类客户占比较大,但是仅仅贡献消费金额的17%左右,平均消费金额仅仅0.25万美元,在各类客户当中属于最低水平;第二大用户群是重要维护客户,人数占比接近20%,贡献的销售额达到63%左右,平均消费金额达3.3万元,这一类客户价值极高,对于这一类客户应该做好重点维护,防止用户流失,从而为平台创造更大的收入。
pivot_user=df.pivot_table(index='user_id',columns='month',values='order_amount',aggfunc='count').fillna(0)
def active_status(x):
status=[]
for i in range(len(x)):
#若本月没有消费
if x[i] ==0:
if i!=0:
if status[i-1]=='unreg':
status.append('unreg')
else :status.append('unactive')
else :status.append('unreg')
#若本月消费了
elif x[i]!=0:
if i!=0:
if status[i-1]=='unreg':
status.append('new')
elif status[i-1]=='unactive':
status.append('return')
else :status.append('active')
else :status.append('new')
return pd.Series(status,index=x.index)
pivot_user_status=pivot_user.apply(active_status,axis=1)
pivot_user_status=pivot_user_status.apply(lambda x:x.value_counts()).fillna(0).T.drop('unreg',axis=1)
pivot_user_status.apply(lambda x:x/x.sum(),axis=1).plot.area()
plt.title('每月用户活动状态')
plt.ylabel('用户人数占比')
14.png
前三月份用户人数不断增高,新增用户数量和活跃用户数量都占较大比例,但后续无新用户注册,活跃用户数量下降最后趋于稳定水平,同时也有稳定的回流用户。
(pivot_user_status['return']/pivot_user_status['unactive'].shift()).plot()
plt.title('每月回流用户占比')
plt.ylabel('用户占比')
15.png
我们可以看到,四月份以后,用户回流率开始显著下降,并且稍有波动。
fig,axes=plt.subplots(1,2,figsize=(12,4))
axes0,axes1=axes.flatten()
# 用户每月复购率
repeated_purchase=pivot_user.applymap(lambda x:1 if x>1 else np.NaN if x==0 else 0)
repeated_purchase.apply(lambda x:x.sum()/x.count()).plot(ax=axes0)
axes0.set_title('每月复购率')
axes0.set_ylabel('复购率')
# 用户每月回购率
def buy_back_choose(x):
mask=x>0
label=[]
for i in range(len(x)-1):
if mask[i] and mask[i+1]:
label.append(1)
elif mask[i]:
label.append(0)
else:
label.append(np.NaN)
return pd.Series(label,index=x.index[:-1])
buy_back=pivot_user.apply(buy_back_choose,axis=1)
buy_back.apply(lambda x:x.sum()/x.count()).plot(ax=axes1)
axes1.set_title('每月回购率')
axes1.set_ylabel('回购率')
16.png
我们可以看到,前几个月,复购率较低,从四月份之后,复购率稳定在21%左右,而从每月回购率的角度来看,前几个月回购率依旧较低,但四月份之后,便在25%~37%之间上下波动。
网友评论