项目目标:分析客户消费数据,根据客户消费行为来监测公司运营活动的质量
所用的数据是一份关于用户消费的数据,
包括四个地段:
user_id:用户ID
order_dt:消费时间
order_products:购买的产品数量
order_amount:消费金额
在正式开始分析之前,需要对数据做一些处理:
- 是否存在缺失值
Python
df.isnull().sum()
SQL中没有找到特别好的方法,只能一列一列看是否存在null
SELECT count(*) FROM aa.cdnow_master
where user_id is null;
返回Python的结果
关于如何处理缺失值问题,现在还没怎么实战过,看到的比较多的就是删除或者填充,填充这一块有很多方法【简单的平均值、中位数、前向、后向,复杂的就涉及到一些机器学习模型了,后面结合例子再来看吧】
这个数据集不存在缺失值,就不考虑这一块了。
- 数据类型
这里主要是处理日期格式,原来的日期格式是int类型,修改为%Y%m%d类型,考虑到后面要按照月份来分析,添加一列month。
Python
df.info() #
df['order_dt'] = pd.to_datetime(df['order_dt'],format='%Y%m%d') #修改日期的数据类型
df['month']=df['order_dt'].values.astype('datetime64[M]') #将日期精确到月份
注:astype修改数据类型,type查看数据类型,dtype数组元素的类型
SQL的话,可以在导入之前就设定好字段类型为date,或者用SQL语句修改
alter table aa.cdnow_master modify column order_dt date #修改order_dt字段为日期类型
alter table aa.cdnow_master add column mon_th date not null;#新增一个精确到月的日期字段
update aa.cdnow_master set mon_th= date_format(order_dt,'%Y-%m-01') #更新新列
注意:在用sql处理的时候,不需要给表设定user_id为主键,否则一些重复的user_id就被自动过滤了。
简单的处理完原始数据,看一下数据的大体情况
从上面的数据可以看出
- 大部分订单只消费了少量产品(平均为2.4),有一定极值干扰(最大值和最小值差异较大)。最小值是1,中位数也才2,最大值为99,平均值却只有2.4,其实没有怎么体现出极值对平均值的影响,猜测是数据量相对较大的原因。
-
用户的消费金额比较稳定,平均消费35.89,中位数是25.98,有一定极值干扰。和产品数有相同的现象。
主要从四个角度来分析:
1、用户消费趋势的分析(按月份)
指标1是每月的消费总金额
指标2是每月的消费次数
指标3是每月的产品购买量
指标4是每月的消费人数
指标1 每月的消费总金额
Python
grouped_month = df.groupby('month')
order_month_amount = grouped_month['order_amount'].sum() #每月的消费总额
order_month_amount.sort_values(ascending=False)
SQL
SELECT mon_th,sum(order_amount) as total
FROM aa.cdnow_master
group by mon_th
order by total desc
返回Python结果
可视化
import matplotlib.pyplot as plt
plt.rcParams['figure.figsize']=(10,6)#设置画布大小
plt.style.use('ggplot') #设置图形显示风格为ggplot类型
order_month_amount.plot(marker='*') #折线图
#或者plt.plot(order_month_amount,marker='*')
返回结果
结论:
- 由上图可知,消费金额在前三个月达到最高峰,四月份有大幅下降,此后消费额较为稳定,仍然有轻微的下降趋势。
- 每年的3月份有一次上涨,4月份下降
指标2是每月的消费订单量
方法与消费金额类似
Python
month_count = grouped_month['user_id'].count()
month_count
SQL
SELECT mon_th,count(user_id)
FROM aa.cdnow_master
group by mon_th
order by mon_th
结果
可视化
month_count.plot(marker='o',mec='r',mfc='w')
结果
结论:
- 前三个月消费订单数在10000单左右,与每月的消费金额一致,四月份的订单量下滑较大,后续月份的平均消费在2500笔左右。
指标3是每月的产品购买量
Python
grouped_month['order_products'].sum().plot()
SQL查询
SELECT mon_th,sum(order_products)
FROM aa.cdnow_master
group by mon_th
order by mon_th
返回Python结果
结论:
- 前三个月是产品销售高峰在23000左右,四月份下滑幅度较大,后续消费较稳定,相较于前三个月,呈下降趋势。
指标4是每月的消费人数
计算消费人数时注意一个用户会多次下单的问题【去重】
Python
grouped_month['user_id'].apply(lambda g: len(g.drop_duplicates())).plot()
#或者用分组来去重
df.groupby(['month','user_id']).count().reset_index().groupby('month')['order_dt'].count().plot()
SQL查询
SELECT mon_th,count(distinct user_id)
FROM aa.cdnow_master
group by mon_th
order by mon_th
返回结果
结论:
- 每月消费人数低于每月消费订单量【消费人数<=消费订单量】,但差异不大【大部分用户每月只下一次单】
- 前三个月每月的消费人数在8000到10000之间,四月份下滑到3000左右,后续月份,平均消费人数在2000人不到
指标1到指标3都可以用数据透视表来进行计算
df.pivot_table(index='month',values=['user_id','order_products','order_amount'],
aggfunc={'user_id':'count','order_products':np.sum,'order_amount':np.sum})
结果
除了上面四个指标,还可以看一下
每月用户平均消费金额趋势
Python
sum_1.div(sum_2,level='month').plot() #每月用户平均消费金额
plt.yticks(range(35,90,20))
SQL查询
select mon_th,totalamount/totaluser
from(
SELECT mon_th,
sum(order_amount) as totalamount,
count(distinct user_id) as totaluser
FROM aa.cdnow_master
group by mon_th) as t
结果
结论:
- 除了前三个月略微低一些,后续月份的用户平均消费金额较为稳定。
每月用户平均下单量的趋势
Python
sum_3 = grouped_month['user_id'].count() #每月订单总量
sum_3.div(sum_2,level='month')
SQL查询
select mon_th,totalorder/totaluser
from(
SELECT mon_th,
count(user_id) as totalorder,
count(distinct user_id) as totaluser
FROM aa.cdnow_master
group by mon_th) as t
结论:
- 每月用户平均下单一次,较稳定。
每月用户平均消费产品数的趋势
Python
sum_4 = grouped_month['order_products'].sum()
sum_4.div(sum_2,level='month') #每月用户平均消费产品数量
SQL查询
select mon_th,totalproducts/totaluser
from(
SELECT mon_th,
sum(order_products) as totalproducts,
count(distinct user_id) as totaluser
FROM aa.cdnow_master
group by mon_th) as t
返回结果
结论:
- 每月用户的产品平均购买量保持在2到3个
注:从平均值的角度去分析一些指标,似乎覆盖掉了一些点,比如:四月份的消费金额、消费产品数、下单量都大幅下降,它的用户平均指标还变高了一些。
一般来说,消费金额、消费产品数、下单量、用户量这四个指标是相互关联的,比如:用户量越多,一般下单量越多,用pairplot验证一下
month_pair=df.pivot_table(index='month',values=['user_id','order_products','order_amount'],
aggfunc={'user_id':'count','order_products':np.sum,'order_amount':np.sum})
month_pair.rename(columns={'user_id':'ordertotal','order_products':'productstotal','order_amount':'amounttotal'},inplace=True)
month_pair['usertotal']=df.groupby('month')['user_id'].apply(lambda x:len(x.drop_duplicates()))
import seaborn as sns
sns.pairplot(month_pair)
plt.savefig('C:/Users/lenovo/Desktop/1.jpeg')
返回结果
结论:
- 四个指标两两之间有线性关系的趋势
- 包含三个离群点【前三个月的数据】
关于线性关系,以用户量和消费金额为例
sns.lmplot(data=month_pair, x='usertotal',y='amounttotal')
sns.lmplot(data=month_pair, x='usertotal',y='amounttotal',robust=True)#去除离群点
返回第一个结果
返回第二个结果
处理离群点:看第一个结果,似乎三个离群点把拟合直线的斜率变得更平缓一些,不了解数据的背景时代,但从用户量和消费金额来说,这3个离群点似乎不能算异常值;用程序验证一下,设置函数的参数robust=True去除离群点的影响,两者差异不大【在拟合时并没有删除3个点】,自己每次看到离群点第一个反应就是删除,也算是给自己做个提醒吧。
2、用户个体消费分析
维度1用户消费金额、消费产品量的描述统计
维度2用户消费金额和消费的散点图
维度3用户消费金额的分布图
维度4用户消费产品量的分布图
维度5用户累计消费金额占比(百分之多少的用户占了百分之多少的消费额)【算好符合28规则之后,可以看看这些个用户的消费特点(看他们的消费金额、消费次数的描述统计以及相应的分布图)】
维度1用户消费金额、消费产品量的描述统计
金额和产品数的统计概况
grouped_user = df.groupby('user_id')
grouped_user.sum().describe()
返回结果
结论:
- 用户平均购买了7张CD,但是中位数只有3,最大值为1033,标准差接近17【说明存在极值干扰,小部分用户购买了大量的CD】
- 用户平均消费106元,中位数只有43,上四分位数为106,最大值为13990,判断同上,存在极值干扰
维度2用户消费金额和消费的散点图
grouped_user.sum().plot.scatter(x='order_products',y='order_amount')
返回结果
结论:
- 数据集中在左下角,大部分用户购买的CD数在300张以内,消费金额在4000以下
- 购买的产品数量与消费金额有线性关系的趋势【如:同一种产品,这种关系代表了产品单价】
维度3用户消费金额的分布图
查看用户消费产品数和消费金额的分布趋势,此处对数据做一个简单清洗,借鉴切比雪夫定理:所有数据中,至少有24/25(或96%)的数据位于平均数5个标准差范围内。因此,消费产品数选取[0,7.12+5*16.98],即[0,92],消费金额选取[0,1310.73]的数据。
Python
grouped_user.sum().query('order_amount<=1310.73')['order_amount'].plot.hist(bins=100) #
SQL查询语句
select user_id,totalamount
from(
SELECT user_id,
sum(order_amount) as totalamount
FROM aa.cdnow_master
group by user_id) as t
where totalamount<=1310.73
返回
结论:
- 从直方图可知,用户消费金额,绝大部分呈现集中趋势,小部分异常值干扰了判断
维度4用户消费产品量的分布图
Python
grouped_user.sum().query('order_products<=92')['order_products'].plot.hist(bins=40)
SQL查询语句
select user_id,totalproducts
from(
SELECT user_id,
sum(order_products) as totalproducts
FROM aa.cdnow_master
group by user_id) as t
where totalproducts<=92
返回
结论:
- 大部分用户购买的cd数量较少,少部分用户购入了大量cd
维度5用户累计消费金额占比
Python
amount_cumsum = grouped_user.sum().sort_values(by=['order_amount'],ascending=False).apply(lambda x:x.cumsum()/x.sum())['order_amount']
amount_cumsum.reset_index()['order_amount'].plot()
SQL查询语句
select t1.user_id,(@csum:=@csum+t1.totalamount) as csumamount
from
(select user_id,sum(order_amount)/(select sum(order_amount) from aa.cdnow_master) as totalamount
from aa.cdnow_master
group by user_id
order by totalamount desc) as t1,(select @csum:=0) as t2;
返回Python结果
结论:
- 消费排名前5000的用户,已经贡献了约70%的消费额度
- 消费排名靠后的50%用户,仅贡献了约15%的消费额度
- 用户消费金额基本呈28原则的趋势
查看一下消费排名前5000的用户的消费金额和消费产品数的情况
grouped_user_2 = grouped_user.sum().sort_values(by=['order_amount'],ascending=False).apply(lambda x:x.cumsum()/x.sum())
grouped_user_80 = grouped_user.sum().loc[grouped_user_2.iloc[:5000].index]
grouped_user_80.describe() #描述统计
返回
由上表可知,数据结果相对集中,仍然存在极值影响。
消费产品数
df2 = df.set_index('user_id')
df2_80 = df2.loc[grouped_user_2.iloc[:5000].index]
product_80 = df2_80.groupby('month').sum()['order_products']
product_total = df.groupby('month').sum()['order_products']
product_80.div(product_total)
SQL查询语句
select mon_th,
sum(if(t2.user_id is null,0,t1.order_products))/sum(t1.order_products) as monthproducts
from aa.cdnow_master as t1
left join
(
select user_id
from aa.cdnow_master
group by user_id
order by sum(order_amount) desc
limit 5000) as t2
on t1.user_id=t2.user_id
group by t1.mon_th
order by t1.mon_th
注:由于计算精度原因,Python和SQL中的第五千个用户分别为用户20464和9654,所以两个结果有一点点不一样
返回
可视化
df2_80.groupby('month').sum()['order_products'].plot()
返回
结论:
- 由上表可知,除了前三个月,这5000名用户贡献了每个月80%以上的产品消费量,【前三个月用户大量增加,所以这些用户的产品消费量贡献在50%左右,后续用户不再增加,由这部分用户承担起大部分消费量,这部分用户包含大量活跃用户】
- 这5000名用户的每月消费量散点图,趋势与原始散点图差异不大
- 每个三月都会有一次上涨
消费金额
amount_80 = df2_80.groupby('month').sum()['order_amount']
amount_total = df.groupby('month').sum()['order_amount']
amount_80.div(amount_total)
df2_80.groupby('month').sum()['order_amount'].plot()
SQL查询语句
select mon_th,
sum(if(t2.user_id is null,0,t1.order_amount))/sum(t1.order_amount) as monthamount
from aa.cdnow_master as t1
left join
(
select user_id
from aa.cdnow_master
group by user_id
order by sum(order_amount) desc
limit 5000) as t2
on t1.user_id=t2.user_id
group by t1.mon_th
order by t1.mon_th;
注:由于计算精度原因,Python和SQL中的第五千个用户分别为用户20464和9654,所以两个结果有一点点不一样
返回
结论与消费产品数的结论一致
3、用户消费行为
维度1用户第一次消费(首购)
维度2用户最后一次消费
维度3多少用户仅消费了一次
- 每月新客占比
维度4用户分层 - RFM()
- 新、老、活跃、回流、流失
维度5用户购买周期(按订单) - 用户消费周期描述
- 用户消费周期分布
维度6用户生命周期(按第一次和最后一次消费) - 用户生命周期描述
- 用户生命周期分布
维度1用户第一次消费(首购)
Python
grouped_user.min()['order_dt'].value_counts().plot()
SQL查询
select firstorder, count(*) from
(select user_id,min(order_dt) as firstorder
from aa.cdnow_master
group by user_id) as t
group by firstorder
返回
结论:
- 用户第一次购买分布,集中在前三个月
- 存在多个峰值,波动较大,其中,2月13号至2月27号有一次剧烈的波动,先大幅度增加再大幅度下降
维度2用户最后一次消费
Python
grouped_user.max()['order_dt'].value_counts().plot()
SQL查询语句
select firstorder, count(*) from
(select user_id,max(order_dt) as firstorder
from aa.cdnow_master
group by user_id) as t
group by firstorder
order by count(*) desc
返回
结论:
- 用户最后一次购买的分布比第一次分布广
- 大部分最后一次购买集中在前三个月,说明有很多用户购买了一次后不再购买
- 随着时间的递增,最后一次购买数也在下降,消费呈现流失上升的情况
维度3多少用户仅消费了一次
Python
user_life = grouped_user['order_dt'].agg(['min','max'])
(user_life['min']==user_life['max']).value_counts()
SQL查询语句
select count(*) from
(select user_id
from aa.cdnow_master
group by user_id
having count(*) = 1) as t
返回
注:上面Python方法得出仅消费一次的用户为12054,但是按照SQL的方法得到11908,原因在于Python方法的时间只精确到天,有一部分用户同一天消费了两次,比如id为2的用户,在1月12号消费了两次,Python方法将他作为只消费了一次的用户。
因此按SQL的思路修改Python
user_life_2 = grouped_user.count()
(user_life_2['order_dt']==1).value_counts()
返回:
结论:
- 约50%的用户仅消费了一次
维度4用户分层
RFM(最近一次消费、消费次数【此处用消费产品数】、消费金额)
Python
rfm = df.pivot_table(index='user_id',values=['order_dt','order_products','order_amount'],aggfunc={'order_dt':'max',
'order_products':'sum','order_amount':'sum'})
rfm['R'] = -(rfm['order_dt']-rfm['order_dt'].max())/np.timedelta64(1,'D') #计算用户的最近一次消费距今的时间【此处用的是所有用户中的最近一次消费】除法是为了消除单位
rfm.rename(columns={'order_products':'F','order_amount':'M'},inplace=True)#RFM模型
def rfm_func(x):
level=x.apply(lambda x: '1' if x>=0 else '0')
label = level.R+level.F+level.M
d={
'111':'重要价值客户',#消费时间较远,消费频次和消费金额够
'011':'重要保持客户', #最重要的客户,消费时间近,消费频次和消费金额够
'101':'重要发展客户',#消费时间远,消费金额够,消费频次不够
'001':'重要挽留客户',#消费时间近,消费频次不够,消费金额够
'110':'一般价值客户',#消费时间远,消费频次够,消费金额不够
'010':'一般保持客户',#消费时间近,消费频次够,消费金额不够
'100':'一般发展客户',#消费时间远,消费频次和消费金额不够
'000':'一般发展客户'#消费时间近,消费频次和消费金额都不够
}
return d[label]
rfm['label'] = rfm[['R','F','M']].apply(lambda x:x-x.mean()).apply(rfm_func,axis=1) #
计算每个字段与平均值的差距,根据差值进行分层
rfm.head()
SQL查询
create table test4 (select user_id,max(order_dt) as R,sum(order_products) as F,sum(order_amount) as M
from aa.cdnow_master
group by user_id);
alter table test4 add RR varchar(40) not Null;
update test4,(select * from (select user_id,datediff((select max(R) from test4),R) as R from test4) as t5) as t6
set test4.RR=t6.R
where test4.user_id=t6.user_id;
select label,count(*)
FROM
(select *,
(case
WHEN RFM='111' THEN '重要价值客户'
WHEN RFM='011' THEN '重要保持客户'
WHEN RFM='101' THEN '重要发展客户'
WHEN RFM='001' THEN '重要挽留客户'
WHEN RFM='110' THEN '一般价值客户'
WHEN RFM='010' THEN '一般保持客户'
WHEN RFM='100' THEN '一般发展客户'
WHEN RFM='000' THEN '一般发展客户'
end) as label
from
(select *,
CONCAT(if((RR-(select AVG(RR) from test4))>=0,1,0),
if((F-(select AVG(F) from test4))>=0,1,0),
if((M-(select AVG(M) from test4))>=0,1,0)) as RFM
from test4) as t4) as t5
group by label;
返回结果中,已为每个用户打好标签
可视化
简单点划分为两种
rfm.loc[rfm['label']=='重要价值客户','color']='g'
rfm.loc[~(rfm['label']=='重要价值客户'),'color']='r'
rfm.plot.scatter('F','R',c=rfm['color'])
返回
注:plot.scatter()中的参数c,只能填颜色名称或者数字,不能填c=rfm['label']会报错【c of shape (23570,) not acceptable as a color sequence for x with size 23570, y with size 23570】
按照原分层
plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False #用来正常显示负号
pie_1= rfm['label'].value_counts()
plt.figure(figsize=(8,9))
pie_1.plot.pie(autopct='%1.1f%%',explode=[0.05,0,0,0,0,0,0],pctdistance = 0.9,labeldistance = 1.1)
返回
结论:
- 由饼图可知,大部分的用户为一般发展客户,其次是重要保持客户,剩余用户层次占比均较小
新用户【第一次消费】、活跃【持续在消费】、回流【隔了一段时间再次消费】、流失【不活跃,一段时间未消费】
Python
pivoted_counts = df.pivot_table(index='user_id',columns='month',values='order_dt',aggfunc='count').fillna(0) #每个用户每月的消费次数
#这边只看用户每个月有没有消费过,消费次数这边不考虑
df_purchase=pivoted_counts.applymap(lambda x:1 if x>0 else 0) #1代表本月消费,0代表未消费
def active_status(data): #data是df_purchase的一行
status=[]
for i in range(18): #一共有18个月,判断每一个月的消费情况
#若本月没有消费
if data[i]==0:
if len(status)>0:#之前有记录
if status[i-1]=='unreg': #一直没有注册,看作未注册用户
status.append('unreg') #未注册用户
else:
status.append('unactive') #这个月没消费,之前消费过
else:#之前没有记录
status.append('unreg') #第一个月没有消费,未注册
#若本月消费
else:
if len(status)==0:#之前没有记录
status.append('new') #第一次消费
else:#之前有记录
if status[i-1]=='unactive':
status.append('return') #前几个月不活跃,现在又消费了,回流
elif status[i-1]=='unreg':
status.append('new') #判断第一次消费
else:
status.append('active') #一直在消费
return status
purchase_stats=df_purchase.apply(active_status,axis=1)#得到用户分层结果
purchase_stats_ct=purchase_stats.replace({'unreg':np.NaN}).apply(lambda x:x.value_counts()) #用NaN替代unreg,以便后续计算不包含这些数据,未注册不考虑
purchase_stats_ct.fillna(0).T.plot.area()#为了便于画图,将缺失值补齐
返回
表格形式【每月各层用户占比】
purchase_stats_ct.fillna(0).T.apply(lambda x:x/x.sum(),axis=1) # 只要首购了,这个用户就会被纳入分析范围
返回
此外,还可以计算每月的回流率
purchase_return = purchase_stats_ct.fillna(0).T[['return','unactive']]
purchase_return['return'].div(purchase_return['unactive'].shift(1)) #回流率一直都不太高
返回
结论:
- 新用户集中在前三个月,后续每月新用户接近0
- 前三个月有一定数量的活跃用户,后续不活跃用户越来越多,表明消费运营质量不高,用户在不断流失
- 回流率一直都不高,表明唤回运营效果不佳
维度5用户购买周期(按订单)
用户消费周期描述
order_diff = df.groupby('user_id').apply(lambda x: x['order_dt']-x['order_dt'].shift()) #用户每笔订单的时间间隔
order_diff.describe() #不计算空值
SQL查询
select t1.user_id,
@difft:=if(@id=user_id,DATEDIFF(order_dt,@pre),null) as dit,
@pre:=order_dt,
@id:=user_id
from aa.cdnow_master as t1,
(select @pre:=0,@id:=null) as t2
ORDER BY user_id,order_dt
返回
结论:
- 用户的平均消费周期为68天,最大值为533天,存在部分极值
用户消费周期分布
sns.distplot((order_diff/np.timedelta64(1,'D')).dropna(),bins=20).set_xlim(0,600)
返回
结论:
- 订单周期呈指数分布
- 绝大部分用户的购买周期都低于100天
维度6用户生命周期(按第一次和最后一次消费)
用户生命周期描述和分布
Python
user_life=grouped_user.agg(['min','max'])['order_dt']
user_life=(user_life['max']-user_life['min'])/np.timedelta64(1,'D')
user_life.plot.hist(bins=40)
user_life.describe()
SQL查询
select user_id,DATEDIFF(MAX(order_dt),MIN(order_dt)) as userlife
from aa.cdnow_master
GROUP BY user_id
返回
结论:
- 用户平均消费134天,用户生命周期受只购买一次的用户影响比较厉害,用户生命周期的中位数为0,这50%的用户仅消费了一次
排除仅消费了一次的用户,再次查看用户生命周期的分布
sns.distplot(user_life[user_life>0],bins=40).set_xlim(0,600)
返回
结论:
- 这部分用户的生命周期不太稳定,分布较分散,暂时无法得到确切的周期分布
第一次消费到第二次消费的时间间隔分布,可以写一下
import heapq
def user_func(x):
if len(x)>1:
return (heapq.nsmallest(2,x)[1]-heapq.nsmallest(2,x)[0])/np.timedelta64(1,'D')
else:
return None
#user_fugou = grouped_user.count()[grouped_user.count()['order_dt']>1]
grouped_user_2 = df.groupby('user_id')
user_fugou_2 = grouped_user_2['order_dt'].apply(user_func )
user_fugou_2.describe()
user_fugou_2.plot.hist(bins=100)
返回
结论:
- 用户复购时间集中在左下角,复购用户中大部分会在50天内第二次购买
- 用户复购的平均时间间隔为108天,中位数为54天,最大值为533天,受部分极值影响
复购率和回购率
复购率
Python
purchase_r = pivoted_counts.applymap(lambda x: 1 if x>1 else np.NaN if x==0 else 0)#1表示本月复购了【购买次数大于1】,0表示本月只消费了一次,NaN表示本月未消费
((purchase_r.sum())/purchase_r.count()).plot(figsize=(10,4))
SQL查询
select mon_th ,
sum(if(ordertimes>1,1,0))/count(*) as f1
from
(select user_id,mon_th,count(*) as ordertimes
from aa.cdnow_master
GROUP BY user_id,mon_th) as t
GROUP BY mon_th
ORDER BY mon_th
返回
结论:
- 复购率稳定在20%左右
- 前三个月相对复购率较低
回购率
Python
def purchase_back(data):
status=[]
for i in range(17): #如果本月消费了,那就看看下个月有没有消费,消费了就是回购,没消费就不是回购,如果本月没有消费,也不用看下个月了
if data[i]==1:
if data[i+1]==1:
status.append(1)
if data[i+1]==0:
status.append(0)
else:
status.append(np.NaN)
status.append(np.NaN) #填充最后一个月
return status
purchase_b = df_purchase.apply(purchase_back,axis=1)
(purchase_b.sum()/purchase_b.count()).plot(figsize=(10,4))
SQL查询
select t1.mon_th,COUNT(DISTINCT t1.user_id) as totaluser,COUNT(t2.mon_th) as huser,
COUNT(t2.mon_th)/COUNT(DISTINCT t1.user_id) as h1
from
(select user_id,mon_th
from aa.cdnow_master
GROUP BY user_id,mon_th) as t1
LEFT JOIN
(select user_id,mon_th
from aa.cdnow_master
GROUP BY user_id,mon_th) as t2
on t1.user_id=t2.user_id
and t1.mon_th = DATE_SUB(t2.mon_th,INTERVAL 1 month)
GROUP BY t1.mon_th
返回
结论:
- 回购率稳定在30%左右
- 4月份回购率涨幅较大,原因在于四月份用户量下滑
结果总结:
1、新用户集中在前三个月,有一定数量的活跃用户,后续不活跃用户增多,回流率较低;
2、消费金额、产品销量、订单量均在前三个月达到最高峰,四月份有大幅下降,后续变化不大
3、小部分用户购买了大量产品;
4、大部分用户的消费金额在4000以下,购买量在300以内,受到部分极值干扰;
5、消费排名前21%左右的用户,贡献了约70%的消费额度,这部分用户贡献了四月份之后当月销量的80%;
6、约74%的用户表现出一段时间未消费、购买量低、消费金额低的特点;
7、复购率和回购率分别稳定在20%和30%左右
表明运营质量不佳,其中唤回运营也没有起到很好的效果,运营依旧不断流失。