用户消费行为分析

数据来自CDNow网站的一份用户购买CD明细,现在尝试从不同维度分析用户的消费行为

  • 分析内容:
    • 按月对数据进行分析:每月用户消费次数,每月用户的购买量,每月用户的购买总金额
    • 用户个体消费数据分析:用户个体消费金额与消费次数的统计描述,用户消费金额和消费次数的散点图,用户消费金额的分布图(符合二八法则),用户消费次数的分布图,用户累计消费金额的占比
    • 用户消费行为分析:用户第一次消费时间,用户最后一次消费时间,用户分层,新老客消费比,用户购买周期,用户生命周期

一、理解数据

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
plt.style.use('ggplot')
import os
os.chdir(r'D:\anaconda\workplace\Data_Analysis\Data')
# user_id:用户ID    order_dt:购买日期    order_products:购买产品数    order_amount:购买金额
columns = ['user_id','order_dt','order_products','order_amount']
# 用户id 购买日期 购买产品数 购买金额
df = pd.read_table("master.txt",names = columns,sep = '\s+')
df.head()
image.png

image.png

可以看到

  • 数据集有近7W条数据,没有空值,数据集比较干净
  • 大部分订单只消费了少量商品(平均2.4),中位数是2,其分位数是3,说明用户购买量大部分都不多,少部分购买量大的,有一定极值干扰
  • 用户的订单金额比较稳定,人均购买CD金额在35,中位数在25元,存在极致干扰,小额比较多,大额较少,收入来源很大一部分是来自大额。符合二八原则

二、按月分析用户消费趋势

  • 每月的消费总金额
  • 每月的消费次数
  • 每月的产品购买量
  • 每月的消费人数
    1、每月的消费总金额
# 解析日期
df['oreder_dt'] = pd.to_datetime(df.order_dt, format='%Y%m%d')
df['month'] = df.order_dt.values.astype('datetime64[M]') 

grouped_month = df.groupby('month')
plt.figure(1, figsize=(10, 4))
plt.title('每月销售额')
plt.ylabel('销售额')
grouped_month.order_amount.sum().plot()
plt.show()
image.png

可以看到,消费金额呈现早期销售多,后期剧降后趋向平稳
2、每月的消费次数

plt.figure(1, figsize=(10, 4))
plt.title('每月订单数')
plt.ylabel('订单数')
grouped_momnth.user_id.count().plot()
plt.show()

image.png

前三个月订单数在 10000 笔左右,后续月份的平均消费订单数则在 2500 笔左右。
3、每月的产品购买量

plt.figure(1, figsize=(10, 4))
plt.title('每月用户购买张数')
plt.ylabel('CD碟数(张)')
grouped_month.order_products.sum().plot()
plt.show()

image.png

从图中可以看到,销量在前几个月异常高涨,并在3月达到最高峰,4月开始骤降,后续销量较为稳定,且有轻微下降趋势。
4、每月的消费人数

plt.figure(1, figsize=(10, 4))
plt.title('每月消费人数')
plt.ylabel('人数')
grouped_month.user_id.apply(lambda x: len(x.drop_duplicates())).plot()
plt.show()

image.png

每月的消费人数小于每月的消费次数,但是区别不大。前三个月每月的消费人数在8000—10000之间,后续月份,平均消费人数在2000不到。一样是前期消费人数多,后期平稳下降的趋势。推测前三个月可能由于商家有促销活动,吸引大量的用户来消费购买
三、用户个体消费分析

  • 用户消费金额、消费次数的描述统计
  • 用户消费金额和消费次数的散点图
  • 用户消费金额的分布图
  • 用户消费次数的分布图
  • 用户累计消费金额占比(百分之多少的用户占了百分之多少的消费额)

1、用户消费金额,消费次数的描述统计

grouped_user = df.groupby('user_id')
grouped_user.sum().describe()
image.png
  • 用户平均购买了7张CD,但是中位数为3,说明小部分用户购买了大量CD,平均值大于中位数,是右偏分布,说明小部分用户购买了大量的 CD。
  • 用户平均消费106元,中位数为43,结合分位数和最大值看,平均数仅和 75 分位接近,肯定存在小部分的高频消费用户。
    2、用户消费金额和购买数量的散点图
plt.figure(figsize=(12,4))
plt.subplot(121)
plt.scatter(x = 'order_amount', y = 'order_products',data=df)
plt.xlabel('每笔订单消费金额')
plt.ylabel('每笔订单购买数量')

plt.subplot(122)
plt.scatter(x = 'order_amount',y = 'order_products', 
            data = grouped_user.sum()) 
