亚灿网志

「Python」用户消费行为分析

预准备

引包

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

绘图全局设置

# 行内绘图
# % matplotlib inline
plt.style.use('ggplot')  # 绘图风格
plt.rcParams['font.sans-serif'] = ['SimHei']  # 显示中文字体

读取数据并整体观察数据结构

In [3]: col = ['user', 'date', 'product', 'amount']
   ...: df_0 = pd.read_table('./data/CDNOW_master.txt', names=col, sep='\s+')  #  sep='\s+ 列于列之间的分隔为至少一个空格
   ...: df = df_0.copy()
   ...: df
    
Out[3]: 
        user      date  product  amount
0          1  19970101        1   11.77
1          2  19970112        1   12.00
2          2  19970112        5   77.00
3          3  19970102        2   20.76
4          3  19970330        2   20.76
...

In [4]: df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 69659 entries, 0 to 69658
Data columns (total 4 columns):
 #   Column   Non-Null Count  Dtype
---  ------   --------------  -----
 0   user     69659 non-null  int64
 1   date     69659 non-null  int64
 2   product  69659 non-null  int64
 3   amount   69659 non-null  float64
dtypes: float64(1), int64(3)
memory usage: 2.1 MB

分析:观察数据结构可得,数据表一共有69659行、4列,且无空行,其中4列包括user(用户索引列)、date(用户消费时间列)、product(购买产品数量列)、amount(购买总金额列)。

数据的预处理

观察date(用户消费时间列)可发现,其时间格式Pandas未能识别,需要手动将其转换成时间格式列(datetime),方便后续操作。

In [5]: df['date'] = pd.to_datetime(df['date'], format='%Y%m%d')

后续数据分析需要按月来操作,因此需要读取date(用户消费时间列)中的月份,

In [6]: df['month'] = df['date'].astype('datetime64[M]')

注意看这里这里从date(用户消费时间列)中获取月份的方式,并没有使用:

In [7]: df['date'].dt.month

为什么呢?使用.dt.month虽然可以获取月份,但是遇到多个年份中提取月份的话就会出现问题,做个简单小对比:

In [8]: demo = pd.DataFrame({
   ...:     'date': ['2022-07-23', '2021-07-23']
   ...: })
   ...: demo['date'] = pd.to_datetime(demo['date'])
   ...: demo['astype'] = demo['date'].astype('datetime64[M]')
   ...: demo['dt.month'] = demo['date'].dt.month
   ...: demo

Out[8]: 
        date     astype  dt.month
0 2022-07-23 2022-07-01         7
1 2021-07-23 2021-07-01         7

由此可以看出,当遇到多个年份有相同月份的时候,使用.dt.month仅仅可以提取出来月份,但是具体是哪一年的7月,确提取不出来,因此其存在着弊端。

数据分析

月统计量分析

按月份统计产品购买数量、消费金额、消费次数以及消费人数。

df.pivot_table(
    index='month',
    aggfunc={
        'user': 'count',  # 每个月的顾客数量(当同一个顾客下多次订单时,都按照新顾客统计)
        'product': 'count',
        'amount': 'sum'
    })

注意:这种聚合分析方法存在的问题就是,如果一个用户一个月内有多条消费记录,那么其每次都会被当作一个新的消费者记录,即通过'user': 'count'聚合得到的是一个月所有消费记录的数量,并不是本月内有多少不同的顾客来过该店里。

那么如何实现统计每个月内有多少顾客(无重复,比如一个顾客一个月内来了10次店里消费,也按成一次处理)来过店里呢,这就需要分组后做一次去重操作。

df.pivot_table(
    index='month',
    aggfunc={
        'user': lambda x: pd.Series.nunique(x), 
        'product': lambda x: x.nunique(), 
        'amount': 'sum'
    })

使用groupby()方法,然后配合聚合函数也可以达到相同的效果:

