电商网站用户购买CD消费分析

用户消费行为分析

一、项目介绍

  用户消费行为贯穿于生活中的各行各业,例如:淘宝、拼多多、叮当快药(医疗行业)、金融理财产品以及物流行业等等。

行业

  每天都会产生很多的消费行为,数据分析中经常需要对用户的消费行为进行分析。为了达到企业盈利、多卖产品的目的,除了保证产品质量,首要任务就是分析用户的消费行为。通过分析,可以更好地针对性地加大营销力度,进而提升企业的营业额。

  本项目以某电商网站用户购买CD的明细作为样本数据,总共有6万条数据。

  项目整体内容分为8大部分,从项目的介绍到数据处理再到各种维度的分析。比如用户整体和个体的分析、消费时间的分析、用户分层模型、复购率和回购率、生命周期和购买周期等等。通过分析,最后总结得出最终的结论。

项目内容

二、需求分析

业务化分析思路:

  用户通过自然渠道或付费渠道成为新客访问网站,从网站的营销落地页/通过导航页/通过搜索/个性化推荐访问商品详情页,用户收藏商品/加入购物车后,产生订单进行支付,获取物流信息,最终收到货物。收货后会产生退换货/用户评价分享/注册会员的行为,最后,用户选择流失/只购买一次或进行二次购买转换为回头客、忠诚客。

图片

需求点分析如下:(分析维度)

需求分析

三、数据预处理

  • user_id:用户ID
  • order_dt:购买日期
  • order_products:购买产品数
  • order_amount:购买金额

数据时间:1977年1月-1998年6月用户行为数据,约6万条

1.导入数据,导入常用库

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from datetime import datetime
%matplotlib inline
plt.rcParams['font.sans-serif'] = 'SimHei'
plt.style.use('ggplot')  # 更改绘图风格,R语言绘图库的风格

2.导入源数据

# 导入数据
columns = ['user_id','order_dt','order_products','order_amount']  # 设置列名
df = pd.read_table('CDNOW_master.txt',names=columns,sep='\s+')  # sep:'\s+':匹配任意个空格
df.head()

源数据

由上表:

  • 日期格式需要转换
  • 存在同一个用户一天内购买多次的行为

3.查看数据基本信息

df.describe()

数据描述统计

由上表:

  • 用户平均每笔订单购买2.4个商品,标准差2.3,有轻微波动,但属于正常现象;
  • 75%分位数也为3,说明绝大多数订单的购买量都不多,数量范围在2-3个左右;
  • 从购买金额列,反映出大部分订单消费金额集中在中小额,30-45左右。
df.info()

数据基本信息

由上表:

  • 日期类型为int64,需要转换

4.数据预处理

df['order_date'] = pd.to_datetime(df['order_dt'],format='%Y%m%d')
# order_date: datetime64[ns]
# format参数:按照指定的格式去匹配要转换的数据列
# %Y:四位数的年份-0000   %m:两位数月份-00    %d:两位数日期-00
# %y:两位是月份-00       %h:两位小时-00      %M:两位数分钟-00       %s:两位秒-00

# 将order_date转换成精度为月份的数据列
df['month'] = df['order_date'].astype('datetime64[M]')  # [M]:控制转换后的精度
# df['month'] = df.order_dt.values.astype('datetime64[M]')  #用values先将数据转换为数组格式
df.head()
df.info()

数据预处理

数据预处理信息

四、用户整体消费趋势分析(按月)

  • 产品购买数量
  • 消费金额
  • 消费次数
  • 消费人数
plt.figure(figsize=(20,15))  # 单位是英寸
# 每月的产品购买数量
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('每月的消费次数')

# 每月的消费人数(先对user_id去重,再统计个数)
plt.subplot(224)                                       
df.groupby(by='month')['user_id'].apply(lambda x: len(x.drop_duplicates())).plot()  # 去重函数drop_duplicates()、计数len()(也可以用count())
plt.title('每月的消费人数')

