电商用户消费行为分析

项目目标:分析客户消费数据,根据客户消费行为来监测公司运营活动的质量
所用的数据是一份关于用户消费的数据,


image.png

包括四个地段:
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的结果


image.png

关于如何处理缺失值问题,现在还没怎么实战过,看到的比较多的就是删除或者填充,填充这一块有很多方法【简单的平均值、中位数、前向、后向,复杂的就涉及到一些机器学习模型了,后面结合例子再来看吧】
这个数据集不存在缺失值,就不考虑这一块了。

  • 数据类型
    这里主要是处理日期格式,原来的日期格式是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就被自动过滤了。
简单的处理完原始数据,看一下数据的大体情况

image.png

从上面的数据可以看出

  • 大部分订单只消费了少量产品(平均为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结果


image.png

可视化

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='*') 

返回结果


image.png

结论:

  • 由上图可知,消费金额在前三个月达到最高峰,四月份有大幅下降,此后消费额较为稳定,仍然有轻微的下降趋势。
  • 每年的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 

结果


image.png

可视化

month_count.plot(marker='o',mec='r',mfc='w')

结果


image.png

结论:

  • 前三个月消费订单数在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结果


image.png

结论:

  • 前三个月是产品销售高峰在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

返回结果


image.png

结论:

  • 每月消费人数低于每月消费订单量【消费人数<=消费订单量】,但差异不大【大部分用户每月只下一次单】
  • 前三个月每月的消费人数在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})

结果


image.png

除了上面四个指标,还可以看一下

每月用户平均消费金额趋势

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

结果


image.png

结论:

  • 除了前三个月略微低一些,后续月份的用户平均消费金额较为稳定。

每月用户平均下单量的趋势

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
image.png

结论:

  • 每月用户平均下单一次,较稳定。

每月用户平均消费产品数的趋势

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

返回结果


image.png

结论:

  • 每月用户的产品平均购买量保持在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')

返回结果


1.jpeg

结论:

  • 四个指标两两之间有线性关系的趋势
  • 包含三个离群点【前三个月的数据】
    关于线性关系,以用户量和消费金额为例
sns.lmplot(data=month_pair, x='usertotal',y='amounttotal')
sns.lmplot(data=month_pair, x='usertotal',y='amounttotal',robust=True)#去除离群点

返回第一个结果


image.png

返回第二个结果


image.png

处理离群点:看第一个结果,似乎三个离群点把拟合直线的斜率变得更平缓一些,不了解数据的背景时代,但从用户量和消费金额来说,这3个离群点似乎不能算异常值;用程序验证一下,设置函数的参数robust=True去除离群点的影响,两者差异不大【在拟合时并没有删除3个点】,自己每次看到离群点第一个反应就是删除,也算是给自己做个提醒吧。

2、用户个体消费分析

维度1用户消费金额、消费产品量的描述统计
维度2用户消费金额和消费的散点图
维度3用户消费金额的分布图
维度4用户消费产品量的分布图
维度5用户累计消费金额占比(百分之多少的用户占了百分之多少的消费额)【算好符合28规则之后,可以看看这些个用户的消费特点(看他们的消费金额、消费次数的描述统计以及相应的分布图)】

维度1用户消费金额、消费产品量的描述统计

金额和产品数的统计概况

grouped_user = df.groupby('user_id')
grouped_user.sum().describe()

返回结果


image.png

结论:

  • 用户平均购买了7张CD,但是中位数只有3,最大值为1033,标准差接近17【说明存在极值干扰,小部分用户购买了大量的CD】
  • 用户平均消费106元,中位数只有43,上四分位数为106,最大值为13990,判断同上,存在极值干扰

维度2用户消费金额和消费的散点图

grouped_user.sum().plot.scatter(x='order_products',y='order_amount')

返回结果


image.png

结论:

  • 数据集中在左下角,大部分用户购买的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

返回


image.png

结论:

  • 从直方图可知,用户消费金额,绝大部分呈现集中趋势,小部分异常值干扰了判断

维度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

返回


image.png

结论:

  • 大部分用户购买的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结果


image.png

结论:

  • 消费排名前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() #描述统计

返回


image.png

