数据来源于CDNOW网站的用户购买记录,包括用户ID,购买日期,购买产品数,购买金额等四项基本数据,以下通过Python对用户消费行为进行分析
数据来源:链接:https://pan.baidu.com/s/1P01mMF9PM6B1P7M1VhWGdg 提取码:2weg
阅读路线:
0、数据准备
1、对用户消费趋势的分析(按月)
2、用户的个体消费分析
3、消费行为分析
4、复购率和回购率分析
0、数据准备
导入常用数据库:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns
plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False #用来正常显示负号
导入数据
columns=['user_id','order_dt','order_products','order_amount']
df=pd.read_table('CDNOW_master.txt',names=columns,sep='\s+')
- user_id:用户ID
- order_dt:购买日期
- order_products:购买产品数
- order_amount:购买金额
该数据为txt文件,通过read_csv读取数据,‘\s+'表示匹配任意空格符。
导入数据后检查数据的完整性
df.head()
df.info()
数据中无缺失值,无需对缺失值处理,但是购买日期的数据类型是int,为方便后面数据处理,在这先将其转化成datetime格式。
df.order_dt=pd.to_datetime(df.order_dt,format='%Y%m%d')
数据预处理好后,先看下数据的基本情况:
df.describe()
- 用户平均每笔订单的购买数是2.4,中位数为2,数据右偏;此外,购买数量最大值达到了99,说明数据存在一定的极值干扰;
2.用户每笔订单的购买金额也存在同样的情况,平均消费35.89元,中位数25.98元,消费最大金额达到了1286元。
1、对用户消费趋势进行分析(按月)
对用户消费趋势总共分成以下四个部分进行:
- 每月的消费频次
- 每月的产品购买量
- 每月消费的总金额
- 每月的消费人数
month_grouped = df.resample('m').agg({'user_id':'count',
'order_products':'sum',
'order_amount':'sum'})
month_grouped['user_sum'] = df.resample('m')['user_id'].nunique()
通过resample函数对数据进行按月分组,分别求出每月的消费频次,购买量、每月消费的总金额以及每月的消费人数:
对每项数据做直方图:
import pylab
%pylab inline
pylab.rcParams['figure.figsize']=(16,20)
fig,axes = plt.subplots(4,1)
axes0,axes1,axes2,axes3 = axes.flatten()
axes0.bar(month_grouped.index,month_grouped.user_id,width=20)
axes0.set_title('每月的消费频次')
axes1.bar(month_grouped.index,month_grouped.order_products,width=20)
axes1.set_title('每月的产品购买量')
axes2.bar(month_grouped.index,month_grouped.order_amount,width=20)
axes2.set_title('每月消费的总金额')
axes3.bar(month_grouped.index,month_grouped.user_sum,width=20)
axes3.set_title('每月的消费人数')
对user_id进行去重处理,再统计每月的消费人数:
df.drop_duplicates('user_id')['user_id'].resample('m').count()
通过以上数据发现:
- 消费人群集中在前三月,从1997年4月开始,订单量、产品购买量、购买金额、消费用户数量基本处于平稳;
- 前三个月每月的消费人数在8000-10000之间,后续月份平均消费人数在2000人不到,说明用户的粘性不足;
- 新用户的购买行为主要集中在前三月,后面的消费主要是由前三月用户的回购行为产生。
每月用户平均消费金额分析:
pylab.rcParams['figure.figsize']=(16,6)
user_avgamount = month_grouped['order_amount']/month_grouped['user_sum']
plt.bar(user_avgamount.index,user_avgamount,width=20)
- 用户每月的平均消费水平比较稳定,波动较小,大部分集中在38-60之间。
每月用户平均消费次数分析:
user_avgorder = month_grouped['order_products']/month_grouped['user_sum']
plt.bar(user_avgorder.index,user_avgamount,width=20)
plt.title('用户每月的平均消费次数',size=20)
- 用户每月的平均消费次数也较为平稳,基本集中在2.5-3.5之间。
2、用户的个体消费分析
分为以下几个方面分析:
- 用户消费金额、消费次数的描述统计
- 用户消费金额和消费次数的相关性
- 用户消费金额的分布图
- 用户产品购买量的分布图
- 用户累计消费金额占比(百分之多少的用户占了百分多少的消费额)
2.1 用户消费金额、消费次数的描述统计:
user_grouped = df.groupby('user_id').agg({'user_id':'count',
'order_products':'sum',
'order_amount':'sum'})
user_grouped.describe()
- 用户平均消费次数是2.95次,四分位数和中位数的值均为1,说明大部分用户只在该网站上进行了一次消费,直方图右偏;
- 商品购买量和购买金额的平均值均等于其3/4分位数,说明少量的用户购买了大量的产品,直方图右偏。
2.2绘制用户消费金额和消费次数的散点图:
user_grouped.plot.scatter(x='order_products',y='order_amount')
大部分数据都集中在在左下角区域,数据最后两个极值拉大了图形区域,通过query函数过滤极值:
- 订单总数和消费金额承明显的线性关系,说明CD产品单一,单价稳定。
2.3 用户消费金额的分布图:
pylab.rcParams['figure.figsize']=(16,9)
user_grouped['order_amount'].hist(bins=30)
大部分购买金额集中在0-2500之间,由于一些极值的影响使得数据分布收到了干扰。
根据切比雪夫定理,距离平均值有三个标准差的值均为异常值,根据2.1的描述值,用户的消费金额应该在106+241*3=829的范围内
user_grouped.query('order_amount<829')['order_amount'].hist(bins=30)
2.4 用户产品购买量的分布图
同样的,使用切比雪夫定理过滤掉异常值:
user_grouped.query('order_products<58.12')['order_products'].hist(bins=30)
2.5 用户累计消费金额占比
user_cumsum = user_grouped.sort_values('order_amount').apply(lambda x : x.cumsum()/x.sum())
user_cumsum.reset_index(drop=True).order_amount.plot()
plt.grid()
- 按用户消费金额进行升序排列,50%的用户仅贡献除了15% 的消费额度,而消费金额排名前5000的用户贡献了60%的消费额度。
3、消费行为分析
主要分为以下几个方面分析:
- 用户第一次消费(首购)
- 用户最后一次消费
- 新老客户消费比
- 多少用户仅消费了一次?
- 每月新客占比?
- 用户分层
- RFM
- 新、老、活跃、回流、流失
- 用户购买周期(按订单)
- 用户消费周期描述
- 用户消费周期分布
- 用户生命周期(按第一次&最后一次消费)
- 用户生命周期描述
- 用户生命周期分布
3.1 用户第一次消费(首购)
提取用户第一次消费的日期并作图:
df.groupby('user_id')['order_dt'].min().value_counts().plot()
- 从图中可以发现,用户的消费行为基本上集中在前三月份,1997年4月份后也几乎无新用户产生;
- 新增用户在1997.2.11到1997.2.25间有巨大波动。
3.2 用户最后一次消费
df.groupby('user_id')['order_dt'].max().value_counts().plot()
- 最后一次购买基本集中在前三月,说明很多用户在前三月进行一次购买后不再回购;
3.3 新老客户消费比
求出用户购买产品日期的最大值和最小值,若该日期的最大值等于其最小值,说明这个用户只消费了一次。
user_dt = df.groupby('user_id').order_dt.agg(['min','max'])
pylab.rcParams['figure.figsize']=(8,8)
rate = (user_dt['min'] == user_dt['max']).value_counts()
labels = ['只消费一次用户','多次消费用户']
patches,l_text,p_text = plt.pie(rate,labels=labels,
explode=(0,0.15),
autopct='%2.1f%%',
startangle=90,
)
for t in l_text: #调整标签字体大小
t.set_size(15)
for t in p_text: #调整百分数字体大小
t.set_size(15)
- 有51.1%的用户仅消费过一次,老用户所占比例为48.9%。
对用户按月分组,计算新用户人数占总消费人数的比例:
pylab.rcParams['figure.figsize']=(10,6)
user_new = df.drop_duplicates('user_id')['user_id'].resample('m').count()#计算每月首次购买产品的用户数
user_sum = df.resample('m')['user_id'].nunique()#计算每月购买产品的用户总数
(user_new/user_sum).fillna(0).plot()
- 新客消费主要集中在前三月,后面消费群体全部为老客户。
3.4 用户分层
3.4.1 RFM分层
RFM模型,通过对用户在R(Recency,最近一次消费)F(Frequency,消费频率)M(Monetary,消费金额)三方面的表现进行分类,然后对分类分组进行定性描述的,分层模型如下:
先对数据做预处理,让每个日期减去最大日期获得间隔天数(该数为负数),当数值越大时,说明日期越近。
df.reset_index(inplace=True)
df['period']=(df.order_dt - df.order_dt.max())/np.timedelta64(1,'D')#计算时间差,并转换为float类型
使用数据透视表功能,求出各用户的最近消费时间(间隔天数),消费频次,消费总金额。
user_rfm = df.pivot_table(values=['period','order_products','order_amount'],
index='user_id',aggfunc={'period':'max',
'order_products':'count',
'order_amount':'sum' })
user_rfm = user_rfm.rename(columns = {'order_amount':'M','order_products':'F','period':'R'})
将用户在R、F、M
三个维度上按照高于平均值和低于平均值进行划分(划分标准可根据不同业务设计也不同),高于平均值则赋值为1,低于平均值则赋值为0,最后根据RFM分层模型给所有用户分层:
def level_label(data):#定义分层函数
level = data.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
user_rfm['label'] = (user_rfm - user_rfm.mean()).apply(level_label,axis=1)
对用户分层结果计数:
作饼状图:
label_count = user_rfm.groupby('label').count()
pylab.rcParams['figure.figsize']=(10,10)
labels = ['一般价值客户','一般保持客户','一般发展客户','潜在客户','重点保持客户','重点发展客户','重点挽留客户','高价值客户']
plt.pie(label_count['M'],labels=labels,autopct = '%3.1f%%',startangle = 90)
- 从用户分层结果可知,不活跃用户占了较大的比重,为57.7%;高价值用户占比第二,为19.6%,这也是该网站的消费主力,需重点保持;重点挽留客户、重点发展客户、重点保持客户占比较低,分别为2.5%,1.1%,1.9%。
3.4.2 用户状态分析:注册、活跃、回流、流失(不活跃)统计
建立数据透视表,以user_id为横坐标,m月份为纵坐标,统计用户每月的消费情况:
pivoted_count = df.pivot_table(index = 'user_id',
columns='M',
values = 'order_products',
aggfunc={'order_products':'count'})
pivoted_count = pivoted_count.fillna(0)
- 数字表示用户在该月的消费次数。
进行多重判断,将用户状态分为unreg(未注册)、new(新客)、active(活跃用户)return(回流用户)和unactive(不活跃用户):
def status(data):
state = []
lenth = len(data)
for i in range(18):
if data[i]>0:
if len(state) == 0:
state.append('new')
else:
if state[i-1] == 'unreg':
state.append('new')
elif state[i-1] == 'unactive':
state.append('return')
else:
state.append('active')
else:
if len(state) == 0:
state.append('unreg')
else:
if state[i-1] == 'unreg':
state.append('unreg')
else:
state.append('unactive')
return state
pivoted_count.apply(status,axis=1)
- 活跃用户是持续消费的用户,对应的是消费运营的质量;
- 回流用户,之前不消费,本月才消费,对应的是唤回运营;
- 不活跃用户,对应的是流失。
不同用户的计数统计,并做面积图:
pylab.rcParams['figure.figsize'] = (10,6)
pivoted_count.replace('unreg',np.NaN).apply(lambda x:pd.value_counts(x)).T.fillna(0).plot.area()
- 前三月份用户人数不断增高,新增用户数量和活跃用户数量都占较大比例,但后续无新用户注册,活跃用户数量下降最后趋于稳定水平,同时也有稳定的回流用户。
3.5 用户生命周期(按第一次&最后一次消费)
按用户进行分组,计算用户本次购买日期和下一次购买日期的时间间隔,并统计:
user_period = df.groupby('user_id').apply(lambda x:x.order_dt-x.order_dt.shift())
user_period.describe()
- 用户的平均购买周期是68天,但是其标准差较大,且中位数为31天远小于其平均数。购买周期最大值为533天,说明用户的购买周期波动较大。
对该数据做分布图:
(user_period/np.timedelta64(1,'D')).hist(bins=20)
plt.title('用户购买周期分布图')
- 用户的购买周期呈指数分布,大部分分布在0-100天内
用户的生命周期分布:
user_cycle = df.groupby('user_id').apply(lambda x:x.order_dt.max()-x.order_dt.min())
user_cycle.describe()
- 用户的平均生命周期为134天,但中位数为0天,说明大部分用户购买了一次之后就再未消费。
对生命周期做直方图,过滤掉生命周期为0的用户:
user_cycle = user_cycle/timedelta64(1,"D")
user_cycle[user_cycle>0].plot.hist(bins=20)
- 部分质量差的用户,虽然消费了两次,但是仍旧无法持续,在用户首次消费30天内应该尽量引导。少部分用户集中在50天~300天,属于普通型的生命周期,高质量用户的生命周期,集中在400天以后,属于忠诚用户。
4、复购率和回购率分析
- 复购率:自然月内,购买多次的用户占比
- 回购率:曾经购买过的用户在某一时期内再次购买的占比
4.1 复购率计算
以月为单位,对3.4.2统计的每月用户消费情况进行预处理:
若消费次数大于1,则说明用户在本月进行了多次消费,对多次消费的情况取值为1;
若消费次数等于1,说明用户在本月只进行了1次消费,每月复购行为,取值为0;
若消费次数等于0,则说明用户在本月未消费。
pivoted_count = df.pivot_table(index = 'user_id',
columns='M',
values = 'order_products',
aggfunc={'order_products':'count'})
pivoted_count = pivoted_count.fillna(0)
df_purchase = pivoted_count.applymap(lambda x:1 if x>1 else np.nan if x==0 else 0)#清洗数据,消费超过两次为1,消费过1次为0,没有消费为nan
计算每个月的复购率,多次消费人数/总消费人数,并对其做柱状图。
df_purchase.apply(lambda x:x.sum()/x.count()).plot.bar(
- 复购率基本上稳定在20%左右,前三个月因为有大量新用户涌入,且大部分只购买了一次,导致复购率偏低。后续月份用户消费次数大于两次的占比稳定在20%上下。
4.1 回购率计算
计算用户的回购率主要以本月与上月的进行对比,假如上月进行过消费,本月再次消费了,说明该用户是回购用户。
对3.4.2统计的每月用户消费情况进行预处理:
若消费次数大于0说明在本月已消费,重新赋值为1;
若消费次数为0说明在本月未消费,重新赋值为np.nan;
df_purchase_back = pivoted_count.applymap(lambda x:1 if x>0 else np.nan)
df_purchase_back.head()
编写筛选函数:
若本月未消费,则全部置np.nan;
若本月已消费,下月未消费置0;
若本月已消费,下月也消费,置1(回购用户)
def purchase_back(data):
lenth = len(data)
state = []
for i in range(0,lenth-1):
if data[i] == 1:
if data[i+1]==1:
state.append(1) #若本月已消费,下个月也消费置1
else:
state.append(0) #本月已消费,下月未消费置0
else:
state.append(np.nan) #本月未消费置np.nan
state.append(np.nan)
return state
计算用户的回购率(本月用户回购人数/本月消费总人数),并做柱状图:
df_purchase_b = df_purchase_back.apply(purchase_back,axis=1)
df_purchase_b.apply(lambda x:x.sum()/x.count()).plot.bar()
- 末月不存在回购行为,因此其回购率为0;
- 前三个月由于有大量新用户涌入,大部分人只消费过一次,所以两个月回购率偏低;
- 第四个月回购率回升,最后稳定在35%左右,即当月消费人数中有30%左右的用户会在下一个月再次消费。