「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模型
- 最近一次消费 (Recency)——date列最大值
- 消费频率 (Frequency)——product购买商品数量代替
- 消费金额 (Monetary)——amount消费金额
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%的消费额。所以说,狠抓高质量用户是万古不变的道理,这些高质量客户都是“会员”类型,需要专门]为会员优化购物体验,比如专线接听、特殊优惠等等。