由上表可知,数据结果相对集中,仍然存在极值影响。
消费产品数

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,所以两个结果有一点点不一样
返回


image.png

可视化

df2_80.groupby('month').sum()['order_products'].plot()

返回


image.png

结论:

  • 由上表可知,除了前三个月,这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,所以两个结果有一点点不一样
返回


image.png

image.png

结论与消费产品数的结论一致

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

返回


image.png

结论:

  • 用户第一次购买分布,集中在前三个月
  • 存在多个峰值,波动较大,其中,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

返回


image.png

结论:

  • 用户最后一次购买的分布比第一次分布广
  • 大部分最后一次购买集中在前三个月,说明有很多用户购买了一次后不再购买
  • 随着时间的递增,最后一次购买数也在下降,消费呈现流失上升的情况

维度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

返回

image.png

注:上面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()

返回:


image.png

结论:

  • 约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;

返回结果中,已为每个用户打好标签


image.png

可视化
简单点划分为两种

rfm.loc[rfm['label']=='重要价值客户','color']='g' 
rfm.loc[~(rfm['label']=='重要价值客户'),'color']='r'
rfm.plot.scatter('F','R',c=rfm['color'])

返回


image.png

注: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)

返回


image.png

结论:

  • 由饼图可知,大部分的用户为一般发展客户,其次是重要保持客户,剩余用户层次占比均较小
新用户【第一次消费】、活跃【持续在消费】、回流【隔了一段时间再次消费】、流失【不活跃,一段时间未消费】

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()#为了便于画图,将缺失值补齐

返回


image.png

表格形式【每月各层用户占比】

purchase_stats_ct.fillna(0).T.apply(lambda x:x/x.sum(),axis=1)  # 只要首购了,这个用户就会被纳入分析范围

返回


image.png

此外,还可以计算每月的回流率

purchase_return = purchase_stats_ct.fillna(0).T[['return','unactive']]
purchase_return['return'].div(purchase_return['unactive'].shift(1)) #回流率一直都不太高

返回


image.png

结论:

  • 新用户集中在前三个月,后续每月新用户接近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

返回


image.png

结论:

  • 用户的平均消费周期为68天,最大值为533天,存在部分极值
用户消费周期分布
sns.distplot((order_diff/np.timedelta64(1,'D')).dropna(),bins=20).set_xlim(0,600)

返回


image.png

结论:

  • 订单周期呈指数分布
  • 绝大部分用户的购买周期都低于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

返回


image.png

结论:

  • 用户平均消费134天,用户生命周期受只购买一次的用户影响比较厉害,用户生命周期的中位数为0,这50%的用户仅消费了一次
    排除仅消费了一次的用户,再次查看用户生命周期的分布
sns.distplot(user_life[user_life>0],bins=40).set_xlim(0,600)

返回


image.png

结论:

  • 这部分用户的生命周期不太稳定,分布较分散,暂时无法得到确切的周期分布
    第一次消费到第二次消费的时间间隔分布,可以写一下
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)

返回


image.png

image.png

结论:

  • 用户复购时间集中在左下角,复购用户中大部分会在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

返回


image.png

结论:

  • 复购率稳定在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

返回


image.png

结论:

  • 回购率稳定在30%左右
  • 4月份回购率涨幅较大,原因在于四月份用户量下滑
    结果总结:
    1、新用户集中在前三个月,有一定数量的活跃用户,后续不活跃用户增多,回流率较低;
    2、消费金额、产品销量、订单量均在前三个月达到最高峰,四月份有大幅下降,后续变化不大
    3、小部分用户购买了大量产品;
    4、大部分用户的消费金额在4000以下,购买量在300以内,受到部分极值干扰;
    5、消费排名前21%左右的用户,贡献了约70%的消费额度,这部分用户贡献了四月份之后当月销量的80%;
    6、约74%的用户表现出一段时间未消费、购买量低、消费金额低的特点;
    7、复购率和回购率分别稳定在20%和30%左右
    表明运营质量不佳,其中唤回运营也没有起到很好的效果,运营依旧不断流失。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,076评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,658评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,732评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,493评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,591评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,598评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,601评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,348评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,797评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,114评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,278评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,953评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,585评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,202评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,180评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,139评论 2 352