用户消费行为分析项目(python)
数据来源:CDNOW网站用户消费购买记录
通过对消费数据的分析,了解网站总体运营状况,分析用户消费行为,并建立用户分层模型
首先导入工具包和数据
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns
columns = ['user_id','order_dt','order_products','order_amount']
# 用\s+匹配任意空白符
df=pd.read_csv('CDNOW_master.txt',names=columns,sep='\s+')
df.head()
image.png
数据字段:
- user_id 用户ID
- order_dt: 购买日期
- order_products: 购买产品数
- order_amount: 购买金额
数据探索
#看下数据类型
df.info()
image.png
时间类型需进行更改
df['order_dt'] = pd.to_datetime(df.order_dt,format='%Y%m%d')
from datetime import datetime
#df['year']=df['order_dt'].dt.year
#df['month']=df['order_dt'].dt.month
df['week']=df['order_dt'].dt.week
df['weekday']=df['order_dt'].dt.weekday #0-6表示周一到周日
df['ym']=(100*df['order_dt'].dt.year+df['order_dt'].dt.month).astype('str')
df.describe()
image.png
len(df['order_dt'].unique())
len(df['user_id'].unique())
购物总天数有546
消费用户ID合计23570
数据集记录了从1997年1月至1998年6月546天的69659条用户购买CD行为的消费数据
每月消费趋势
# 中文字符和正负号正常显示的设置
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus']=False
fig = plt.figure(figsize=(13,15))
ax1=fig.add_subplot(4,1,1)
ax2=fig.add_subplot(4,1,2)
ax3=fig.add_subplot(4,1,3)
ax4=fig.add_subplot(4,1,4)
sns.barplot(df.groupby(['ym'])['order_amount'].sum().index,df.groupby(['ym'])['order_amount'].sum().values,ax=ax1)
ax1.set_title('每月订单总金额变化趋势')
sns.barplot(df.groupby(['ym'])['order_products'].count().index,df.groupby(['ym'])['order_products'].count().values,ax=ax2)
ax2.set_title('每月总订单数变化趋势')
sns.barplot(df.groupby(['ym'])['order_products'].sum().index,df.groupby(['ym'])['order_products'].sum().values,ax=ax3)
ax3.set_title('每月总订购产品数变化趋势')
sns.barplot(df.groupby(['ym'])['user_id'].apply(lambda x:len(x.drop_duplicates())).index,
df.groupby(['ym'])['user_id'].apply(lambda x:len(x.drop_duplicates())).values,ax=ax4)
ax4.set_title('每月总购买用户数变化趋势')
下载 (1).png
四张图分别是金额,订单数,产品数,用户数by月份变化趋势,可以看到基本趋势是一样的,前三个数据呈增长趋势,并且数据很高,应该是产品导入期。之后进入平稳期,增长趋势较为平稳没有很大变动。
均消费数据
#每月平均消费金额
s=(df.groupby(['ym'])['order_amount'].sum())/
(df.groupby(['ym'])['user_id'].apply(lambda x:len(x.drop_duplicates())))
fig = plt.figure(figsize=(12,4))
sns.lineplot(s.index,s.values)
#用户每月平均消费次数
s=(df.groupby(['ym'])['user_id'].count())/
(df.groupby(['ym'])['user_id'].apply(lambda x:len(x.drop_duplicates())))
fig = plt.figure(figsize=(12,4))
sns.lineplot(s.index,s.values)
image.png
image.png
这是用户人均消费金额和人均消费次数by月份趋势
前三个月均消费和均次数较低,应属于导入增长期
之后人均消费在50元上下波动,人均次数在1.33次上下波动
星期是否对消费有影响
fig = plt.figure(figsize=(10,6))
sns.barplot(df.groupby(['weekday'])['order_amount'].sum().index,
df.groupby(['weekday'])['order_amount'].sum().values)
image.png
很多网站在周末会有消费高峰,可推出针对性促销活动,从图上看,CDNOW网站周一、周日消费金额稍微高点,但并没有特别突出。因此不用再追加分析。
用户个体消费分析
df['user_id'].value_counts().max()
最高的一名用户有217次购物记录
#用户消费金额,消费次数统计
df.groupby(['user_id'])['order_products','order_amount'].sum().describe()
image.png
用户均购买产品7件,消费金额106元。
#每笔消费记录产品和金额散点图
fig = plt.figure(figsize=(10,6))
sns.scatterplot(df['order_products'],df['order_amount'])
image.png
#每个用户消费记录产品和金额散点图
fig = plt.figure(figsize=(10,6))
sns.scatterplot('order_products','order_amount',data=df.groupby(['user_id']).sum())
image.png
消费产品和消费金额呈线性相关
s=df.groupby(['user_id']).sum()
#每个用户购买金额分布
fig = plt.figure(figsize=(10,6))
sns.distplot(s['order_amount'],kde=False)
image.png
#去除极值
fig = plt.figure(figsize=(10,6))
sns.distplot(s.query('order_amount<500')['order_amount'],kde=False)
image.png
可以看到消费10-20元的用户最对,然后依次是20-30元,30-40元
用户消费行为分析
首次购买时间
s=df.groupby(['user_id'])['order_dt'].min()
fig=plt.figure(figsize=(10,5))
sns.lineplot(s.value_counts().index,s.value_counts().values)
image.png
最后一次购买时间
s=df.groupby(['user_id'])['order_dt'].max()
fig=plt.figure(figsize=(10,5))
sns.lineplot(s.value_counts().index,s.value_counts().values)
image.png
新用户集中在前三个月,同时流失的用户也很多,说明很多用户只消费了一次,之后用户呈现缓慢流失的趋势。
oneshoptime=df.groupby(['user_id']).count().query('order_dt==1')['order_dt'].count()
oneshoptime/len(df['user_id'].unique())
结果:约50%用户只购买一次。
新用户占比
新用户/老用户 判断是否为新用户:用户第一次购买日期记录是否是用当月用户第一次购买日期记录
grouped_month = df.groupby(['ym','user_id'])
grouped_user = df.groupby(['user_id'])
judge_df = grouped_month['order_dt'].agg(['min']).join(grouped_user['order_dt'].min()) #联结
judge_df['judge']=(judge_df['min']==judge_df['order_dt']) #判断当月第一次是否为总第一次购买
judge_df.head()
image.png
#计算新用户占比
new_old_rate =judge_df.reset_index().groupby('ym')['judge'].apply(lambda x:x.sum()/x.count())
fig=plt.figure(figsize=(12,5))
sns.lineplot(new_old_rate.index,new_old_rate.values)
image.png
可以看出前三个月之后,新用户占比降为0
用户分层
RFM模型
#RFM模型
# 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.head()
image.png
最近一次消费 (Recency)
消费频率 (Frequency)
消费金额 (Monetary)
# 计算每位用户最后一次消费时间与全部用户最后一次消费时间的差值
rfm['R'] = (rfm['order_dt'].max()-rfm['order_dt']).astype('timedelta64[D]')
rfm.rename(columns={'order_products':'F','order_amount':'M'},inplace=True)
rfm.head()
image.png
rfm[['R','F','M']].apply(lambda x:x-x.mean()).head()
image.png
# 客户层次的定义,RFM得分可根据业务定义打分,也可以通过K-means聚类模型,得出不同相似程度的数据集,并且根据每一个数据集的特点进行客户定义
def rfm_func(x):
level = x.apply(lambda x:'1' if x>= 0 else '0')
'''
字符串拼接
111,R>0,是距离平均消费时间要久,R越大,说明没有消费时间越久,
F>0 M>0,消费次数和金额也是较高的,重要价值客户,依次类推
'''
label=level['R']+level['F']+level['M']
d={
'111':'重要价值用户',
'011':'重要保持客户',
'101':'重要挽留客户',
'001':'重要发展客户',
'110':'一般价值客户',
'010':'一般保持客户',
'100':'一般挽留客户',
'000':'一般发展客户'
}
result=d[label]
return result
# x - x.mean() (具体真实情况可以修改,不一定需要用均值) 切比雪夫也可以 > 200 极值人工处理掉
rfm['label'] = rfm[['R','F','M']].apply(lambda x:x-x.mean()).apply(rfm_func,axis=1)
rfm.head()
image.png
# 计算每层客户R、F、M的和
rfm.groupby('label').sum()
image.png
可以看出,重要保持客户对于消费总金额的占比远大于其他客户的占比,这说明绝大部分收益是由重要保持客户贡献的,只要能保证这部分客户不流失和增加,那么公司收益将得到有力保障
s=rfm.groupby('label')['M'].count()
fig =plt.figure(figsize=(10,10))
plt.pie(s.values,labels=s.index,autopct='%1.1f%%')
image.png
一般挽留用户: 59.7%的用户占比 最近有购买,但消费频率和消费金额不高
措施:采取刺激消费措施,如优惠券和大促
重要保持客户: 19.3%用户占比同时很高的总消费金额 最近无购买,但消费频率和消费金额高
分析:可以回访需求,存在极值影响
用户状态分析
pivot_counts=df.pivot_table(index = 'user_id',
columns='ym',
values='order_dt',
aggfunc='count').fillna(0)
pivot_counts.head()
image.png
#有消费记录1,无消费记录改为0
data_purchase = pivot_counts.applymap(lambda x:1 if x>0 else 0)
data_purchase.head()
image.png
将用户状态分为unreg(未注册)、new(新客)、active(活跃用户)return(回流用户)和unactive(不活跃用户)
思路:
- 若本月没有消费
- 若之前是未注册,则依旧为未注册
- 若之前有消费,则为流失/不活跃
- 其他情况,为未注册
- 若本月有消费
- 若是第一次消费,则为新用户
- 若之前有过消费,则上个月为不活跃,则为回流
- 若上个月为未注册,则为新用户
- 除此之外,为活跃
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:
if status[i-1]=='unreg':
status.append('new')
elif status[i-1]=='unactive':
status.append('return')
else:
status.append('active') #其他情况是活跃
else:
status.append('new')
return pd.Series(status, index = pivot_counts.columns) #将列表转为Series
purchase_status = data_purchase.apply(active_status,axis=1)
purchase_status.head()
image.png
purchase_status_sta_T =purchase_status_sta.fillna(0).T
#作用户状态分层面积图
purchase_status_sta_T.plot.area(figsize=(10,6))
image.png
可以直观看到每个月的用户状态变化,以采取拉新,促活,召回等方案。
用户购买周期
order_diff = grouped_user.apply(lambda x:x['order_dt']-x['order_dt'].shift())
order_diff.head(10)
image.png
order_diff.describe()
image.png
#作用户消费周期 跟上次购物时间差距比
fig = plt.figure(figsize=(10,6))
sns.distplot(order_diff.astype('timedelta64[D]').dropna(),bins=20,kde=False)
plt.title('用户消费周期')
image.png
同上次购买日期时间差,说明随着时间拉长,越来越不可能再次来回购
#usertime_diff代表用户消费首次订单与最后一次订单间隔时间
usertime_diff=df['order_dt'].groupby(df['user_id']).max()-df['order_dt'].groupby(df['user_id']).min()
usertime_diff=usertime_diff.reset_index()
usertime_diff=usertime_diff['order_dt'].astype('timedelta64[D]')
#周期分布 同首次消费日期对比
fig = plt.figure(figsize=(10,6))
sns.distplot(usertime_diff[usertime_diff>0],bins=100,kde=False)
image.png
同首次消费时间差比对,出现两个高峰,一个是第一次消费后会容易第二次消费,但时间推移在下降。第二次高峰在一年的时间段,从业务上解释,应该是每次一次新专售卖。
复购率和回购率分析
复购率:自然月内,购买多次的用户占比
回购率:曾今购买过的用户在某一时期内的再次购买占比
#透视表,每月购买次数统计
pivot_counts = df.pivot_table(index = 'user_id',
columns='ym',
values='order_dt',
aggfunc='count').fillna(0)
pivot_counts.head()
image.png
#购买大于1次的赋值1,等于1次的赋值0,小于1次的赋值nan
rebuy_counts = pivot_counts.applymap(lambda x:1 if x>1 else np.NaN if x==0 else 0)
rebuy_counts.head()
image.png
#计算复购率
rebuy_rate=rebuy_counts.sum()/rebuy_counts.count()
fig = plt.figure(figsize=(12,4))
sns.lineplot(rebuy_rate.index,rebuy_rate.values)
plt.title('复购率')
image.png
前三个月大量新用户,复购率较低,hi后复购率稳定在20%至22%之间。
#data_purchsey消费过为1,未消费过为0
data_purchase.head()
def purchase_back(data):
status = []
for i in range(17):
if data[i] == 1: # 本月进行过消费
if data[i+1] == 1: # 下一月是否进行消费
status.append(1) #消费为1 回购了
if data[i+1] == 0:
status.append(0) # 未消费则为0 没有回购
else:
status.append(np.NaN) # 之前没消费则不计
status.append(np.NaN) # 最后一个月没有判断需要补上
return pd.Series(status,data_purchase.columns)
#对透视表应用函数purchase_back:
return_counts = data_purchase.apply(purchase_back, axis =1)
return_counts.head()
image.png
#计算回购率
return_rate=return_counts.sum()/return_counts.count()
fig = plt.figure(figsize=(12,4))
sns.lineplot(return_rate.index,return_rate.values)
plt.title('回购率')
image.png
前三个月因为有大量的新用户涌入,但是超过一半的人只消费了一次,所以前三个月回购率比较低,后续月份用户人数比较稳定,回购率也比较稳定,稳定在30%左右,即当月消费人数中有30%左右的用户会在下一个月再次消费
本文探索了用户在cdnow电商网站的消费记录,包括
- 总体消费情况(周/月),如金额,次数,平均金额,平均次数
- 用户消费情况,用户均消费金额,均消费次数,及其分布
- 消费行为分析,首次购买时间,最后一次购买时间,新老用户占比
- 用户分层RFM模型的建立
- 用户分层状态分析如活跃会员占比
- 用户购买周期分析
- 复购率和回购率分析