plt.xlabel('每位用户消费金额')
plt.ylabel('每位用户购买数量')
plt.show()
image.png
  • 从每笔订单的散点图观察:大部分订单消费金额处于400元以下,每笔订单购买数量处于40以下,所以大部分用户都是少量购买
  • 从每位用户的消费散点图观察:每位用户购买数量与每位用户消费金额具有很强的线性相关性,也好理解,这是 CD 网站的销售数据,商品较为单一,金额和商品的关系因此呈线性关系,离群点不多
    3、用户消费分布图
plt.figure(figsize=(12, 4))
plt.subplot(121)
ax = grouped_user.order_amount.sum().hist(bins=50)
ax.set_xlabel('金额(美元)')
ax.set_ylabel('用户人数')
ax.set_xlim(0, 2000)
ax.set_title('用户消费金额分布图')

plt.subplot(122)
ax1 = grouped_user.order_products.sum().hist(bins = 50)

ax1.set_xlabel('CD 数(张)')
ax1.set_ylabel('用户人数')
ax1.set_xlim(0, 150)
ax1.set_title('购买 CD 数分布图')
plt.show()
image.png

从直方图可知,用户消费金额,绝大部分呈现集中趋势,大部分用户购买CD数在20张内,高消费用户在图上几乎看不到,这是符合消费行为的行业规律。
4、用户累计消费金额的占比

user_cumsum = grouped_user.sum().sort_values('order_amount').apply(lambda x:x.cumsum()/x.sum())
user_cumsum.reset_index().order_amount.plot()

image.png

按用户消费金额进行升序排列,由图可知50%的用户仅贡献了15%的消费额度;而排名前5000的用户就贡献了60%的消费额
四、用户消费行为

  • 用户第一次消费(首购)
  • 用户最近一次消费
  • 用户分层
    • RFM
    • 新、老、活跃、流失
  • 用户购买周期(按订单)
    • 用户消费周期描述
    • 用户消费周期分布
  • 用户生命周期(按第一次&最后一次消费)
    • 用户生命周期描述
    • 用户生命周期分布

1、用户首购时间

grouped_user.min().order_dt.value_counts().plot()
image.png

用户第一次购买分布,集中在前三个月,其中,在2月11日至2月25日有一次剧烈的波动
2、用户最近一次购买时间

grouped_user.max().order_dt.value_counts().plot()
image.png

可以看出

  • 用户最后一次购买的分布比第一次分布广
  • 大部分最后一次购买,集中在三个月,说明很多用户购买了一次后就不进行购买
  • 随着时间的递增,最后一次购买数也在递增,消费呈现流失上升的状况

