本文主要是主要是根据用户消费记录,分析用户消费行为,建立RFM模型,分析复购率、回购率等关键指标。希望对其他产品的线上消费数据分析有一定的借鉴价值。
一、项目背景
CDNOW是美国的一家网上唱片公司,成立于1994年,后来被贝塔斯曼音乐集团收购。
二、分析目标
本次分析报告的数据来源于这家CD网站上的用户消费记录,旨在分析用户消费行为,建立RFM模型,分析复购率、回购率等关键指标。
三、主要分析框架
image.png四、分析过程
准备工作:
# 导入数据包
import pandas as pd
import numpy as np
# 导入数据
columns = ['user_id','order_dt','order_products','order_amount']
df = pd.read_table('bicycle_master.txt',names = columns,sep = '\s+',engine='python')
df.head()
# user_id:用户ID
# order_dt:购买日期
# order_products:购买产品数
# order_amount:购买金额
image.png
# 查看数据类型及数据完整情况
df.info()
image.png
通过查看数据类型发现,order_dt的数据类型是int64型,而非日期型,需要将其转化为日期型
# 转化日期格式
df['order_dt'] = pd.to_datetime(df.order_dt,format = "%Y%m%d")
# 新增一列记录月,方便后续按月统计
df['month'] = df['order_dt'].values.astype('datetime64[M]')
df.head(1)
image.png
# 数据描述性统计
df.describe()
image.png
从描述性统计可知:
- 大部分订单只消费了少量商品(平均2.4),有一定值干扰
- 用户的消费金额比较稳定,平均消费35元,中位数25元,有一定极值的干扰,导致平均消费被拉高,绝大多数订单的消费金额都小于35元
1. 用户消费趋势分析
根据月份分组,分别对消费额,购买人数,购买量,消费人数聚合
# 根据月份分组
grouped_month = df.groupby('month')
# 聚合获得月消费次数
order_month_amount = grouped_month.order_amount.sum()
# 聚合获得月消费金额,月消费次数,月产品购买量
grouped_month_info = grouped_month[['order_amount','user_id','order_products']].agg({'order_amount':'sum','user_id':'count','order_products':'sum'})
# 重命名
grouped_month_info.rename(columns = {'order_amount':'月消费金额','user_id':'月消费次数','order_products':'月产品购买量'},inplace = True)
# 求消费次数
grouped_month_info['消费次数'] = grouped_month['user_id'].unique().map(len)
# 重置索引
grouped_month_info = grouped_month_info.reset_index()
grouped_month_info.head()
image.png
导出数据,PowerBI作图
# 如果是日期类型,导入Power BI会是时间戳格式,所以这里转成字符串
grouped_month_info['month'] = grouped_month_info['month'].astype('str')
grouped_month_info.to_excel(r'group_month_infor.xlsx')
image.png
由上图可知
- 消费金额在前三个月达到最高峰,后续消费较为稳定,有轻微下降趋势
- 产品购买量在前三个月达到最高峰,后续消费较为稳定,有轻微下降趋势
- 前三个月消费订单人数在10000笔左右,后续月份的平均消费人数则在2500人
2. 用户个体消费分析
2.1 用户消费金额,消费次数的描述统计
对用户分组聚合
grouped_user = df.groupby('user_id')
grouped_user.sum().describe()
image.png
根据用户购买数量看,max是1033,但是中位数只有3,而均值为7,所以可知有少部分的用户购买了大量的产品
2.2 用户消费金额和消费次数的散点图
对用户分组,对订单数及订单金额求和求得用户消费次数和消费金额
grouped_user_info = grouped_user.sum()
grouped_user_info.to_excel(r'.\临时表\用户个体消费行为分析.xlsx')
image.png
消费金额与产品消费数量成正相关
2.3 用户消费金额的分布图
# 获取用户消费金额
grouped_user_sum_order_amount = grouped_user.sum()['order_amount']
# 消费金额按照50分箱
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:])
# 导出数据
grouped_user_sum_order_amount.to_excel(r'grouped_user_sum_order_amount.xlsx')
image.png
从直方图可知,用户消费金额,绝大部分呈现集中趋势,小部分异常值干扰了判断,可以使用过滤操作排除异常
2.4 用户消费次数的分布图
与消费金额分布情况相同,对消费次数进行分区
#4. 用户消费次数的分布图
grouped_user_sum_order_products = grouped_user.sum()['order_products']
grouped_user_sum_order_products_lst = [i for i in range(0,int(grouped_user_sum_order_products.max())+50,50)]
grouped_user_sum_order_products = pd.cut(grouped_user_sum_order_products,bins = grouped_user_sum_order_products_lst,labels = grouped_user_sum_order_products_lst[1:])
grouped_user_sum_order_products.to_excel(r'grouped_user_sum_order_products.xlsx')
image.png
2.5 用户累计消费金额占比情况
对用户分组,对消费金额排序后累加即可
# 排序后使用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'user_cumsum_order_amount.xlsx')
image.png
3. 用户消费行为
3.1 用户第一次消费(首购)
# 按用户分组,取每个用户的最小日期,然后对最小日期计数并按照次数由大到小排序value_counts
grouped_user_dt_min = grouped_user.min()['order_dt'].value_counts().reset_index().rename(columns = {'index':'first_date'})
grouped_user_dt_min['first_date'] = grouped_user_dt_min['first_date'].astype('str')
grouped_user_dt_min.to_excel(r'grouped_user_dt_min.xlsx')
image.png
3.2 用户最后一次消费
与用户首购分布同理,获取用户最后一次购买日期的分布
grouped_user_dt_max = grouped_user.max()['order_dt'].value_counts().reset_index().rename(columns = {'index':'last_date'})
grouped_user_dt_max['last_date'] = grouped_user_dt_max['last_date'].astype('str')
grouped_user_dt_max.to_excel(r'grouped_user_dt_max.xlsx')
image.png
大部分最后一次购买,集中在前三个月,大量用户购买了一次后就不再进行购买。
3.3 新老客户消费占比
3.3.1 多少用户只消费一次
通过判断起始购买日期与最后购买日期,如果相等则任务是新客户,如果不等则为老客户
user_life=grouped_user.order_dt.agg(['min','max'])
# 统计只消费了一次的用户
(user_life['min']==user_life['max']).value_counts()
image.png
可知,新客户占比51.1%
3.3.2 每月新客占比
分别对月与客户分组,获取每月每个客户第一次与最后一次购买日期,然后判断是否为新客,通过月份分组获得新老客户数量
# 按月与用户分组,获得首购及最后一次购买日期
group_um = df.groupby(['month','user_id'])['order_dt'].agg(['min','max'])
# 新老客判断
group_um['new'] = (group_um['min'] == group_um['max'])
# 按月分组对新老客计数
group_um.reset_index().groupby('month')['new'].value_counts()
image.png
3.4 用户分层RFM模型
RFM模型介绍:
RFM模型是衡量客户价值和客户创利能力的重要工具和手段。
R:最近一次消费 (Recency)
F:消费频率 (Frequency)
M:消费金额 (Monetary)
将以上三个指标分别拆分为二,这样就产生了8个维度用户客户的分组。R为1 表示比均值大,离最早时间近,F为1 表示消费金额比较多,M为1 表示消费频次比较多
'111':'重要价值客户',
'011':'重要保持客户',
'101':'重要发展客户',
'001':'重要挽留客户',
'110':'一般价值客户',
'010':'一般保持客户',
'100':'一般发展客户',
'000':'一般挽留客户',
# 将消费记录表转透视表,user_id为行索引,分别对订单日期取最大值,订单金额汇总,订单产品数求和。
rfm=df.pivot_table(index='user_id',
values=['order_products','order_amount','order_dt'],
aggfunc={'order_dt':'max',
'order_amount':'sum',
'order_products':'sum'})
rfm.head()
image.png
# 因为数据日期距今天比较久远,所以这里令表中日期的最大值为当前日期
# 求R值
RFM['R'] = (RFM.order_dt.max() - RFM.order_dt)/np.timedelta64(1,'D')
# 重命名
RFM = RFM.rename(columns = {'order_amount':'M','order_products':'F'})
RFM.head()
image.png
将个用户分组,按照RFM的均值作为标准,比均值大就为1,比均值小就为0
# 设定函数,如果
def rfm_fumc(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':'一般挽留客户'}
result = d[label]
return result
# apply 默认axis = 0,按列映射
RFM['label'] = RFM[['R','F','M']].apply(lambda x : x - x.mean()).apply(rfm_fumc,axis = 1)
RFM.to_excel(r'RFM.xlsx')
RFM.head()
image.png
image.png
3.5 用户购买周期(按订单)
3.5.1 用户消费周期描述
购买周期,即用户每次购买的间隔
# 利用shift将用户的购买日期向下偏移1行,然后相减就可以得到每次购买的间隔日期
order_diff = grouped_user.apply(lambda x : x.order_dt - x.order_dt.shift())
# 然后描述性统计
order_diff.describe()
image.png
3.5.2 用户消费周期分布
对用户的消费周期进行区间划分,然后对划分后的组进行分类计数即为消费周期的分布
# 去掉相减后的单位days
order_diff_info = (order_diff/np.timedelta64(1,'D'))
# 指定分箱
order_diff_cut_lst = [i for i in range(0,int(order_diff_info.max())+1,10)]
# 按照分箱去分组划分
order_diff_info_hist = pd.cut(order_diff_info,bins = order_diff_cut_lst,labels = order_diff_cut_lst[1:])
# 用10填充NaN。NaN为第一次购买,所以没有周期
order_diff_info_hist = order_diff_info_hist.fillna(10)
order_diff_info_hist.to_excel(r'order_dt_diff_info.xlsx')
image.png
订单周期呈指数分布
绝大部分用户的购买周期都低于100天
3.6 用户生命周期分布
用户生命周期即为最后一次购买的日期减去第一次购买的日期。
按照用户分组,获取第一次与最后一次够吗的日期,并求间隔
# 分组获取第一次与最后一次购买日期
user_life = grouped_user['order_dt'].agg(['min',max])
# 获取购买周期
user_life['user_life'] = user_life['max'] - user_life['min']
user_life['user_life'] = user_life['user_life']/np.timedelta64(1,'D')
对购买周期进行分组划分,统计各分组的频数可形成用户周期的分布图
user_life_info = user_life['user_life']
# 分箱,然后按照分箱分区划分,对于只购买一次的用户,不进行处理,即为NaN,后续在处理的时候不会统计NaN
user_life_lst = [i for i in range(0,int(user_life_info.max()+10),10)]
user_life_info_hist = pd.cut(user_life_info,bins = user_life_lst,labels = user_life_lst[1:])
user_life_info_hist.to_excel(r'user_life_info_noNaN.xlsx')
image.png
4. 复购率和回购率分析
复购率:自然月内,购买多次的用户占比
本分析,定义复购为当月多次购买为复购
回购率:曾经购买过的用户在某一时期的再次购买的占比
本分析,定义上个月购买,这个月继续购买为回购。即周期为1个月
4.1 复购率
对df进行用户和月份的透视,值为用户单月购买的次数
# 以用户为索引,月份为列,对订单进行计数,对于空值填充0,得到的值就为用户单月购买的次数
pivoted_counts=df.pivot_table(index='user_id',
columns='month',
values='order_dt',
aggfunc='count').fillna(0)
pivoted_counts.head()
image.png
为了方便统计,我们把用户单月购买次数大于等于2的设为1,表示复购
只有一次购买设置为0,表示有购买,没有复购
没有购买的设置为NaN,表示未购买,方便后续算复购率
purchase_r=pivoted_counts.applymap(lambda x: 1 if x>1 else np.NaN if x==0 else 0)
purchase_r.head()
image.png
计算复购率
# 每列的和即为当月的复购次数,每列的计数即为当月购买客户的数量
purchase_r_reshop = (purchase_r.sum()/purchase_r.count()).reset_index(name = 'reshop')
purchase_r_reshop['month'] = purchase_r_reshop['month'].astype(str)
purchase_r_reshop.to_excel(r'purchase_reshop.xlsx')
image.png
4.2 回购率
#在用户单月购买次数的表的基础上,将当月购买过标记为1,未购买过的标记为0
df_purchase = pivoted_counts.applymap(lambda x : 1 if x > 0 else 0)
df_purchase.head()
image.png
接下来判断是否回购,通过apply设置axis = 1,对每个用户逐个映射。
先判断该用户本月是否购买,如果没购买:填充NaN,进行下一个月份的判断;如果购买:则判断下个月是否够购买,如果购买则即为复购1,否则为未复购0
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
indexs=df['month'].sort_values().astype('str').unique()
purchase_b = df_purchase.apply(lambda x :pd.Series(purchase_back(x),index = indexs),axis =1)
purchase_b.head()
image.png
计算各月的复购率
# 对各列求和即为当月复购的次数,对各列计数即为当月购买的次数
purchase_backshop = purchase_b.sum()/purchase_b.count()
purchase_backshop.index = purchase_backshop.index.astype('str')
purchase_backshop.to_excel(r'purchase_backshop.xlsx')
image.png
网友评论