df.groupby('month')[['user', 'product', 'amount']].agg({
    'user': lambda x: pd.Series.nunique(x),
    'product': lambda x: pd.Series.nunique(x),
    'amount': 'sum'
})

订单商品数量与订单金额关系分析

df[['product', 'amount']].plot(kind='scatter', x='product', y='amount')

由图中可以看出,大部分订单都有趋向于购买价格少、数量少的特征。

用户消费金额占比(贡献度)

重点使用.cumsum()函数。

user_cumsum = df.pivot_table(index='user', values='amount', aggfunc='sum')
user_cumsum = user_cumsum.sort_values(by='amount').reset_index()
user_cumsum['cumsum_per'] = (user_cumsum['amount'].cumsum() / np.sum(user_cumsum['amount'])).apply(
    lambda x: format(x, '.2%'))
user_cumsum.tail(10)

Out[9]: # 消费累计中贡献度最大的十位用户
        user    amount cumsum_per
23560  15162   4234.45     97.56%
23561   3049   4262.85     97.73%
23562    499   4378.55     97.90%
23563  22279   4490.64     98.08%
23564   6569   4968.00     98.28%
23565   7931   6497.18     98.54%
23566  19339   6552.70     98.80%
23567   7983   6973.07     99.08%
23568  14048   8976.33     99.44%
23569   7592  13990.93    100.00%

用户购买行为分析

每天客流量分析

df.pivot_table(index='user', values='date', aggfunc='min').value_counts().sort_index().plot()

用户最后一次消费时间

df.pivot_table(index='user', values='date', aggfunc='max').value_counts().sort_index().plot()

由此图可以发现,在门店开业初期吸引了大量用户,但是大部分用户仅在前期参与活动后,后期便再也没来过。

RFM模型

rfm_model = df.pivot_table(
    index='user',
    # values=['product', 'amount', 'date'],
    aggfunc={
        'product': 'size',
        'amount': 'sum',
        'date': 'max'
    }
)
# 求出R
rfm_model['R'] = df['date'].max() - rfm_model['date']
rfm_model['R'] = rfm_model['R'].dt.days
# 
rfm_model.rename(columns={
    'amount': 'M',
    'product': 'F'
}, inplace=True)
rfm_model.drop('date', axis=1, inplace=True)

根据RFM模型对用户进行分类:

def check_cus_grade(s):
    label = s.apply(lambda x: '1' if x >= 1 else '0')
    label = label['R'] + label['F'] + label['M']
    grade = {
        '111': '重要价值客户',
        '011': '重要保持客户',
        '101': '重要发展客户',
        '001': '重要挽留客户',
        '110': '一般价值客户',
        '010': '一般保持客户',
        '100': '一般发展客户',
        '000': '一般挽留客户'
    }
    return grade[label]


rfm_model['label'] = rfm_model.apply(lambda x: x - x.mean()).apply(check_cus_grade, axis=1)
rfm_model

RFM模型的可视化:

用户生命周期分析

所谓用户生命周期是指用户第一次消费与最后一次消费的时间间隔。用户最后一次购买日期\=\=第一次购买的日期,说明用户仅仅购买了一次或者用户在同一天内购买了两次。这个时候就需要排除掉仅由一条消费记录的顾客,如果用户仅有一条购买数据,那还谈何生命周期,直接赋值为NaN。

三种不同的实现方法:

# 1、
df.groupby('user').apply(lambda X: X['date'].max() - X['date'].min() if X.shape[0] > 1 else np.NaN)
# 2、
df.groupby('user')['date'].agg(lambda s: (s.max() - s.min()) if len(s) > 1 else np.NaN)

是否多次消费

df.groupby('user').apply(lambda X: 1 if X.shape[0] > 1 else 0).value_counts(normalize=True).plot.pie(autopct='%1.1f%%')
plt.legend(['仅消费一次', '多次消费'])

生命周期分析

user_life_cycle = df.pivot_table(index='user', values='date',
                                 aggfunc=(lambda s: (s.max() - s.min()) if len(s) > 1 else np.NaN))

