数据来自CDNow网站的一份用户购买CD明细,现在尝试从不同维度分析用户的消费行为
- 分析内容:
- 按月对数据进行分析:每月用户消费次数,每月用户的购买量,每月用户的购买总金额
- 用户个体消费数据分析:用户个体消费金额与消费次数的统计描述,用户消费金额和消费次数的散点图,用户消费金额的分布图(符合二八法则),用户消费次数的分布图,用户累计消费金额的占比
- 用户消费行为分析:用户第一次消费时间,用户最后一次消费时间,用户分层,新老客消费比,用户购买周期,用户生命周期
一、理解数据
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
plt.style.use('ggplot')
import os
os.chdir(r'D:\anaconda\workplace\Data_Analysis\Data')
# user_id:用户ID order_dt:购买日期 order_products:购买产品数 order_amount:购买金额
columns = ['user_id','order_dt','order_products','order_amount']
# 用户id 购买日期 购买产品数 购买金额
df = pd.read_table("master.txt",names = columns,sep = '\s+')
df.head()
可以看到
- 数据集有近7W条数据,没有空值,数据集比较干净
- 大部分订单只消费了少量商品(平均2.4),中位数是2,其分位数是3,说明用户购买量大部分都不多,少部分购买量大的,有一定极值干扰
- 用户的订单金额比较稳定,人均购买CD金额在35,中位数在25元,存在极致干扰,小额比较多,大额较少,收入来源很大一部分是来自大额。符合二八原则
二、按月分析用户消费趋势
- 每月的消费总金额
- 每月的消费次数
- 每月的产品购买量
- 每月的消费人数
1、每月的消费总金额
# 解析日期
df['oreder_dt'] = pd.to_datetime(df.order_dt, format='%Y%m%d')
df['month'] = df.order_dt.values.astype('datetime64[M]')
grouped_month = df.groupby('month')
plt.figure(1, figsize=(10, 4))
plt.title('每月销售额')
plt.ylabel('销售额')
grouped_month.order_amount.sum().plot()
plt.show()
可以看到,消费金额呈现早期销售多,后期剧降后趋向平稳
2、每月的消费次数
plt.figure(1, figsize=(10, 4))
plt.title('每月订单数')
plt.ylabel('订单数')
grouped_momnth.user_id.count().plot()
plt.show()
前三个月订单数在 10000 笔左右,后续月份的平均消费订单数则在 2500 笔左右。
3、每月的产品购买量
plt.figure(1, figsize=(10, 4))
plt.title('每月用户购买张数')
plt.ylabel('CD碟数(张)')
grouped_month.order_products.sum().plot()
plt.show()
从图中可以看到,销量在前几个月异常高涨,并在3月达到最高峰,4月开始骤降,后续销量较为稳定,且有轻微下降趋势。
4、每月的消费人数
plt.figure(1, figsize=(10, 4))
plt.title('每月消费人数')
plt.ylabel('人数')
grouped_month.user_id.apply(lambda x: len(x.drop_duplicates())).plot()
plt.show()
每月的消费人数小于每月的消费次数,但是区别不大。前三个月每月的消费人数在8000—10000之间,后续月份,平均消费人数在2000不到。一样是前期消费人数多,后期平稳下降的趋势。推测前三个月可能由于商家有促销活动,吸引大量的用户来消费购买
三、用户个体消费分析
- 用户消费金额、消费次数的描述统计
- 用户消费金额和消费次数的散点图
- 用户消费金额的分布图
- 用户消费次数的分布图
- 用户累计消费金额占比(百分之多少的用户占了百分之多少的消费额)
1、用户消费金额,消费次数的描述统计
grouped_user = df.groupby('user_id')
grouped_user.sum().describe()
- 用户平均购买了7张CD,但是中位数为3,说明小部分用户购买了大量CD,平均值大于中位数,是右偏分布,说明小部分用户购买了大量的 CD。
- 用户平均消费106元,中位数为43,结合分位数和最大值看,平均数仅和 75 分位接近,肯定存在小部分的高频消费用户。
2、用户消费金额和购买数量的散点图
plt.figure(figsize=(12,4))
plt.subplot(121)
plt.scatter(x = 'order_amount', y = 'order_products',data=df)
plt.xlabel('每笔订单消费金额')
plt.ylabel('每笔订单购买数量')
plt.subplot(122)
plt.scatter(x = 'order_amount',y = 'order_products',
data = grouped_user.sum())
plt.xlabel('每位用户消费金额')
plt.ylabel('每位用户购买数量')
plt.show()
- 从每笔订单的散点图观察:大部分订单消费金额处于400元以下,每笔订单购买数量处于40以下,所以大部分用户都是少量购买
- 从每位用户的消费散点图观察:每位用户购买数量与每位用户消费金额具有很强的线性相关性,也好理解,这是 CD 网站的销售数据,商品较为单一,金额和商品的关系因此呈线性关系,离群点不多
3、用户消费分布图
plt.figure(figsize=(12, 4))
plt.subplot(121)
ax = grouped_user.order_amount.sum().hist(bins=50)
ax.set_xlabel('金额(美元)')
ax.set_ylabel('用户人数')
ax.set_xlim(0, 2000)
ax.set_title('用户消费金额分布图')
plt.subplot(122)
ax1 = grouped_user.order_products.sum().hist(bins = 50)
ax1.set_xlabel('CD 数(张)')
ax1.set_ylabel('用户人数')
ax1.set_xlim(0, 150)
ax1.set_title('购买 CD 数分布图')
plt.show()
从直方图可知,用户消费金额,绝大部分呈现集中趋势,大部分用户购买CD数在20张内,高消费用户在图上几乎看不到,这是符合消费行为的行业规律。
4、用户累计消费金额的占比
user_cumsum = grouped_user.sum().sort_values('order_amount').apply(lambda x:x.cumsum()/x.sum())
user_cumsum.reset_index().order_amount.plot()
按用户消费金额进行升序排列,由图可知50%的用户仅贡献了15%的消费额度;而排名前5000的用户就贡献了60%的消费额
四、用户消费行为
- 用户第一次消费(首购)
- 用户最近一次消费
- 用户分层
- RFM
- 新、老、活跃、流失
- 用户购买周期(按订单)
- 用户消费周期描述
- 用户消费周期分布
- 用户生命周期(按第一次&最后一次消费)
- 用户生命周期描述
- 用户生命周期分布
1、用户首购时间
grouped_user.min().order_dt.value_counts().plot()
用户第一次购买分布,集中在前三个月,其中,在2月11日至2月25日有一次剧烈的波动
2、用户最近一次购买时间
grouped_user.max().order_dt.value_counts().plot()
可以看出
- 用户最后一次购买的分布比第一次分布广
- 大部分最后一次购买,集中在三个月,说明很多用户购买了一次后就不进行购买
- 随着时间的递增,最后一次购买数也在递增,消费呈现流失上升的状况
3、用户分层
3.1 构建RFM 模型
- R:末次消费距今的天数(数值越小越好)
- F:消费频率(越高越好)
- M:消费金额(越高越好)
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['R'] = -(rfm.order_dt - rfm.order_dt.max())/np.timedelta64(1,'D')
rfm.rename(columns={'order_products':'F','order_amount':'M'},inplace=True)
rfm.head()
def rfm_func(x):
level = x.apply(lambda x:'1' if x>=1 else '0')
label = level.R + level.F + level.M
d = {
'111':'重要价值客户',
'011':'重要保持客户',
'101':'重要挽留客户',
'001':'重要发展客户',
'110':'一般价值客户',
'010':'一般保持客户',
'100':'一般挽留客户',
'000':'一般发展客户'
}
result = d[label]
return result
rfm['label'] = rfm[['R', 'F', 'M']].apply(lambda x:x-x.mean()).apply(rfm_func,axis=1)
rfm.head()
rfm.groupby('label').sum()
for label,gropued in rfm.groupby('label'):
x= gropued['F']
y = gropued['R']
plt.scatter(x,y,label = label) # 利用循环绘制函数
plt.legend(loc='best') # 图例位置
plt.xlabel('Frequency')
plt.ylabel('Recency')
plt.show()
从RFM分层可知,大部分用户为重要保持客户,但是这是由于极值的影响,所以RFM的划分标准应该以业务为准
- 尽量用小部分的用户覆盖大部分的额度
- 不要为了数据好看划分等级
3.2 按新、活跃、回流、流失分层用户
pivoted_counts = df.pivot_table(index='user_id',
columns = 'month',
values='order_dt',
aggfunc='count').fillna(0)
pivoted_counts.head()
df_purchase = pivoted_counts.applymap(lambda x:1 if x > 0 else 0)
df_purchase.tail()
col = ['1997-01-01', '1997-02-01', '1997-03-01', '1997-04-01',
'1997-05-01', '1997-06-01', '1997-07-01', '1997-08-01',
'1997-09-01', '1997-10-01', '1997-11-01', '1997-12-01',
'1998-01-01', '1998-02-01', '1998-03-01', '1998-04-01',
'1998-05-01', '1998-06-01']
def active_status(data):
status = []
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 pd.Series(status,index = col)
若本月没有消费
- 若之前是未注册,则依旧为未注册
- 若之前有消费,则为流失/不活跃
若本月有消费
- 若是第一次消费,则为新用户
- 如果之前有消费,上个月为不活跃,则为回流
- 如果上个月为未注册,则为新用户
- 除此之外,为活跃
purchase_stats = df_purchase.apply(active_status,axis = 1)
purchase_stats.head()
purchase_stats_ct = purchase_stats.replace('unreg',np.nan).apply(lambda x:pd.value_counts(x))
purchase_stats_ct.fillna(0).T.apply(lambda x:x/x.sum(),axis=1)
由上表可知,每月的用户消费状态变化
- 活跃用户,持续消费的用户,对应的是消费运营的质量
- 回流用户,之前不消费本月才消费,对应的是唤回运营
- 不活跃的用户。对应的是流失
purchase_stats_ct.fillna(0).T.plot.area(figsize=(10,6))
可以看到不活跃用户,也就是图中的灰色区域,占了大部分面积,新用户主要集中在1到3月份,活跃用户和回流用户在四月份之后就趋向稳定,属于核心客户,但总量并不高
3.3 回流用户占比
plt.figure(figsize=(20, 6))
rate = purchase_stats_ct.fillna(0).T.apply(lambda x: x/x.sum())
plt.plot(rate['return'],label='return')
plt.plot(rate['active'],label='active')
plt.legend()
plt.show()
- 回流用户比:某个时间段内回流用户在总用户中的占比
- 由图可知,用户每月回流用户比占 5% ~ 8% 之间,有下降趋势,说明客户有流失倾向。
- 回流用户率:上月有多少不活跃用户在本月消费
- 由于这份数据的不活跃用户量基本不变,所以这里的回流率,也近似等于回流比
- 活跃用户比:某个时间段内活跃用户在总用户中的占比。
- 活跃用户的占比在 3% ~ 5%间,下降趋势更显著,活跃用户可以看作连续消费用户,忠诚度高于回流用农户。
结合活跃用户和回流用户看,在后期的消费用户中,60%是回流用户,40%是活跃用户,整体用户质量相对不错。也进一步说明前面用户消费行为分析中的二八定律,反应了在消费领域中,狠抓高质量用户是不变的道理。
4、用户购买周期
# 订单时间间隔
order_diff = grouped_user.apply(lambda x:x.order_dt - x.order_dt.shift())
order_diff.head(10)
order_diff.describe()
# 订单周期分布图
(order_diff/np.timedelta64(1,'D')).hist(bins=20)
- 用户的平均购买周期是68天
- 绝大部分用户的购买周期低于100天
- 可以根据用户的购买周期对用户进行定期召回
5、用户生命周期
# 最后一次购买的时间减去首购时间
user_life = grouped_user.order_dt.agg(['min', 'max'])
user_life.head()
(user_life['max'] - user_life['min']).describe()
# 只消费过一次的用户占比
(user_life['min'] == user_life['max']).value_counts().plot.pie()
plt.show()
- 用户的平均生命周期134天,中位数为0,说明大部分用户的第一次消费也是最后一次消费,这批用户质量较低,而最大的有544天,这批用户属于核心用户
plt.figure(figsize=(20, 4))
plt.subplot(121)
((user_life['max'] - user_life['min']) / np.timedelta64(1, 'D')).hist(bins = 15)
plt.title('二次消费以上用户的生命周期直方图')
plt.xlabel('天数')
plt.ylabel('人数')
# 过滤生命周期为0 的
plt.subplot(122)
u_l = ((user_life['max'] - user_life['min']).reset_index()[0] / np.timedelta64(1, 'D'))
u_l[u_l > 0].hist(bins = 40)
plt.title('二次消费以上用户的生命周期直方图')
plt.xlabel('天数')
plt.ylabel('人数')
plt.show()
由于大量用户的消费周期为0,我们将周期为0的数据进行过滤,得到右边的图,将两图进行对比,可以看出,依然有不少用户的生命周期趋向0天。部分低质量用户虽然消费两次,但并不能持续消费,若要提高用户留存率,应该在用户第一次消费后继续进行引导,少部分用户集中在50 - 350天,属于普通用户,忠诚度一般。350天之后的就属于高质量用户了,忠诚度较高,应尽力维护。
五、复购率和回购率分析
- 复购率:自然月内,购买多次的用户占比
- 回购率:曾经购买的用户在某一时期内的再次购买的占比
复购率
# 消费两次及以上为 1 ,消费一次为 0 ,没有消费为空
purchase_r = pivoted_counts.applymap(lambda x:1 if x>1 else np.nan if x==0 else 0)
purchase_r.head()
(purchase_r.sum()/purchase_r.count()).plot(figsize=(10,4))
- 复购率稳定在20%,前三个月因为有大量新用户涌入,而这批用户只购买了一次,所以导致复购率降低
回购率
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 pd.Series(status,index = col)
purchase_b = df_purchase.apply(purchase_back,axis=1)
purchase_b.head()
- 1 为回购用户, 0 为上月没购买当月购买过,NaN 为连续两月都没购买
plt.figure(figsize=(20,8))
plt.subplot(211)
(purchase_b.sum() / purchase_b.count()).plot()
plt.title('用户回购率图')
plt.ylabel('百分比%')
plt.subplot(212)
plt.plot(purchase_b.sum(),label='每月消费人数')
plt.plot(purchase_b.count(),label='每月回购人数')
plt.xlabel('month')
plt.ylabel('人数')
plt.legend()
plt.show()
从图中可以看出,用户的回购率高于复购,约在30%左右,和老客户差异不大。从回购率和复购率综合分析可以得出,新客的整体质量低于老客,老客的忠诚度(回购率)表现较好,消费频次稍次