数据源:数据来源于网上,是用户在一家CD网站上的消费记录。
链接:https://pan.baidu.com/s/1s1Ep6x5K6bJMLmpb8I_CWg
提取码:am5t
分析维度如下:
- 用户消费趋势分析
- 用户个体消费分析
- 用户消费行为分析
- 复购率和回购率的分析
数据集的读入和观察
import pandas as pd
import numpy as np
#用户id,购买日期,购买产品数,购买金额
columns = ["user_id","order_dt","order_products","order_amount"]
df = pd.read_table('CDNOW_master.txt',names = columns,sep = '\s+')
#此处用s+,是因为我们有多个字符,是由空格进行分割的
df.info() #此处我们看到购买日期的类型是int型,说明我们后面需要进行类型转换
各字段含义说明:
- user_id:用户ID
- order_dt:购买日期
- order_products:购买产品数
- order_amount:购买金额
我们要进行按月的分析,所以需要增加一个字段描述月份。
#转换购买日期的数据类型以及增加一个字段
df['order_dt'] = pd.to_datetime(df.order_dt,format = '%Y%m%d')
df['month'] = df.order_dt.values.astype('datetime64[M]')
df.head()
查看数据的整体分布情况
大部分订单只是消费了少量商品(平均2.4),有一定极值干扰
用户的消费金额比较稳定,平均消费是35元,中位数在25元,仍然有一定极值干扰
用户消费趋势的分析
#每月消费总金额
grouped_month = df.groupby('month') #按月进行分组
order_month_amount = grouped_month.order_amount.sum() #分组后对购买金额求和
import matplotlib.pyplot as plt #加载可视化展示包
%matplotlib inline #魔法函数,将可视化展示出来
plt.style.use('ggplot') #更改设计风格
order_month_amount.plot()
#每月消费次数
grouped_month.user_id.count().plot()
#每月消费订单量
grouped_month.order_products.sum().plot()
#每月消费客户数
df.groupby('month').user_id.apply(lambda x:len(x.drop_duplicates())).plot()
从上面的图表可以看到,消费金额、消费次数、订单量和客户数在前三个月呈上升趋势,且在3月时达到峰值;
4月开始过山车式猛降,随后几个月都趋于稳定,起伏并不大,总体趋势有轻微下降;
每月消费客户数低于每月消费订单量,但差异并不显著;
前三月每月的消费人数在8000-10000之间,后续月份,平均消费人数在2000人不到;
由此可见平台应该要采取一定措施拉新、留存了;
#每月客单价趋势
#对‘月份’及‘用户id’进行分组求和>>去掉表中的索引打平>>再按月份进行分组>>对每月的消费金额求平均
plt.style.use('ggplot')
df.groupby(['month','user_id']).sum().reset_index().groupby('month')['order_amount'].mean().plot()
#每月用户平均消费次数的趋势
user_c=df.groupby(['month','user_id']).count().reset_index()
user_c['count']=user_c['order_products']
user_c.loc[:,['month','user_id','count']].groupby('month')['count'].mean().plot()
#每月件单价走势
df_jiandanj = df.groupby('month').order_amount.sum()/df.groupby('month').order_products.sum()
df_jiandanj.plot()
- 客单价:
①前三个月的客单价最低,根据之前看到消费金额等数据却是最高的,基本可以判定是因为客户数的新增来高销售额;
②从4月开始客单价开始提升但存在轻微起伏,可见后面的客户逐渐开始认可产品,并愿意为之付费; - 月消费次数:
总的消费次数都在1-2次之间,但前三月最低,可见大部分客户都只在最初消费了一次; - 件单价分析:
可以看到前三个月件单价是最高的,但随着时间的推移,件单价开始下降,充分说明价格不是导致客户流失的因素;
用户个体消费分析
#用户消费金额、消费次数的描述统计
grouped_user = df.groupby('user_id') #以用户进行分组
grouped_user.sum().describe()
用户平均购买了7张CD,但是中位值只有3,说明小部分用户购买了大量的CD;
用户平均消费了106元,中位值有43,判断同上,有极值干扰;
#消费金额和消费次数的散点图
grouped_user.sum().plot.scatter(x = 'order_amount',y = 'order_products')
#设置过滤条件,更好的展示两者关系
grouped_user.sum().query('order_amount<4000').plot.scatter(x = 'order_amount',y = 'order_products')
因为我们的数据都是针对CD的消费做的分析,不难看出,销售金额与销售数量是线性关系;
#用户消费金额的分布图
plt.style.use('ggplot')
grouped_user.sum().order_amount.plot.hist(bins = 20)
#设置过滤条件
grouped_user.sum().query('order_products < 100').order_products.hist(bins = 20)
从直方图可知,用户消费金额,绝大部分呈现集中趋势,小部分异常值干扰了判断,所以我们将极值剔除。
#用户累计消费金额占比(百分之多少的用户占了百分之多少的消费额)
user_cumsum = grouped_user.sum().sort_values('order_amount').apply(lambda x:x.cumsum() / x.sum())
plt.style.use('ggplot')
user_cumsum.reset_index().order_amount.plot()
plt.title('用户消费金额占比')
plt.xlabel('用户数量')
plt.ylabel('消费金额占比')
按用户消费金额进行升序排列,由图可知50%的用户仅贡献了15%的消费额度,而排名前500的消费用户就贡献了60%的消费额度,符合消费数据的二八法则。
用户消费行为分析
#用户第一次消费(首购)的趋势图
plt.style.use('ggplot')
grouped_user.min().order_dt.value_counts().plot(figsize = (10,4))
plt.title('用户首购趋势')
plt.ylabel('人数')
#用户最后一次购买趋势
plt.style.use('ggplot')
grouped_user.max().order_dt.value_counts().plot(figsize = (10,4))
plt.title('用户最后一次购买趋势')
plt.ylabel('人数')
用户第一次购买的分布,主要集中在前三个月;
其中,在2月15日左右有一次剧烈的波动,具体原因可能与公司运营活动相关;
用户最后一次购买的分布比首次购买分布广;
大部分最后一次购买,主要集中在前三个月,说明有很多用户购买了一次后就不再进行购买;
随着时间的递增,最后一次购买的客户数也在递增,消费呈现流失上升的状况;
#新老客户的消费占比,多少用户仅消费了一次
#将用户首次消费和最后一次消费的时间求出来
user_life = grouped_user.order_dt.agg({'min','max'})
#求出消费一次的用户数
(user_life['min'] == user_life['max']).value_counts()
下面我们来看重中之重:怎么应用RFM模型对客户进行分层管理运营
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)
#给每条数据新增标签
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':'一般发展客户'
}
result = d[label]
return result
rfm['label'] = rfm[['R','F','M']].apply(lambda x:x-x.mean()).apply(rfm_func,axis = 1)
rfm.loc[rfm.label=='重要价值客户','color'] = 'g'
rfm.loc[~(rfm.label == '重要价值客户'),'color'] = 'r'
plt.style.use('ggplot')
rfm.plot.scatter('F','R',c=rfm.color)
#查看不同组别客户的详细数据
rfm.groupby('label').sum()
从RFM分层可知,大部分用户为重要保持客户,但由于极值的影响,所以RFM的划分标准应该以业务为准:
尽量用小部分的用户覆盖大部分的消费金额,这部分可以作为平台的重点运营客户;
不要为了数据好看而去划分出更多的等级;
#新、活跃、回流、流失/不活跃
#按照用户id和月份对数据进行透视,便于看用户的生命周期
pivoted_counts = df.pivot_table(index = 'user_id',
columns = 'month',
values = 'order_dt',
aggfunc = 'count').fillna(0)
#生命周期只看是否有消费,所以我们将数据优化一下,使其只有0,1
df_purchase = pivoted_counts.applymap(lambda x:1 if x>0 else 0)
#定义各种类型的用户,其实这个未注册是可以省略的
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')
data.iloc[0:]=status
return data
本月没有消费:
若之前是未注册,则依旧为未注册
若之前有消费,则为流失/不活跃
其他情况,是为未注册
本月有消费:
若是第一次消费,则为新用户
如果之前有过消费,上月为不活跃,则为回流
如果之前为未注册,则为新用户
除此之外,为活跃
#将类型函数应用到数据中
purchase_stats = df_purchase.apply(active_status,axis = 1)
#将未注册的用户调整为缺失值,便于对其他类型进行计数
purchase_stats_ct = purchase_stats.replace('unreg',np.NaN).apply(lambda x:pd.value_counts(x))
plt.style.use('ggplot')
purchase_stats_ct.fillna(0).T.plot.area() #制作面积图,可以更直观的观察走势
plt.title('每月用户活动状态')
plt.ylabel('用户人数')
#查看每个月各类用户的占比
purchase_stats_ct.fillna(0).T.apply(lambda x:x/x.sum(),axis = 1)
由上面的图表我们看到,每月的用户消费状态都在变化,且都属于前三个月达到峰值,随后逐渐下降;
活跃用户:持续消费的用户,对应的是消费运营的质量;
回流用户:之前不消费本月才消费,对应的是唤回运营;
不活跃用户:对应的是流失;
#求出每月不同状态用户的个数
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()
plt.title('每月回流用户占比')
plt.ylabel('用户占比')
每月回流用户占比:指的是上个月不活跃的用户在本月重新活跃的用户占比;
从图可知,用户的整体活跃度在不断下降,用户正在流失。
#用户购买周期(按订单)
#计算每个用户的每笔订单的周期
order_diff = grouped_user.apply(lambda x:x.order_dt - x.order_dt.shift())
#购买周期描述
order_diff.describe()
#购买周期分布图
plt.style.use('ggplot')
(order_diff / np.timedelta64(1,'D')).hist(bins = 20)
plt.title('用户购买周期分布')
plt.xlabel('时间天数')
plt.ylabel('频次')
再次证明,只消费一次的用户有很多。
#用户生命周期(按第一次&最后一次消费)
#查看用户生命周期的描述
(user_life['max'] - user_life['min']).describe()
plt.style.use('ggplot')
((user_life['max'] - user_life['min']) / np.timedelta64(1,'D')).hist(bins = 40)
#排除只消费一次的数据
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)
复购率和回购率的分析
复购率:自然月内,购买多次的用户占比;
回购率(类似于留存率):例如本月购买的用户有100个,到下月仍然购买的有30个,那么回购率则为30%
曾经购买过的用户在某一时期内的再次购买占比;
##求复购,消费次数大于1的则说明是复购,其余不是
purchase_r = pivoted_counts.applymap(lambda x:1 if x > 1 else np.NaN if x == 0 else 0)
plt.style.use('ggplot')
(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) #最后这边是因为我们的数据截止到6月,没有7月的数据进行计算,所以定义为空值
data.iloc[0:]=status
return data
本月有消费,下月有消费,为1;
本月有消费,下月无消费,为0;
本月无消费,则为空值。
purchase_b = df_purchase.apply(purchase_back,axis = 1)
plt.style.use('ggplot')
(purchase_b.sum() / purchase_b.count()).plot(figsize = (10,4))
由此可见,回购率一直在30%左右上下波动。