Teqing Cai

Value Investing | Data Science


  • 首页

  • 笔记

  • 归档

  • 搜索
close

用户消费行为分析

时间: 2022-01-08   |   阅读: 3753 字 ~8分钟

目标:针对用户消费行为进行分析,获取一定的数据理解
数据:产品订单数据,含user_id,order_dt,order_products,order_amount,month五个字段
实现方式:数据可视化,描述性分析,指标分析,RFM模型


import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from datetime import datetime
import warnings
warnings.filterwarnings('ignore')

%matplotlib inline
plt.style.use('ggplot') # 更改绘图风格,R语言绘图库的风格
plt.rcParams['font.sans-serif']='SimHei'  ##设置中文显示
导入数据 数据预处理
df = pd.read_table('data\CDNOW_master.txt',sep='\s+') #sep: '\s+'以任意空格作为不同属性的分隔符
df.columns=['user_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()
#df.info()
user_id order_dt order_products order_amount month
0 2 1997-01-12 1 12.00 1997-01-01
1 2 1997-01-12 5 77.00 1997-01-01
2 3 1997-01-02 2 20.76 1997-01-01
3 3 1997-03-30 2 20.76 1997-03-01
4 3 1997-04-02 2 19.54 1997-04-01
数据分析之用户整体消费分析(按月)
##每月产品购买数量
plt.figure(figsize=(12,5))
plt.subplot(221)
df.groupby(by='month')['order_products'].sum().plot()
plt.title('每月产品购买数量')
##每月的消费金额
plt.subplot(222)
df.groupby(by='month')['order_amount'].sum().plot()
plt.title('每月的消费金额')
##每月的消费次数
plt.subplot(223)
df.groupby(by='month')['user_id'].count().plot()
plt.title('每月的消费次数')
##每月的消费人数
plt.subplot(224)
df.groupby(by='month')['user_id'].apply(lambda x:len(x.drop_duplicates())).plot()
plt.title('每月的消费人数')
Text(0.5, 1.0, '每月的消费人数')
png
png
数据分析之用户个体消费分析
#df['u_id']=int(df['user_id'])
user_grouped=df.groupby(by='user_id').sum()
print(user_grouped.describe())
       order_products  order_amount
count    23569.000000  23569.000000
mean         7.122916    106.084427
std         16.983845    240.929523
min          1.000000      0.000000
25%          1.000000     19.970000
50%          3.000000     43.410000
75%          7.000000    106.490000
max       1033.000000  13990.930000
#绘制每个用户购买产品数与消费金额的散点图
df.plot(kind='scatter',x='order_products',y='order_amount')
# 订单极值点较少(消费金额>1000或购买量>60),对于样本来说影响不大,可以忽略不记
# 用户消费金额与购买量呈线性相关,均价约为15
<AxesSubplot: xlabel='order_products', ylabel='order_amount'>
png
png
用户消费分布图
plt.figure(figsize=(12,4))
plt.subplot(121)
plt.xlabel('每个订单的消费金额')
df['order_amount'].plot(kind='hist',bins=20) #bins:区间分数,影响柱子宽度---宽度=(列最大值-列最小值)/bins
# 消费金额在100以内占据绝大部分

plt.subplot(122)
plt.xlabel('每个uid的购买数量')
df.groupby(by='user_id')['order_products'].sum().plot(kind='hist',bins=50)
# 由两图可知我们的大部分用户消费金额低,且购买数量小于50(在电商领域是普遍现象)
<AxesSubplot: xlabel='每个uid的购买数量', ylabel='Frequency'>
png
png
用户累计消费金额占比分析(用户贡献度)
user_cumsum=df.groupby(by='user_id')['order_amount'].sum().sort_values().reset_index() # 用户分组,取消费金额,进行求和,排序,重置索引
user_cumsum 
user_cumsum['amount_cumsum']=user_cumsum['order_amount'].cumsum()
user_cumsum.tail()

amount_total=user_cumsum['amount_cumsum'].max() #消费金额总值
user_cumsum['prop']= user_cumsum.apply(lambda x:x['amount_cumsum']/amount_total,axis=1) #axis=1:对列apply函数
user_cumsum.tail()
user_id order_amount amount_cumsum prop
23564 7931 6497.18 2463810.83 0.985405
23565 19339 6552.70 2470363.53 0.988025
23566 7983 6973.07 2477336.60 0.990814
23567 14048 8976.33 2486312.93 0.994404
23568 7592 13990.93 2500303.86 1.000000
user_cumsum['prop'].plot()
# 由图可知前2w名用户贡献40%的消费金额,后3k+名用户贡献60%的消费金额(2/8原则)
<AxesSubplot: >
png
png
plt.figure(figsize=(12,4))
plt.subplot(121)
df.groupby(by='user_id')['order_dt'].min().value_counts().plot() #用户首购时间
# 由图一可知,首购的用户量在1月1号~2月10号呈上升趋势,后续逐渐下降,可能跟商家促销活动有关
plt.subplot(122)
df.groupby(by='user_id')['order_dt'].max().value_counts().plot() #用户最后一次购买时间
# 由图二可知,大多数用户最后一次购买时间集中在前3个月,说明缺少忠诚用户
# 随着时间的推移,最后一次购买商品的用户量呈上升趋势,猜测:这份数据选择的是前3个月的消费 用户在后面18个月的跟踪记录
<AxesSubplot: >
png
png
用户分层分析
构建RFM模型
# 指标:距离最近时间 (Recency),消费频率次数 (Frequency),消费金额 (Monetary)
rfm= df.pivot_table(index='user_id',
                   values=['order_products','order_amount','order_dt'],
                   aggfunc={
                   'order_dt':'max', #最后一次购买
                   'order_products':'sum', #购买产品总数
                   'order_amount':'sum' # 消费总金额
                   })
rfm['R']=(rfm['order_dt'].max()-rfm['order_dt'])/np.timedelta64(1,'D') #取相差天数,保留一位小数
rfm.rename(columns={'order_products':'F','order_amount':'M'},inplace=True)
# RMF计算方式:每一列对应值减去所在列的均值,若结果>0,设置为1,否则为0
def rfm_func(x):
    level=x.apply(lambda x:'1' if x>=0 else '0')
    label=level['R']+level['F']+level['M']
    d={
        '111':'重要价值客户',
        '101':'重要发展客户',
        '001':'重要挽留客户',
        '011':'重要保持客户',
        '110':'一般价值客户',
        '100':'一般发展客户',
        '000':'一般挽留客户',
        '010':'一般保持客户',
    }
    result =d[label]
    return result
    
rfm['label']=rfm[['R','F','M']].apply(lambda x:x-x.mean()).apply(rfm_func,axis=1)  #根据业务需求看是用平均值还是中位数啥的
rfm.head() 
M order_dt F R label
user_id
2 89.00 1997-01-12 6 534.0 一般发展客户
3 156.46 1998-05-28 16 33.0 重要保持客户
4 100.50 1997-12-12 7 200.0 一般挽留客户
5 385.61 1998-01-03 29 178.0 重要保持客户
6 20.99 1997-01-01 1 545.0 一般发展客户
客户分层之RMF可视化
for label,grouped in rfm.groupby(by='label'):
    #print(label,grouped)
    x=grouped['F'] # 消费频率次数 单个用户购买的数量
    y=grouped['R'] # 最后一次购买时间与最近:df['order_dt'].max()的距离
    plt.scatter(x,y,label=label)
plt.legend() #显示图例
plt.xlabel('F')
plt.ylabel('R')
Text(0, 0.5, 'R')
png
png
新老 活跃 回流用户分析
# 通过透视表得到想要的数据
pivoted_counts = df.pivot_table(
                index='user_id',
                columns='month',
                values='order_dt',
                aggfunc='count'
).fillna(0)
# pivoted_counts

# 由于浮点数不直观,只需转化成是否消费即可,用0,1表示
df_purchase=pivoted_counts.applymap(lambda x:1 if x>0 else 0)
# apply 作用于df中的一行或一列
# applymap 作用于df中每一个元素
# map 本身是一个series函数,无法在df结构中使用,作用于series中每一个元素
df_purchase.head()
month 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
user_id
2 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
3 1 0 1 1 0 0 0 0 0 0 1 0 0 0 0 0 1 0
4 1 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0
5 1 1 0 1 1 1 1 0 1 0 0 1 1 0 0 0 0 0
6 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
判断是否是为用户 活跃用户 不活跃用户 回流用户
# 自定义函数 实现以上的判断
def active_status(data): #data :整行数据,共18列
    status =[] #负责存储18个月的状态;unreg/new/active/unactive/return
    for i in range(18):
        #本月没有消费
        if data[i]==0:
            if len(status)==0: # 前面没有任何消费记录
                status.append('unreg')
            else: #开始判断上一个月消费状态
                if status[i-1]=='unreg': #一直未消费
                    status.append('unreg')
                else: #/new/active/unactive/return
                    status.append('unactive') # 不管上一个月是否消费过,本月都是不活跃用户
            pass
        #本月有消费==1
        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: #new/active/return=1
                    status.append('active')
    return pd.Series(status,df_purchase.columns) # 值status ,列名df_purchase中的列名

#apply自定义函数 得到用户分层结果
purchase_states=df_purchase.apply(active_status,axis=1) 
purchase_states.head()      
month 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
user_id
2 new unactive unactive unactive unactive unactive unactive unactive unactive unactive unactive unactive unactive unactive unactive unactive unactive unactive
3 new unactive return active unactive unactive unactive unactive unactive unactive return unactive unactive unactive unactive unactive return unactive
4 new unactive unactive unactive unactive unactive unactive return unactive unactive unactive return unactive unactive unactive unactive unactive unactive
5 new active unactive return active active active unactive return unactive unactive return active unactive unactive unactive unactive unactive
6 new unactive unactive unactive unactive unactive unactive unactive unactive unactive unactive unactive unactive unactive unactive unactive unactive unactive
# 统计每个月各种状态用户的数量
purchase_states_ct=purchase_states.replace('unreg',np.NaN).apply(lambda x: pd.value_counts(x))  #用NaN替换unreg 未注册用户
purchase_states_ct.head(60)
month 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
active NaN 1157.0 1681 1773.0 852.0 747.0 746.0 604.0 528.0 532.0 624.0 632.0 512.0 472.0 571.0 518.0 459.0 446.0
new 7845.0 8476.0 7248 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
return NaN NaN 595 1049.0 1362.0 1592.0 1434.0 1168.0 1211.0 1307.0 1404.0 1232.0 1025.0 1079.0 1489.0 919.0 1029.0 1060.0
unactive NaN 6688.0 14045 20747.0 21355.0 21230.0 21389.0 21797.0 21830.0 21730.0 21541.0 21705.0 22032.0 22018.0 21509.0 22132.0 22081.0 22063.0
purchase_states_ct.T.fillna(0).plot.area() # 行列变换
# 前3个月可知,红色和蓝色用户占比大  前3个月活跃用户与新用户比较多
<AxesSubplot: xlabel='month'>
png
png
回流用户的占比
rate=purchase_states_ct.T.fillna(0).apply(lambda x: x/x.sum(),axis=1)
plt.plot(rate['return'],label='return')
plt.plot(rate['active'],label='active')
plt.legend()
<matplotlib.legend.Legend at 0x1d55c72fe90>
png
png
用户消费周期与生命周期
order_diff=df.groupby('user_id').apply(lambda x: x['order_dt']-x['order_dt'].shift(axis=0)) #默认axis=0 按行遍历 向下
order_diff.head()
(order_diff/np.timedelta64(1,'D')).hist(bins=20)
<AxesSubplot: >
png
png
# 计算用户生命周期 最后一次购买日期-第一次购买日期,若两者相等,说明该用户仅购买一次
user_life=df.groupby('user_id')['order_dt'].agg(['max','min'])
(user_life['max']==user_life['min']).value_counts().plot.pie(autopct='%1.1f%%') #格式化成1位小数
plt.legend(['仅消费一次','消费多次'])
# 由图可知,一半以上的用户仅仅消费一次
<matplotlib.legend.Legend at 0x1d55c727d50>
png
png
(user_life['max']-user_life['min']).describe() #对消费用户生命周期描述性分析
count                          23569
mean     134 days 21:03:51.405659978
std      180 days 13:49:09.372703982
min                  0 days 00:00:00
25%                  0 days 00:00:00
50%                  0 days 00:00:00
75%                294 days 00:00:00
max                544 days 00:00:00
dtype: object
plt.figure(figsize=(12,4))
plt.subplot(121)
((user_life['max']-user_life['min'])/np.timedelta64(1,'D')).hist(bins=15)
plt.title('所有用户生命周期直方图')
plt.xlabel('生命周期天数')
plt.ylabel('用户人数')

#消费多次用户生命周期
plt.subplot(122)
u_1=(user_life['max']-user_life['min']).reset_index()[0]/np.timedelta64(1,'D') #[0]刚刚好是重置索引后需要字段的列名
u_1[u_1>0].hist(bins=15)
plt.title('多次消费用户生命周期直方图')
plt.xlabel('生命周期天数')
plt.ylabel('用户人数')
# 由以下两图可知,第二幅图过滤掉了生命周期==0的用户,呈现双峰结构
# 虽然图二还有一部分生命周期趋于0天,但比图一情况好了很多,对这部分可以针对性进行营销活动,以拉长其生命周期
# 少部分用户生命周期集中在300~500天,属于我们的忠诚客户,
Text(0, 0.5, '用户人数')
png
png
复购率分析
# 在自然月内,购买多次的用户在总消费人数中的占比(若同一客户在一天内多次复购,也属于复购)
# 消费者有三种:复购:消费记录>=2,无消费:消费记录==0,有消费非复购:消费记录==1
# 对应表示复购 1 ,无消费 NaN ,有消费非复购 0
purchase_r=pivoted_counts.applymap(lambda x: 1 if x>=2 else np.NaN if x==0 else 0)
purchase_r.head()
(purchase_r.sum()/purchase_r.count()).plot(figsize=(12,5))
<AxesSubplot: xlabel='month'>
png
png
回购率分析
# 在同一个时间窗口进行了消费,在下一个时间窗口又进行消费
def purchase_back(data):
    status =[]
    # 1:回购用户 0:非回购用户(当月消费,下个月未消费) NaN:当前月份未消费
    for i in range(17):
        if data[i]==1:
            if data[i+1]==1:
                status.append(1) #回购用户
            elif data[i+1]==0:
                status.append(0)
        else:
            status.append(np.NaN)
    status.append(np.NaN)
    return pd.Series(status,df_purchase.columns)
purchase_b=df_purchase.apply(purchase_back,axis=1)
purchase_b.head()
month 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
user_id
2 0.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
3 0.0 NaN 1.0 0.0 NaN NaN NaN NaN NaN NaN 0.0 NaN NaN NaN NaN NaN 0.0 NaN
4 0.0 NaN NaN NaN NaN NaN NaN 0.0 NaN NaN NaN 0.0 NaN NaN NaN NaN NaN NaN
5 1.0 0.0 NaN 1.0 1.0 1.0 0.0 NaN 0.0 NaN NaN 1.0 0.0 NaN NaN NaN NaN NaN
6 0.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
plt.figure(figsize=(20,4))
plt.subplot(211)
# 回购率
(purchase_b.sum()/purchase_b.count()).plot(label='回购率')
# 复购率
(purchase_r.sum()/purchase_r.count()).plot(label='复购率')
plt.legend()
plt.ylabel('百分比%')
plt.title('用户回购率与复购率对比图')

plt.subplot(212)
plt.plot(purchase_b.sum(),label='回购人数')
plt.plot(purchase_b.count(),label='购物总人数')
plt.xlabel('month')
plt.ylabel('人数')
plt.legend()
<matplotlib.legend.Legend at 0x1d55cd57c50>
png
png
物流数据分析
阅读笔记--《洞见》
  • 文章目录
  • 站点概览

真诚的生活着

8 日志
3 分类
2 标签
© 2020 - 2023 Teqing Cai
0%