户整体消费趋势

分析结果:

  • 图一可以看出,前三个月销量非常高,而后的销量较为稳定,并且稍微呈现下降趋势;

  • 图二可以看出,前三个月消费金额依然较高,与消费数量成正比例关系,三月份后下降严重,并呈现下降趋势,猜测原因:

    • 与月份有关,我国的1-3月份处于春节前后;
    • 网站在1-3月份的时候加大了促销力度
  • 图三可以看出,前三个月订单数在10000左右,后续月份的订单数在2500左右

  • 图四可以看出,前三个月平均消费人数在8000-10000,后续月份的平均消费人数在2000不到

  • 总结:所有数据显示,97年前三月消费事态异常,后续趋于常态化

五、用户个体消费分析

1.用户消费金额,消费次数(产品数量)的描述统计(用户个体user_id)

user_grouped = df.groupby(by='user_id').sum()
print(user_grouped.describe())
print('用户数量:',len(user_grouped))

个体分组
用户数量: 23570

  • 从用户的角度,用户数量23570个,每个用户平均购买7个CD,但是中位数只有3,并且最大购买量为1033,平均值大于中位数,属于典型的有偏分布(替购买量小于7的用户背锅);
  • 从消费金额的角度,用户平均消费106,中位数43,并且存在土豪用户消费金额为13990,结合分位数和最大值来看,平均数与75%分位数几乎相等,属于典型的右偏分布,说明存在小部分用户(25%)高额消费(这些用户需要给消费金额小于106的用户背锅)
# 绘制每个用户的产品购买量与消费金额的散点图
df.plot(kind='scatter',x='order_products',y='order_amount')

购买量与消费金额的散点图

  • 从图中可知,用户的消费金额与购买了存在线性关系,每个商品均价15左右;
  • 订单的极值点比较少(消费金额>1000/购买量>60),对于样本来说影响不大,可以忽略不计

2.用户消费分布图

plt.figure(figsize=(12,4))
plt.subplot(121)
plt.xlabel('每个订单的消费金额') # 对每条记录
df['order_amount'].plot(kind='hist',bins=50) # 区间分数,影响柱子的宽度,值越大柱子越细;宽度=(列最大值-最小值)/bins

plt.subplot(122)
plt.xlabel('每个用户购买的数量')
df.groupby(by='user_id')['order_products'].sum().plot(kind='hist',bins=50)

用户消费分布图

  • 绝大部分的订单消费金额在100以内
  • 每个用户的购买数量较少,集中在50以内
  • 综上,该网站的大部分用户是消费金额低,并且购买量小于50的类型(是正常的电商领域现象)

3.用户累计消费金额的占比(用户的贡献度)

# 进行用户分组,取出消费金额,进行求和,排序,重置索引
user_cumsum = df.groupby(by='user_id')['order_amount'].sum().sort_values().reset_index()
# 每个用户消费金额累加
user_cumsum['amount_cumsum'] = user_cumsum['order_amount'].cumsum()
# 消费金额总值
amount_total = user_cumsum['amount_cumsum'].max()
# 前x名用户的贡献率
user_cumsum['prop'] = user_cumsum.apply(lambda x:x['amount_cumsum']/amount_total,axis=1)
user_cumsum.tail()

用户累计消费金额

user_cumsum['prop'].plot()
plt.xlabel('用户数量')

用户的贡献度

由图分析,前20000名用户贡献总金额的40%,剩余3500名用户贡献了60%(二八法则)

六、用户消费行为

1.用户首购时间

# 用户分组,取最小值,即为首购时间,并计数
df.groupby(by='user_id')['order_date'].min().value_counts().plot()

用户首购时间

由图可知,首次购买的用户量在1月1号~2月10号呈明显上升趋势,后续开始逐步下降
猜测原因:公司产品的推广力度或价格的调整

2.用户最近一次消费时间

# 用户分组,取最小值,即为首购时间,并计数
df.groupby(by='user_id')['order_date'].max().value_counts().plot()

