用户消费行为分析
目录
一.项目背景与数据来源
1.1 项目背景
用户分析一方面我们要知道用户得基础属性,他可以反映用户的能力和意愿。另一方面我们需要用户的行为数据,他反映了用户消费能力,兴趣偏好。通过用户行为数据分析,可以使运营部门在营销时更加具有针对性,从而节省成本,提升效率。为产品运营提供策略帮助,提升产品生命周期。
CDNow曾经是一家在线音乐零售平台,后被德国波泰尔斯曼娱乐集团公司出资收购,其资产总价值在最辉煌时曾超过10亿美元。本文主要通过分析CDNow网站的用户购买明细来分析该网站的用户消费行为。
1.2 数据来源
数据来源CDNow网站的用户在1997年1月1日至1998年6月30日期间内购买CD交易明细,共有234213条数据,主要包含以下四个字段:
- user_id 用户ID
- order_dt 购买日期
- order_products 购买产品数
- order_amount 购买金额
二.提出问题
三. 数据处理
3.1 数据得获取和查看
#导入常用库
import pandas as pd
import numpy as np
from datetime import datetime
#导入源数据
columns = ['用户ID','购买日期','订单数','订单金额']
df = pd.read_table("CDNOW_master.txt",names = columns,sep = '\s+')
#加载包和数据,文件是txt,用read_table方法打开
#因为原始数据不包含表头,所以需要赋予
#字符串是空格分割,用read_table中\s+表示匹配任意空白符
#加载包和数据,文件是txt,用read_table方法打开,因为原始数据不包含表头,所以需要赋予。字符串是空格分割,用\s+表示匹配任意空白符
df.head()
观察数据,order_dt表示购买时间,但是他是年月日组合的一串数字,不是时间类型,,需要转换。
df.describe()
- 用户每个订单平均购买2.41个商品,每个订单平均消费35.89元。
- 购买商品数量的标准差为2.33,说明数据具有一定的波动性;中位数为2个商品,75分位数为3个商品,说明大部分订单的购买数量都不多。最大值在99个,数字比较高。购买金额的情况差不多,大部分订单都集中在小额。大部分用户都是小额,然而小部分用户贡献了收入的大头,俗称二八。
df.info()
- 原数据没有空值,很干净的数据。
- 当利用pandas进行数据处理的时候,经常会遇见数据类型的问题,order_dt数据类型是int64
3.2 数据处理
df["order_dt"]= pd.to_datetime(df.order_dt,format="%Y%m%d")
# pandas to_datetime将给定的日期时间转换为timestamp的标准日期时间格式
# https://pandas.pydata.org/pandas-docs/version/0.15/generated/pandas.to_datetime.html
# https://blog.csdn.net/qq_39290225/article/details/98313743
df["month"]=df.order_dt.astype("datetime64[M]")
# astype 转换数据类型 时间类型可以截取到年Y 或 月M 等等
# https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.astype.html
df.head()
在这里插入图片描述
四. 用户总体消费趋势分析
#加载可视化的包
import matplotlib.pyplot as plt
#可视化显示在页面
%matplotlib inline
#更改设计风格
plt.style.use('ggplot')
#解决中文显示参数设置
plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False #用来正常显示负号
#按照月份进行分组
grouped_month = df.groupby("month")
order_month_amount = grouped_month[["order_amount"]].sum()
order_month_amount.head()
4.1 每月的总销售额
order_month_amount.plot()
- 由上图可知,消费金额在前三个月达到最高峰,后续消费金额较为稳定,有轻微下降趋势
4.2 每月的消费次数
grouped_month[["user_id"]].count().plot()
前三个月的消费次数在10000次左右,后续月份的平均消费此时在2500次
4.3 每月的销量
grouped_month[["order_products"]].sum().plot()
4.4 每月的消费人数
grouped_month[["user_id"]].apply(lambda x:len(x.drop_duplicates())).plot()
- 四个折线图的整体趋势基本一致,可以看出来,1997年前3个月的销量特别高,随之而来的销售额也是暴涨,在3月份之后骤然下降,接*稳。为什么会呈现这个原因呢?我们假设是用户身上出了问题,早期时间段的用户中有异常值,第二假设是各类促销营销,但这里只有消费数据,所以无法判断。
- 另一方面,在2月到3月这段期间,可以发现消费人数稍有下降,但总销量与总销售额却依然上升,是不是说明3月份的用户中有我们需要重点发展的高价值客户呢?
五.用户个体消费数据分析
#根据用户分组
grouped_user = df.groupby('user_id')
5.1 用户消费金额和消费次数的描述统计
grouped_user.sum().describe()
从用户角度看,每位用户平均购买7张CD,最多的用户购买了1033张。用户的平均消费金额(客单价)106元,标准差是240,结合分位数和最大值看,平均值才和75分位接近,肯定存在小部分的高额消费用户,这也符合二八法则。
5.2 用户消费金额和消费次数的散点图
grouped_user.sum().plot.scatter(x='order_amount',y='order_products')
我们将消费金额大于等于4000的点比较稀疏,我们将其过滤掉
# 进行一个过滤
grouped_user.sum().query('order_amount<4000').plot.scatter(x='order_amount',y='order_products')
- 金额和商品量的关系也因此呈线性,没几个离群点,商品比较单一,用户比较健康而且规律性很强。
5.3 用户消费金额的分布图
grouped_user.sum().query('order_amount < 1500').order_amount.plot.hist(bins = 100) # bins设置组数为100
- 大部分用户的消费能力并不高,将近半数的用户消费金额不超过40元,高消费用户( >200元 )不超过2000人。
5.4 用户消费次数的分布图
grouped_user.sum().query(‘order_products < 60’).order_products.plot.hist(bins = 50)
- 大部分用户购买CD的数量都是在3张以内,购买大量CD的用户数量并不多。
5.5大客户贡献情况
5.5.1用户销售额贡献情况
# 计算每个用户的消费金额总额
user_sum = grouped_user.sum().sort_values('order_amount')
# 计算用户的累加消费额
user_cumsum = grouped_user.sum().sort_values('order_amount').cumsum()
# 计算累计消费占比= 用户累加消费额/总消费额
user_cumsum = grouped_user.sum().sort_values('order_amount').apply(lambda x:x.cumsum()/x.sum())
user_cumsum.reset_index().order_amount.plot()
- 说明:前20000个用户,大约80%的用户贡献了40%的销售额,20%的用户贡献了60%的销售额
5.5.2用户销量贡献情况
# 计算累计消量占比= 用户累加消量/总消量
user_Pcumsum = grouped_user.sum().sort_values('order_products').apply(lambda x:x.cumsum()/x.sum())
user_Pcumsum.reset_index().order_amount.plot()
plt.xlabel("用户数")
plt.ylabel("累计消量占比")
- 跟销售额十分接近。前80%用户贡献了40%的消费,而后20%用户贡献了60%的消费。符合二八趋势。也就是们只要维护了这20%用户就可以把业绩KPI完成60%,如果能把20%的用户运营的更好就可以占比70%—80%之间。
六.用户消费周期分析
6.1 用户购买周期(按订单)
6.1.1 用户消费周期描述
df.order_dt.head()
df.order_dt.shift().head()#shift()就是将所有数据进行了一个向下平移
#计算每个用户的每笔订单的周期
order_diff = grouped_user.apply(lambda x:x.order_dt - x.order_dt.shift())#d
#购买周期描述
order_diff.describe()
- 用户平均购买周期是68天,最大购买周期是533天,想要召回用户,在六十几天天左右的消费间隔是比较好的。
6.1.2 用户消费周期分布
#购买周期分布图
plt.style.use('ggplot')
(order_diff / np.timedelta64(1,'D')).hist(bins = 20)#去掉单位
plt.title('用户购买周期分布')
plt.xlabel('时间天数')
plt.ylabel('频次')
- 典型的(指数)长尾分布,大部分用户的消费间隔确实比较,消费在一到十天的用户非常多。不妨将时间召回点设为消费后立即赠送优惠券,消费后10天询问用户礼品怎么样,消费后20天提醒优惠券到期,消费后30天短信推送。
6.2 用户生命周期(按第一次&最后一次消费)
接下来计算每一位用户生命周期,这里定义第一次消费至最后一次消费为整个用户生命。
6.2.1 用户生命周期描述
#用户第一次消费(首购)
grouped_user.min().order_dt.value_counts().plot()
# pandas 的 value_counts() 函数可以对Series里面的每个值进行计数并且排序。https://www.cnblogs.com/keye/p/9664414.html
#用户最后一次消费
grouped_user.max().order_dt.value_counts().plot()
-
用户最后一次购买买的分布比第一次分布广 大部分用户最后一次购买,集中在前三个月,说明很多用户购买一次后就不再购买
-
随着时间的递增,最后一次购买数也在递增,消费呈现流失上升的情况
user_life= grouped_user.order_dt.agg(['min','max'])
user_life.head()
#用户生命周期(按第一次&最后一次消费)
(user_life['max'] - user_life['min']).describe()
- 所有用户的平均生命周期是134天, 消费一次的用户超过一半。
6.2.2 用户生命周期分布
plt.style.use('ggplot')
((user_life['max'] - user_life['min']) / np.timedelta64(1,'D')).hist(bins = 40)
- 大部分用户只消费了一次,所有生命周期的大头都集中在了0天。
但这不是我们想要的答案,不妨将只消费了一次的新客排除,来计算所有消费过两次以上的老客的生命周期。
#排除只消费一次的数据
user_life['a0']=(user_life['max']-user_life['min'])/np.timedelta64(1,'D')
plt.style.use('ggplot')
user_life.loc[user_life['a0']>0]['a0'].hist(bins=50)
上图可见,用户生命周期呈现双峰趋势,20天内生命周期的用户是一个高峰,400至500天内生命周期的用户是另一个高峰。
根据此情况,应该在20天内对客户进行引导,促进其再次消费并形成消费习惯,延长其生命周期;在100至400天的用户,也要根据其特点推出有针对性的营销活动,引导其持续消费。
七.用户分层
7.1 按用户价值分层—RFM模型
为了进行用户精细化运营,可以利用RFM模型来衡量用户价值和用户创利能力,其中
R(Recency)最近一次消费时间:客户最近一次交易时间的间隔。R值越大,表示客户交易发生的日期越久,反之则交易发生的日期越近。
F(Frequency)消费频率:消费频率是指用户在统计周期内购买商品的次数。F值越大,表示客户交易越频繁,反之则表示客户交易不够活跃。
M(Monetary)消费金额:消费金额是指用户在统计周期内消费的总金额。M值越大,表示客户价值越高,反之则表示客户价值越低。
根据上述三个维度,对客户做细分:
#透视表将rfm需要的字段拎出来
rfm = df.pivot_table(index = "user_id",
values=['order_products','order_amount','order_dt'],
aggfunc={'order_products':'sum','order_amount':'sum','order_dt':'max'})
rfm.head()
将日期的最大值和当前日期的差值作为R
rfm['R'] = -(rfm.order_dt - rfm.order_dt.max())/np.timedelta64(1,'D')#最近一次消费距离最近时间的天数,np.timedelta64(1,'D')相当于天数的单位 1表示小数位的个数
rfm.rename(columns= {'order_products':'F','order_amount':'M'},inplace = True)
rfm
# 标准化 减去平均值
rfm[['R','F','M']].apply(lambda x: x-x.mean())
# 构建rfm模型
def rfm_func(x):
level = x.apply(lambda x:'1' if x >= 0 else '0')
label = level.R + level.F +level.M
d = {
'011':'重要价值客户',
'111':'重要保持客户',
'001':'重要发展客户',
'101':'重要挽留客户',
'010':'一般价值客户',
'110':'一般保持客户',
'000':'一般发展客户',
'100':'一般挽留客户',
}
result = d[label]
return result
rfm['label'] = rfm[['R','F','M']].apply(lambda x: x-x.mean()).apply( rfm_func,
axis=1 )
rfm
#c求和
rfm.groupby("label").sum()
- 从rfm分层可知,重要价值客户的累计消费金额排名最高,高达1592039。
#计数
rfm.groupby("label").count()
- 不同层次用户的消费人数,一般挽留用户的消费人数排名第一,有14074人,重要价值客户排名第二,有4554人,与一般挽留用户差距比较大,但累计消费金额最多,业务方可以根据结果对客户分类运营,降低营销成本,提高ROI。
rfm.loc[rfm.label == '重要价值客户','color'] = 'g' # 给rfm添加颜色这一列,将重要价值客户表示成绿色
rfm.loc[rfm.label == '重要保持客户','color'] = 'r'# 将重要保持客户表示成红色
rfm.loc[rfm.label == '重要挽留客户','color'] = 'b'# 将重要发展客户表示成蓝色
rfm.loc[rfm.label == '重要发展客户','color'] = 'y'# 将重要挽留客户表示成蓝色
#将一般客户表示成黑色
rfm.loc[rfm.label == '一般价值客户','color'] = 'k'
rfm.loc[rfm.label == '一般保持客户','color'] = 'k'
rfm.loc[rfm.label == '一般挽留客户','color'] = 'k'
rfm.loc[rfm.label == '一般发展客户','color'] = 'k'
#rfm.loc[~(rfm.label == '重要价值客户'or rfm.label == '重要保持客户'or rfm.label == '重要挽留客户' or rfm.label == '重要发展客户'),'color'] = 'k' #给rfm添加颜色这一列,将非重要客户表示成黑色
rfm.plot.scatter('F','R',c = rfm.color) # scatter(x坐标,y坐标,c表示颜色) 我们必须在rfm增加一列
7.2 按用户活跃程度分层——新用户、活跃用户、不活跃用户、回流用户
- 新用户的定义是第一次消费。
- 活跃用户即老客,在某一个时间窗口内有过消费。
- 不活跃用户是注册了的用户在这个时间窗口内没有消费过的老客。
- 回流用户是注册了得用户在上一个窗口中没消费,而在当前时间窗口内有过消费。
#将用户消费数据进行数据透视:
pivoted_counts = df.pivot_table(index="user_id",
columns = "month",
values = "order_dt",
aggfunc = "count").fillna(0)
pivoted_counts.head()
- 图中的数字(0,1,2)代表了当月的消费次数。
df2 = df1.applymap(lambda x:1 if x>0 else 0)
df2.tail()
- 0代表当月没有消费,1代表有消费。
#用户活跃分层函数
def active_status(data):
status = []#活跃状态
for i in range(18):#month 上面得月份数
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') #这个月就是活跃用户
data.iloc[0:]=status#将status赋值给第一行
return data
#可得到一张不同用户在不同月份的不同状态(new=新、active=活跃、return=回流、unactive=流失),unreg相当于未注册,指这个用户在这个月及以前从未购买过产品,主要为了统计起来更加方便而加进去。
indexs=df['月份'].sort_values().astype('str').unique()
purchase_stats = df_purchase.apply(active_status,axis = 1)#轴向为行
product_stats.head(5)
#将未注册的用户调整为缺失值,便于对其他类型进行计数
purchase_stats_ct = purchase_stats.replace('unreg',np.NaN).apply(lambda x:pd.value_counts(x))
purchase_stats_ct
#将NAN换成0 然后用T进行转列
purchase_stats_ct.fillna(0).T.head()
- 从表中可以看出,新客都是集中在前三个月,回流用户整体在1000左右,而不活跃用户数量随时间稍有上升。
#每个月消费占比构成
purchase_stats_ct.fillna(0).T.apply(lambda x:x/x.sum(),axis=1)
#制作面积图,可以更直观的观察走势
plt.style.use('ggplot')
purchase_stats_ct.fillna(0).T.plot.area()
plt.title('每月用户活动状态')
plt.ylabel('用户人数')
由上面的图表我们看到:
- 新客用户:仅在前三个月,后续再无新增客户。
- 活跃用户:比例持续下降,说明持续消费的用户数量在减少,也说明运营部门的促活效果并不好。
- 回流用户:比例也稍有下降,在4%左右波动。
- 不活跃用户:比例稍有上升,流失较大。
#每月用户回回占比
#求出每月不同状态用户的个数
pivot_user_status=purchase_stats.apply(lambda x:x.value_counts()).fillna(0).T.drop('unreg',axis=1)
(pivot_user_status['return']/pivot_user_status['unactive'].shift()).plot()#shift()表示取上一个单元格得数据 即这个月得回流除以上个月的活跃 https://blog.csdn.net/weixin_30760895/article/details/95394034?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase
plt.title('每月回流用户占比')
plt.ylabel('用户占比')
每月回流用户占比:指的是上个月不活跃的用户在本月重新活跃的用户占比; 从图可知,用户的整体活跃度在不断下降,用户正在流失。
八.用户质量分析
8.1 复购率
复购率的定义:在某时间窗口内消费两次及以上的用户在总消费用户中占比。这里的时间窗口是月,如果一个用户在同一天下了两笔订单,这里也将他算作复购用户
##求复购,消费次数大于1的则说明是复购,其余不是
# x > 1 就表示购买多次 复购了 purchase_r赋值为1
# x = 1 就表示购买1次 purchase_r赋值为0
# x = 0 就表示没有购买 purchase_r赋值为NaN
purchase_r = pivoted_counts.applymap(lambda x:1 if x > 1 else np.NaN if x == 0 else 0)#多重条件得嵌套写法 当x<=1 np.NaN if x == 0 else 0
plt.style.use('ggplot')
(purchase_r.sum() / purchase_r.count()).plot(figsize = (10,4))
- 复购率稳定在20%左右,前三个月因为有大量的新用户涌入,而这批用户只购买了一次,所以导致复购率并不高
8.2 回购率
回购率:是某一个时间窗口内消费的用户,在下一个时间窗口仍旧消费的占比。比如,1月消费用户1000,他们中有300个2月依然消费,回购率是30%。
#回购率的定义
#本月有消费,下月有消费,为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)
status.append(np.NaN) #最后这边是因为我们的数据截止到6月,没有7月的数据进行计算,所以定义为空值
data.iloc[0:]=status
return data
purchase_b = df_purchase.apply(purchase_back,axis = 1)
plt.style.use('ggplot')
(purchase_b.sum() / purchase_b.count()).plot(figsize = (10,4))
- 上图可以看出,在初期用户的回购率并不高,1月的回购率只有15%左右,4月份起回购率稳定在30%左右。
- 从每月有回购消费的用户数数据可以看出,回购用户数整体有下降趋势。
- 对回购率的分析,再次说明了对于新用户,在其第一次消费后的三个月内是一段重要的时期,需要营销策略积极引导其再次消费及持续消费。
- 另外,对于有持续消费的老客,也要适时推出反馈老客户的优惠活动,以加强老客的忠诚度
8.3分析留存率(3,7,15,30,90,365天为周期)
orderdt_min=df.groupby('user_id').order_dt.min().reset_index()#第一次消费
orderdt_copy = df[['user_id','order_dt','order_products','order_amount']]
user_purchase_retention = pd.merge(left=orderdt_copy,right=orderdt_min,how='inner',on='user_id',suffixes=('','_min'))
user_purchase_retention.head()
增加一列,表示订单日期与用户首次消费日期间的间隔时间,将时间差值分桶处理:
分成0~3天内,3~7天内,7~15天……代表用户当前消费时间距第一次消费属于哪个时间段
#每一次消费时间与第一次消费时间间隔
user_purchase_retention['order_date_diff']=user_purchase_retention['order_dt']-user_purchase_retention['order_dt_min']
#将timedelta转换为数值型
user_purchase_retention['date_diff']=user_purchase_retention.order_date_diff.apply(lambda x:x/np.timedelta64(1,'D'))
user_purchase_retention.head(5)
#将时间间隔分桶(0-3)(3-7)等
bin=[0,3,7,15,30,60,90,180,365]
user_purchase_retention['date_diff_bin']=pd.cut(user_purchase_retention.date_diff,bins=bin)#用cut进行分桶
user_purchase_retention.head(10)
#这里date_diff=0并没有被划分入0~3天,因为计算的是留存率,如果用户仅消费了一次,留存率应该是0。另外,如果用户第一天内消费了多次,但是往后没有消费,也算作留存率0。
#用pivot透视表 用户第一次消费之后,后续各时间段的消费次数
pivoted_retention=user_purchase_retention.pivot_table(index='user_id',
columns='date_diff_bin',values='order_products',aggfunc=sum,dropna=False)
#1代表有消费,0代表没有
pivoted_retention_trans=pivoted_retention.applymap(lambda x:1 if x>0 else 0)
pivoted_retention_trans
#不同时间间隔的留存率
(pivoted_retention_trans.sum()/pivoted_retention_trans.count()).plot.bar(figsize=(10,5))
plt.xlabel('消费时间间隔')
plt.title('留存率')
只有2.5%的用户在第一次消费的次日至3天内有过消费,3%的用户在3~7天内有过消费。数字并不好看,CD购买确实不是高频消费行为。有20%的用户在第一次消费后的三个月到半年之间有过购买,27%的用户在半年后至1年内有过购买。
从运营角度看,CD机营销在服务新用户的同时,应该注重用户忠诚度的培养,放长线掉大鱼,在一定时间内召回用户购买。
九.结论
1.整体趋势:按年的月份趋势销量和销售额在1-3月份相对极高,然后骤降,原因可能跟这段时间的大力促销或与商品的季度属性有关。
2.用户个体特征:每笔订单的金额和商品购买量都集中在区间的低段水平,都是小金额小批量进行购买,此类交易群体,可在丰富产品线和增加促销活动提高转换率和购买率。
3.大部分用户的消费总额和购买总量都集中刚在低段,长尾分布,这个跟用户需求有关,可以对商品进行多元文化价值的赋予,增强其社交价值属性,提高用户的价值需求。
4.用户的消费周期:有二次以上消费的用户,平均68天,所以在50天到60天期间,应该对这批用户进行刺激召回,细致点,比如10天回复满意度,30天发放优惠券,55天的时候提醒优惠券的使用。
5.用户的生命周期:有二次及以上消费的用户的平均生命周期是276天。用户的生命周期分别在20天内与400至500天间,应该在20天内对客户进行引导,促进其再次消费并形成消费习惯,延长其生命周期;在100至400天的用户,也要根据其特点推出有针对性的营销活动,引导其持续消费。
6…新客户的复购率约为6%,老客户的复购率在20%左右;新客户的回购率在15%左右,老客户的回购率在30%左右,需要营销策略积极引导其再次消费及持续消费。
7.用户质量:用户个体消费有一定规律性,大部分用户的消费集中在2000以下,用户消费反应了2/8法则,消费排名前20%的用户贡献了80%的消费额。所以说,狠抓高质量用户是万古不变的道理,这些高质量客户都是“会员”类型,需要专门为会员优化购物体验,比如专线接听、特殊优惠等等。
7.留存率来看,一半的用户会流失,所以应该注重对用户的忠诚度的培养,比如打卡签到,积分制度,老用户打折制度会员升级制度。
本文地址:https://blog.csdn.net/weixin_45339389/article/details/107341599