本文是对电商案例的一个总结,主要是根据用户消费记录,分析用户消费行为,建立RFM模型,分析复购率、回购率等关键指标。
一、项目背景
数据集来源于是美国的一家网上唱片公司,该公司成立于1994年,后来被贝塔斯曼音乐集团收购。
二、项目需求
数据集来源于某电商网站的用户购买行为,数据集一共包含四个字段:user_id,购买日期,购买数量和购买金额。属于非常典型的消费行为数据集。通过用户的消费记录,来分析用户的消费行为。
三、分析思路
1.准备工作(数据集观察和数据清洗)
2.按月对数据进行分析:每月消费总金额、消费次数、产品购买量、消费人数
3.用户个体消费分析:用户消费金额,产品购买量的描述性统计、用户消费金额和产品购买量分布、用户累计消费金额占比
4.用户消费行为分析:用户第一次消费(首购)时间分布、用户最后一次消费时间分布、用户分层RFM模型、各类用户(新用户、活跃用户、流失用户、回流用户)数量和占比
5.复购率和回购率分析:复购率、回购率
四、分析过程
1.准备工作
(1)导入数据集
数据集为txt文件格式,没有列名,并且字段之间使用多个空格进行分隔。
这里读取文件时使用sep=’\s+’,如果在分隔符里存在多个字符串,可以用s+,+类似于正则匹配,无论分隔符有多少个空格都可以自动处理。
import pandas as pd
import numpy as np
columns=['user_id','order_dt','order_products','order_amount']
df=pd.read_table(r'E:\CDNOW_master.txt',names=columns,sep='\s+')
(2)数据清洗
检查数据,发现日期order_dt数据类型为int64,需要转换为日期型datetime64[ns]
由于需要按月分析用户的消费趋势,这里新建一个month字段。将order_dt列转换为datetime64[M],默认会是每月的第一天。
df['order_dt'] = pd.to_datetime(df.order_dt,format='%Y%m%d')
df['month'] = df.order_dt.values.astype('datetime64[M]')
(4)数据初步了解
- 大部分订单只消费了少量商品(平均2.4),有一定极值的干扰。
- 用户的消费金额比较稳定,平均消费35元,中位数在35元,有一定极值的干扰。
2.用户消费趋势分析(按月)
(1)统计每月的消费总金额、产品购买量、消费次数、消费人次
grouped_month = df.groupby('month')
grouped_month_info = grouped_month.agg({'user_id':'count','order_products':'sum','order_amount':'sum'}).reset_index()
grouped_month_info.rename(columns = {'order_products':'产品购买量','user_id':'消费次数','order_amount':'消费金额'},inplace=True)
grouped_month_info['消费人次'] = grouped_month.user_id.unique().map(len).to_list()
(2)可视化分析
将上表导出为excel文件,并在powerBI中绘图
grouped_month_info['month'] = grouped_month_info.month.astype(str) # 将month转换为字符串格式,便于导入powerBI不会被识别为时间戳
grouped_month_info.to_excel(r'.\临时表\月销售额、销售次数、产品购买数、消费人数.xlsx')
-
月消费金额趋势图
由上图可知,消费金额在前三个月达到最高峰,4月开始出现断崖式下降,后消费金额趋于平稳,有轻微下降趋势。
-
月产品购买量趋势图
由上图可知,产品购买量在前三个月达到最高峰,后续消费较为稳定,有轻微下降趋势 -
月消费次数和消费人次趋势图
每月消费人数低于每月消费次数,但差异不大
前三个月每月的消费人数在8000-10000之间,后续月份,平均消费人数在2000不到
3.用户个体消费分析
(1)用户消费金额、消费次数的描述性统计
grouped_user = df.groupby('user_id')
grouped_user_info = grouped_user.sum()
从上图可知:
- 用户平均购买了7张,但是中位值只有3,说明小部分用户购买了大量货物
- 用户平均消费106元,中位值有43,判断同上,有极值干扰
(2)绘制消费金额和消费次数的散点图
将上表导入到powerBI中绘制
由散点图可知,用户消费金额与产品购买量几乎成线性关系,购买的商品越多,消费金额越大。
(3)用户消费金额分布
对消费金额进行分层
# 对消费金额进行分层
grouped_user_sum_order_amount = grouped_user.sum().order_amount
grouped_user_sum_order_amount_lst = [i for i in range(0,int(grouped_user.sum().order_amount.max())+50,50)]
grouped_user_sum_order_amount = pd.cut(grouped_user_sum_order_amount,bins = grouped_user_sum_order_amount_lst,labels = grouped_user_sum_order_amount_lst[1:])
在powerBI中绘制直方图,如下:
从直方图可知,用户消费金额,绝大部分呈现集中趋势,小部分异常值干扰了判断,可以使用过滤操作排除异常
使用切比雪夫定理过滤掉异常值,因为切比雪夫定理说明,95%的数据都分布在5个标准差之内,剩下5%的极值就不要了
grouped_user.sum().order_amount.values.mean()+5*grouped_user.sum().order_amount.values.std()
# 1310.6808436308752
(4)用户累计消费金额占比
按照用户消费金额进行升序排序,cumsum 是求累加值
# 对用户的消费金额做一个排序,并做一个累加计算
user_cumsum = grouped_user.sum().sort_values('order_amount').apply(lambda x:x.cumsum()/x.sum())
user_cumsum.reset_index().order_amount.to_excel(r'.\临时表\累积销售.xlsx')
在powerBI中绘图,如下:
由图可以知道50%的用户仅贡献了11%的消费额度,而排名前5000的用户就贡献了60%的消费额度
4.用户消费行为分析
(1)用户第一次消费(首购)时间趋势
按用户分组找出每个用户第一次的消费日期,并按照日期进行计数
grouped_user_min = grouped_user.min().order_dt.value_counts().reset_index().rename(columns = {'index':'first_date'})
grouped_user_min['first_date'] = grouped_user_min['first_date'].astype(str)
grouped_user_min.to_excel(r'.\临时表\用户首购.xlsx')
在powerBI中绘图,如下:
由上图可知,用户第一次购买的时间集中分布在前三个月。其中,在2月9日到2月25日之间有剧烈的波动。
(2)用户最后一次消费时间分布
grouped_user_max = grouped_user.max().order_dt.value_counts().reset_index().rename(columns = {'index':'last_date'})
grouped_user_max['last_date'] = grouped_user_max['last_date'].astype(str)
grouped_user_max.to_excel(r'.\临时表\用户最后一次购买.xlsx')
从上图中可以分析出:
- 大部分用户在1-3月份流失了,说明很多用户可能是被活动的吸引而产生了一次消费行为,后面就不再进行购买。
- 用户流失量出现断崖式下跌,结合一开始用户数量增长迅猛,流失的也会比较多,是正常表现。
- 用户最后一次购买的时间分布比第一次购买的时间分布广。
- 随着时间的递增,最后一次购买的用户数量也在递增,消费呈现流失上升的状况。可能原因是随着时间的增长,运营没跟上,或者用户忠诚度下降了。
(3)用户分层
这里使用RFM模型来分析。
RFM模型是根据客户活跃程度和交易金额的贡献,进行客户价值细分的一种方法。
- R(Recency)——最近一次交易时间间隔。基于最近一次交易日期计算的得分,距离当前日期越近,得分越高。
- F(Frequency)——客户在最近一段时间内交易次数。基于交易频率计算的得分,交易频率越高,得分越高。
- M(Monetray)——客户最近一段时间内交易金额。基于交易金额计算的得分,交易金额越高,得分越高。
a.对原始数据进行数据透视
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()).astype('timedelta64[D]').astype(float)
# 重命名,R :消费时间 F:消费频次 M:消费金额
rfm.rename(columns={'order_products':"F",'order_amount':'M'},inplace=True)
这里思考一个问题,怎样的客户是重要客户呢?肯定是消费时间距离今天越近,消费次数越高,消费金额越高的客户,越有价值。这些指标要如何体现出来呢,这里考虑用均值,让每个字段的值与均值进行比较,如果比均值高,及相减大于0,则价值越大;若与均值小,及相减小于0,则价值越小。
rfm = rfm[['R','F','M']].apply(lambda x:x-x.mean())
rfm.head()
b.定义函数,将客户进行分层
def rfm_func(x):
level = x.apply(lambda x:'1' if x>0 else '0')
label = level['R']+level['F']+level['M']
d={
# R 为1 表示比均值大,离最早时间近,F为1 表示 消费频次比较多,M 为1 表示消费金额比较多,所以是重要价值客户
'111':'重要价值客户',
'011':'重要保持客户',
'101':'重要发展客户',
'001':'重要挽留客户',
'110':'一般价值客户',
'010':'一般保持客户',
'100':'一般发展客户',
'000':'一般挽留客户',
}
result = d[label]
return result
rfm['label'] = rfm.apply(rfm_func,axis=1)
这里我们绘制一下重要价值客户和非重要价值客户的分布
rfm.loc[rfm.label=='重要价值客户','color']='重要价值客户'
rfm.loc[~(rfm.label=='重要价值客户'),'color']='非重要价值客户'
rfm.to_excel(r'.\临时表\RFM模型.xlsx')
在powerBI中绘图,如下:
从RFM 分层可知,大部分用户是一般挽留客户,但是这是由于极值的影响,RFM的划分标准应该以业务为准,也可以通过切比雪夫定理去除极值后求均值,并且 RFM 的各个划分标准可以都不一样。
(4) 用户生命周期:新客,活跃,回流,流失
a.统计每月用户是否有消费
# 数据透视, userid为索引,月为列,求每月的消费次数,这里填充了
df_purchase=df.pivot_table(index='user_id',
columns='month',
values='order_dt',
aggfunc='count').fillna(0).applymap(lambda x:1 if x>0 else 0)
df_purchase.tail()
b.从用户在当月是否有消费行为,来判断用户所处的生命周期
若本月没有消费:
- 若之前是未注册,则依旧为未注册
- 若之前有消费,则为流失/不活跃
- 其他情况,为未注册
若本月有消费:
- 若是第一次消费,则为新用户
- 如果之前有过消费,则上个月为不活跃,则为回流
- 如果上个月为未注册,则为新用户
- 除此之外,为活跃
return:回流
new:新客
unreg:未注册
active:活跃
unactive:流失/不活跃
# 判断用户所处的生命周期
def active_status(data):
status=[]
# 数据一共有18个月份,每次输入一行数据,这样进行逐月判断
for i in range(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(lambda x: pd.Series(active_status(x),index=df_purchase.columns),axis=1)
purchase_stats.head()
c.计算每日处于不同用户生命周期的用户数
# 这里把未注册的替换为空值,这样 count 计算时不会计算到
# 得到每个月的用户分布
purchase_stats_ct=purchase_stats.replace('unreg',np.NaN).apply(lambda x:pd.value_counts(x))
purchase_stats_ct_info = purchase_stats_ct.fillna(0).T
purchase_stats_ct_info.index = purchase_stats_ct_info.index.astype(str)
purchase_stats_ct_info.to_excel(r'.\临时表\用户分层-新、活跃、流失、回流.xlsx')
在powerBI中绘图,如下:
从上图可以发现:
新用户,前3个月大量涌入,后面没有新增。
活跃用户,前几个月较多,后面有所下降。
回流用户,数量趋于稳定,每月1000多。
流失/不活跃用户,数量非常多,基本上每月都在20000以上。
d.每日分类用户的占比
# 求出所有用户的占比
purchase_stats_ct_T = purchase_stats_ct.fillna(0).T.apply(lambda x:x/x.sum(),axis=1)
purchase_stats_ct_T
从上图可知,不活跃用户是不断增加的,而新增用户只有前三个月有数据,后面都为0,说明后期没有了拓客行为。
5.复购率和回购率分析
- 复购率:自然月内,购买多次的用户占比(即,购买了两次以上)
- 回购率:曾经购买过的用户在某一时期再次购买的占比(可能是在三个月内)
(1)复购率
a.求每月不用用户的消费次数
# 数据透视, userid为索引,月为列,求每月的消费次数,这里填充了
pivoted_counts=df.pivot_table(index='user_id',
columns='month',
values='order_dt',
aggfunc='count').fillna(0)
df_purchase.tail()
b.计算每日的复购率
purchase_r=pivoted_counts.applymap(lambda x: 1 if x>1 else np.NaN if x==0 else 0)
purchase_r_reshop = (purchase_r.sum()/purchase_r.count()).reset_index(name = 'reshop')
将表导入powerBI中绘图,如下:
purchase_r_reshop['month'] = purchase_r_reshop['month'].astype(str)
purchase_r_reshop.to_excel(r'.\临时表\复购人数与总消费人数比例.xlsx')
从上图分析可知,复购率稳定在20%左右,前一个月因为有大量新用户涌入,而这批用户只购买了一次,所以导致复购率降低
(2)回购率
在上面我们已经计算过每月用户是否有消费行为的表df_purchase
a.定义函数来判断是否回购
判断标准:根据上个月是否购买来判断,上个月消费下个月没有消费就不是回购
# 需要使用函数来判断是否回购:当月消费过的用户下个月也消费了叫做回购,这个定义可以改变
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)
# 第18个月补充NaN
status.append(np.NaN)
return status
purchase_b = df_purchase.apply(lambda x :pd.Series(purchase_back(x),index = df_purchase.columns),axis =1)
purchase_b.head()
b.计算回购率:回购次数/总消费次数
purchase_b_backshop = purchase_b.sum()/purchase_b.count()
将表导入到powerBI中绘图,如下:
purchase_b_backshop.index = purchase_b_backshop.index.astype(str)
purchase_b_backshop.to_excel(r'.\临时表\回购率.xlsx')
回购率稳定在30%左右,前3个月因为有大量新用户涌入,而这批用户只购买了一次,所以导致回购率较低。