用户最近一次消费时间

  • 大多数用户最后一次购买时间集中在前3个月,说明缺少忠诚用户;
  • 随着时间的推移,最后一次购买商品的用户量呈现上升趋势;
  • 猜测这份数据是针对前三个月消费的用户在之后18个月消费的跟踪记录

3.用户分层

3.1 构建RFM模型(透视图的使用)

  • 为了进行精细化运营,可以利用RFM模型对用户价值指数(衡量历史到当前用户贡献的收益)进行计算,其中:
  • R:(Recency,最近一次消费)R值越大,表示客户交易发生的日期越久远,反之则最近一次交易发生的日期越近;
  • F:(Frequency,消费频率),F值越大,表示客户交越频繁,反之则表示客户交易不够活跃(此处我们选择购买产品数量作F);
  • M:(Monetary,消费金额),M值越大,表示客户价值越高,反之则表示客户价值越低

根据上述三个维度,对客户做细分

RFM模型

透视表是一种可以对数据动态排布并且分类汇总的表格格式。在pandas中称为pivot_table

  • 灵活性高,可以随意定制分析计算要求;
  • 脉络清晰易于理解数据;
  • 操作性强,报表神器
# 透视表的使用
rfm = df.pivot_table(index = 'user_id',
                     values = ['order_products','order_amount','order_date'],
                     aggfunc={
                        'order_date':'max',     # 最近一次购买
                        'order_products':'sum', # 购买产品的总数量
                        'order_amount':'sum'    # 最消费总金额
                    })
rfm.head()

透视表的建立

  • index:相当于groupby
  • values:取出的数据列
  • aggfunc:key值必须存在values列中,并且必须跟随有效的聚合函数
# 用每个用户的最后一次购买时间-日期列中的最大值(当前日期),最后转换成天数,小数保留一位
rfm['R'] = -(rfm['order_date'] - rfm['order_date'].max()) / np.timedelta64(1,'D') # 取相差的天数
rfm.rename(columns={'order_products':'F','order_amount':'M'},inplace=True)
rfm.head()

RFM列名

# RFM计算方式:每一列数据减去数据所在列的平均值,有正有负,将结果只与1作比较,如果大于等于1,设置标签为1,否则为0
def rfm_func(x):   # 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['label']

# 客户分层可视化
for label,grouped in rfm.groupby(by='label'):
    x = grouped['F']    # 单个用户的购买数量
    y = grouped['R']    # 最近一次购买时间与98年7月的相差天数
    # 可以随意选择
    plt.scatter(x,y,label = label)
plt.legend()
plt.xlabel('F')
plt.ylabel('R')

rfm散点图

3.2 新、老、活跃、回流、流失分层用户

  • 新用户的定义是第一次消费
  • 活跃用户即老客,在某个时间窗口内有过消费
  • 不活跃用户则是时间窗口内没有消费过的老客
  • 回流用户:即回头客
  • 用户回流的动作可以分为自主回流和人工回流
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)
df_purchase.head()

转成boolean

  • apply:作用于dataFrame数据中的一行或一列数据
  • applymap:作用于dataFrame数据中的每一个元素
  • map:本身是一个series的函数,在DataFrame结构中无法使用,作用于series中的每一个元素
# 用户分群:新用户、活跃用户、不活跃用户、回流用户
# 由于用户第一次消费的时间不同,定义一个新状态‘未注册(unreg)’来表示未开始第一次消费的新用户

# df_purchase.apply(func,axis=1)

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
        # 本月有消费
        else: # data[i] == 1
            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
                    status.append('active')
    return pd.Series(status,df_purchase.columns)  #值status,列名为df_purchase中的列名

purchase_status = df_purchase.apply(active_status,axis=1) #得到用户分层结果
purchase_status.head()

用户分层结果

# 用NaN替换unreg
purchase_status_ct = purchase_status.replace('unreg',np.NaN).apply(lambda x:pd.value_counts(x))
purchase_status_ct.head()