user_life_cycle.div(np.timedelta64(1, 'D')).plot.hist()
plt.title('所有用户生命周期直方图')
plt.xlabel('生命周期天数')
plt.ylabel('用户人数')

复购率与回购率分析

复购率计算方式:在自然月内,购买多次的用户在总消费人数中的占比(若客户在同一天消费了多次,也称之复购用户)。

user_repurchase_rate = df.pivot_table(index='user', columns='month', values='product', aggfunc='count')

#复购用户:1;非复购的消费用户: 0;自然月没有消费记录的用户: NAN
user_repurchase_rate = user_repurchase_rate.applymap(lambda num: 1 if num > 1 else 0 if num == 1 else np.NaN)

回购率计算方式:在一个时间窗口(一个月)内进行了消费,在下一个窗口内又进行了消费。回购用户:本月消费了,上个月也消费了,记为1;非回购用户:本月消费了,但是上个月未消费,记为0;本月未消费,记为NaN。

def back_purchase(s):
    every_month_status = []
    # 第一个月不会有回购用户
    if s[0] == 1:  # 第一个月消费了
        every_month_status.append(0)  # 记为非回购用户
    else:  # 第一个月未消费
        every_month_status.append(np.NaN)  # 记为NaN

    for i in range(1, len(s)):
        if s[i] == 1:  # 当前月份消费了
            if s[i - 1] == 1:  # 上个月份也消费了
                every_month_status.append(1)  # 回购用户
            else:  # 下个月份未消费
                every_month_status.append(0)  # 非回购用户
        else:  # 当前月份未消费
            every_month_status.append(np.NaN)

    return pd.Series(every_month_status, user_repurchase_rate.columns)

purchase_return = user_repurchase_rate.apply(back_purchase, axis=1)

复购率与回购率的可视化

purchase_return.sum().div(purchase_return.count()).plot(label='回购率')
user_repurchase_rate.sum().div(user_repurchase_rate.count()).plot(label='复购率')
plt.legend()
plt.ylabel('百分比(%)')
plt.title('用户复购率与回购率对比图')

plt.plot(purchase_return.sum(), label='回购人数')
plt.plot(user_repurchase_rate.sum(), label='复购人数')
plt.legend()
plt.ylabel('百分比(%)')
plt.xlabel('month')
plt.title('用户复购人数与回购人数对比图')

总结

1、用户个体特征:每笔订单的金额和商品购买量都集中在区间的低段水平,都是小金额小批量进行购买,此类交易群体,可在丰富产品线和增加促销活动提高转换率和购买率。

2、大部分用户的消费总额和购买总量都集中刚在低段,长尾分布,这个跟用户需求有关,可以对商品进行多元文化价值的赋予,增强其社交价值属性,提高用户的价值需求。

3、用户的消费周期:有二次以上消费的用户,平均68天,所以在50天到60天期间,应该对这批用户进行刺激召回,细致点,比如10天回复满意度,30天发放优惠券,55天的时候提醒优惠券的使用。

4、用户的生命周期:有二次及以上消费的用户的平均生命周期是276天。用户的生命周期分别在20天内与400至500天间,应该在20天内对客户进行引导,促进其再次消费并形成消费习惯,延长其生命周期;在100至400天的用户,也要根据其特点推出有针对性的营销活动,引导其持续消费。

5、新客户的复购率约为12%,老客户的复购率在20%左右;新客户的回购率在15%左右,老客户的回购率在30%左右,需要营销策略积极引导其再次消费及持续消费。

6、用户质量:用户个体消费有一定规律性,大部分用户的消费集中在2000以下,用户消费反应了2/8法则,消费排名前20%的用户贡献了80%的消费额。所以说,狠抓高质量用户是万古不变的道理,这些高质量客户都是“会员”类型,需要专门]为会员优化购物体验,比如专线接听、特殊优惠等等。

当前页面是本站的「Google AMP」版。查看和发表评论请点击:完整版 »