3、用户分层
3.1 构建RFM 模型

  • R:末次消费距今的天数(数值越小越好)
  • F:消费频率(越高越好)
  • M:消费金额(越高越好)
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)
rfm.head()
image.png
def rfm_func(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()
image.png
rfm.groupby('label').sum()
image.png
for label,gropued in rfm.groupby('label'):
    x= gropued['F']
    y = gropued['R']
    
    plt.scatter(x,y,label = label) # 利用循环绘制函数
plt.legend(loc='best') # 图例位置
plt.xlabel('Frequency')
plt.ylabel('Recency')
plt.show()
image.png

从RFM分层可知,大部分用户为重要保持客户,但是这是由于极值的影响,所以RFM的划分标准应该以业务为准

  • 尽量用小部分的用户覆盖大部分的额度
  • 不要为了数据好看划分等级

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

pivoted_counts = df.pivot_table(index='user_id',
                               columns = 'month',
                               values='order_dt',
                               aggfunc='count').fillna(0)
pivoted_counts.head()
image.png
df_purchase = pivoted_counts.applymap(lambda x:1 if x > 0 else 0)
df_purchase.tail()
image.png
col = ['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']
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')
    return pd.Series(status,index = col)

若本月没有消费

  • 若之前是未注册,则依旧为未注册
  • 若之前有消费,则为流失/不活跃

若本月有消费

  • 若是第一次消费,则为新用户
  • 如果之前有消费,上个月为不活跃,则为回流
  • 如果上个月为未注册,则为新用户
  • 除此之外,为活跃
purchase_stats = df_purchase.apply(active_status,axis = 1)
purchase_stats.head()
image.png
purchase_stats_ct = purchase_stats.replace('unreg',np.nan).apply(lambda x:pd.value_counts(x))
purchase_stats_ct.fillna(0).T.apply(lambda x:x/x.sum(),axis=1)
image.png

由上表可知,每月的用户消费状态变化

  • 活跃用户,持续消费的用户,对应的是消费运营的质量
  • 回流用户,之前不消费本月才消费,对应的是唤回运营
  • 不活跃的用户。对应的是流失
purchase_stats_ct.fillna(0).T.plot.area(figsize=(10,6))
image.png

可以看到不活跃用户,也就是图中的灰色区域,占了大部分面积,新用户主要集中在1到3月份,活跃用户和回流用户在四月份之后就趋向稳定,属于核心客户,但总量并不高

3.3 回流用户占比

plt.figure(figsize=(20, 6))
rate = purchase_stats_ct.fillna(0).T.apply(lambda x: x/x.sum())
plt.plot(rate['return'],label='return')
plt.plot(rate['active'],label='active')
plt.legend()
plt.show()
image.png
  • 回流用户比:某个时间段内回流用户在总用户中的占比
    • 由图可知,用户每月回流用户比占 5% ~ 8% 之间,有下降趋势,说明客户有流失倾向。
  • 回流用户率:上月有多少不活跃用户在本月消费
    • 由于这份数据的不活跃用户量基本不变,所以这里的回流率,也近似等于回流比
  • 活跃用户比:某个时间段内活跃用户在总用户中的占比。
    • 活跃用户的占比在 3% ~ 5%间,下降趋势更显著,活跃用户可以看作连续消费用户,忠诚度高于回流用农户。

结合活跃用户和回流用户看,在后期的消费用户中,60%是回流用户,40%是活跃用户,整体用户质量相对不错。也进一步说明前面用户消费行为分析中的二八定律,反应了在消费领域中,狠抓高质量用户是不变的道理。

4、用户购买周期

# 订单时间间隔
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
# 订单周期分布图
(order_diff/np.timedelta64(1,'D')).hist(bins=20)
image.png
  • 用户的平均购买周期是68天
  • 绝大部分用户的购买周期低于100天
  • 可以根据用户的购买周期对用户进行定期召回

5、用户生命周期

# 最后一次购买的时间减去首购时间
user_life = grouped_user.order_dt.agg(['min', 'max'])
user_life.head()
image.png
(user_life['max'] - user_life['min']).describe()
image.png
# 只消费过一次的用户占比
(user_life['min'] == user_life['max']).value_counts().plot.pie()
plt.show()
image.png
  • 用户的平均生命周期134天,中位数为0,说明大部分用户的第一次消费也是最后一次消费,这批用户质量较低,而最大的有544天,这批用户属于核心用户
plt.figure(figsize=(20, 4))
plt.subplot(121)
((user_life['max'] - user_life['min']) / np.timedelta64(1, 'D')).hist(bins = 15)
plt.title('二次消费以上用户的生命周期直方图')
plt.xlabel('天数')
plt.ylabel('人数')

# 过滤生命周期为0 的
plt.subplot(122)
u_l = ((user_life['max'] - user_life['min']).reset_index()[0] / np.timedelta64(1, 'D'))
u_l[u_l > 0].hist(bins = 40)
plt.title('二次消费以上用户的生命周期直方图')
plt.xlabel('天数')
plt.ylabel('人数')
plt.show()
image.png

由于大量用户的消费周期为0,我们将周期为0的数据进行过滤,得到右边的图,将两图进行对比,可以看出,依然有不少用户的生命周期趋向0天。部分低质量用户虽然消费两次,但并不能持续消费,若要提高用户留存率,应该在用户第一次消费后继续进行引导,少部分用户集中在50 - 350天,属于普通用户,忠诚度一般。350天之后的就属于高质量用户了,忠诚度较高,应尽力维护。

五、复购率和回购率分析

  • 复购率:自然月内,购买多次的用户占比
  • 回购率:曾经购买的用户在某一时期内的再次购买的占比

复购率

# 消费两次及以上为 1 ,消费一次为 0 ,没有消费为空
purchase_r = pivoted_counts.applymap(lambda x:1 if x>1 else np.nan if x==0 else 0)
purchase_r.head()
image.png
(purchase_r.sum()/purchase_r.count()).plot(figsize=(10,4))
image.png
  • 复购率稳定在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)
    return pd.Series(status,index = col)

purchase_b = df_purchase.apply(purchase_back,axis=1)
purchase_b.head()
image.png
  • 1 为回购用户, 0 为上月没购买当月购买过,NaN 为连续两月都没购买
plt.figure(figsize=(20,8))
plt.subplot(211)
(purchase_b.sum() / purchase_b.count()).plot()
plt.title('用户回购率图')
plt.ylabel('百分比%')

plt.subplot(212)
plt.plot(purchase_b.sum(),label='每月消费人数')
plt.plot(purchase_b.count(),label='每月回购人数')
plt.xlabel('month')
plt.ylabel('人数')
plt.legend()
plt.show()
image.png

从图中可以看出,用户的回购率高于复购,约在30%左右,和老客户差异不大。从回购率和复购率综合分析可以得出,新客的整体质量低于老客,老客的忠诚度(回购率)表现较好,消费频次稍次

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,755评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,369评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,799评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,910评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,096评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,159评论 3 411
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,917评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,360评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,673评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,814评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,509评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,156评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,123评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,641评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,728评论 2 351

推荐阅读更多精彩内容