替换unreg

purchase_status_ct.T.fillna(0).plot.area()  # 行列变换后绘制面积图
  • 由图可知,前三个月的活跃用户(红)和新用户(蓝)占比较大
  • 四月份过后,新用户和活跃用户开始减少,并且呈现稳定趋势
  • 回流用户主要产生在4月过后,呈现稳定趋势,是网站的重要客户(忠诚客户)

3.3 回流用户占比

# 用户的占比:status/总消费人数
rate = purchase_status_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()

回流、活跃用户的占比图

  • 回流用户:前五个月回流用户上涨,之后呈现下降趋势,平均维持在5%比例
  • 活跃用户:前三个月活跃用户大量增长(活动引来新用户),4月份过后开始下降,平均维持在2.5%左右
  • 网站运营稳定后,回流用户(5%)占比大于活跃用户(2.5%)

4.用户购买周期

shift函数的使用

# shift函数:将数据移动到指定的位置
data1 = pd.DataFrame({
    'a':[0,1,2,3,4,5],
    'b':[5,4,3,2,1,0]
})
data1.shift(axis=0)  # 整体向下移动一个位置(默认值:axis=0)
data1.shift(axis=1)  # 整体向右移动一个位置
order_diff = df.groupby(by='user_id').apply(lambda x:x['order_date']-x['order_date'].shift())  # 当前订单日期-上次订单日期
order_diff.head()
order_diff.describe()

用户购买周期

用户购买周期描述统计

# 将dtype: timedelta64[ns]类型转换为天数,绘制直方图
(order_diff/np.timedelta64(1,'D')).hist(bins=20)
plt.xlabel('购买周期')

购买周期直方图

  • 用户的平均消费周期为68天,且大多数用户的消费周期低于100天
  • 呈现典型的长尾分布,只有小部分用户消费周期在200天以上(不积极消费的用户),可以在这批用户消费后3天左右进行电话回访或者短信赠送优惠券等活动,增大消费频率

5.用户生命周期(新老客户消费比)

# 计算方式:用户最后一购买日期(max)-第一次购买的日期(min)(如果差值等于0,说明用户仅仅购买了一次)
user_life = df.groupby('user_id')['order_date'].agg(['min','max'])
(user_life['max'] == user_life['min']).value_counts().plot.pie(autopct='%1.1f%%') # 格式化为1位小数
plt.legend(['仅消费一次','多次消费'])

客户消费比

一半以上的用户仅消费了一次,说明运营不利,留存率不好

(user_life['max'] - user_life['min']).describe() # 生命周期描述统计分析
  • 用户平均生命周期为134天,但是中位数为0,再次说明大多数用户只消费了一次,是低质量用户;
  • 75%分位数以后的用户,生命周期大于294,属于核心用户,需要着重维护
# 绘制所有用户的生命周期直方图
plt.figure(figsize=(12,6))
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')
u_1[u_1>0].hist(bins=15 )
plt.title('多次消费的用户生命周期直方图')
plt.xlabel('生命周期天数')
plt.ylabel('用户人数')
  • 通过两图对比可知,第二幅图过滤掉了生命周期为0的用户,呈现双峰结构
  • 虽然过滤后仍有一部分用户的生命周期趋于0,但状况明显比第一幅图好了很多;这部分用户虽然进行了多次消费但是生命周期仍然很短,属于普通用户,可针对性进行营销推广活动
  • 少部分用户生命周期集中在300-500天,属于忠诚用户,需要大力度维护此类用户

七、复购率和回购率分析

1.复购率分析

# 计算方式:在自然月内,购买多次的用户在总消费人数中的占比(包括在同一天消费多次)
# 某月内的消费者:复购用户(1)、非复购用户(0)、无消费用户(NaN)
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():表示总消费人数(NaN不参与计数)
(purchase_r.sum() / purchase_r.count()).plot(figsize=(12,6))

复购率

  • 由图:前三个月复购率开始上升,后续趋于平稳维持在20%-22%之间
  • 前三个月复购率较低的原因:大部分新用户仅仅购买一次

2.回购率分析

# 计算方式:在一个时间窗口内进行了消费,在下一个窗口内又进行了消费
# 1:回购用户  0:非回购用户   NaN:当前月份未消费
# df_purchase.apply(func,axis=1)
def purchase_back(data):
    status = [] # 存储用户回购率状态
    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:
# 由于for循环只需要执行17次,返回17个结果,但原表有18列,需要填充最后一列数据
    status.append(np.NAN)
    return pd.Series(status,df_purchase.columns)
purchase_b = df_purchase.apply(purchase_back,axis=1)
purchase_b

回购率

# 用户回购率与复购率的对比
plt.figure(figsize=(10,12))
plt.subplot(211)
# 回购率
(purchase_b.sum()/purchase_b.count()).plot(label = '回购率')
# 复购率
(purchase_b.sum()/purchase_b.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.legend()
plt.ylabel('人数')
plt.title('回购人数与购物总人数的对比图')

回购率可视化

由图一:

  • 回购率:平稳后在30%左右,波动性相对较大
  • 复购率低于回购率,平稳后在20%左右,波动性较小
  • 前三个月的回购率和复购率都呈现上升趋势,说明新用户需要一定的时间转换成复购/回购用户
  • 结合新老用户分析,新客户的忠诚度远低于老客户

由图二:

  • 前三个月的购物总人数远远大于回购人数,主要是因为很多新用户在1月份进行了首次购买
  • 三个月后,回购人数和购物总人数开始稳定,其中,回购人数稳定在1000左右,购物总人数在2000左右。

八、结论

  • 1.整体趋势:按年的月份趋势销量和销售额在1-3月份相对较高,然后骤降,原因可能跟这段时间的大力促销与商品的季度属性有关;
  • 2.用户个体特征:每笔订单的金额和商品购买量都集中在区间的低段水平,都是小金额小批量进行购买,此类交易群体,可在丰富产品线和增加促销活动提高转换率和购买率;
  • 3.大部分用户的消费总额和购买总量都集中在区间的低段,长尾分布,这个主要跟用户的需求有关,可以对商品进行多元文化价值的赋予,增强其社交价值属性,提高用户的价值需求;
  • 4.用户的消费周期:有两次消费以上的用户,平均消费周期为68天,所以在50-60天期间,应该对这批用户进行刺激召回。例如10天咨询满意度,30天发放优惠券,60天提醒使用优惠券;
  • 5.用户的生命周期:有两次消费以上的用户,平均生命周期为276天,针对生命周期分布在20天内或400-500天间的用户,应该在20天内对用户进行引导,促进其再次消费形成消费习惯,延长生命周期;生命周期在100-400天的用户,也要根据其特点推出有针对性的营销活动,引导其持续消费;
  • 6.新客户的复购率约为12%,老客户的复购率在20%左右;新客户的回购率在15%左右,老客户的回购率在30%左右;需要营销策略积极引导其再次消费以及持续消费;
  • 7.用户质量:用户个体消费有一定规律性,大部分用户的消费金额集中在2000以下,用户消费反映了二八法则。消费排名前20%的用户贡献了80%的消费额。所以,狠抓高质量用户是亘古不变的道理,这些高质量客户都是“会员”类型,需要专门为会员优化购物体验,比如专线接听、特殊优惠等等。

九、方法总结

  • 1.针对用户按照月份进行整体和个体分析,主要分析维度为人数,消费金额和购买量;
  • 2.消费分析:首购时间、最近一次购买时间、购买周期(相邻两个购物时间的间隔)、用户分层(RFM模型+数据透视表),分析维度主要是新用户、活跃用户、不活跃用户流失分析、回流用户占比;
  • 3.复购率和回购率的分析
posted @ 2022-10-05 14:59  啊哼  阅读(242)  评论(0编辑  收藏  举报