欢迎光临散文网 会员登陆 & 注册

用户消费行为数据行为(源码自用)

2023-03-22 22:52 作者:无真凡尘  | 我要投稿


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

# user_id:用户ID,order_dt:购买日期,order_products:购买产品数量,order_amounts:购买金额
# 数据时间:1997年1月~1998年6月用户行为数据,约6万条

# 导入数据
columns = ['user_id', 'order_dt', 'order_products', 'order_amounts']
df = pd.read_table('CDNOW_master.txt', names=columns, sep=r'\s+')   # sep:'\s+'匹配任意的空格
"""
1.日期格式需要转换
2.存在同一用户一天内购买多次的行为
print(df.describe())
1.用户平均每笔订单购买2.4个商品,标准差2.3,稍微有点波动,属于正常。然而75%分位数的时候,说明绝大多数订单的购买量都不多,围绕在2~3个产品左右
2.购买金额,反映出大部分订单消费金额集中在中小额,30~45左右
"""

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

# 将order_data转化成精度为月份的数据列
df['month'] = df['order_date'].astype('datetime64[M]')   # [M]:控制转换后的精度


# 用户整体消费趋势分析(按月份)
# 按月份统计产品购买数量,消费金额,消费次数,消费人数
plt.figure(figsize=(20, 15), dpi=240)
# 每月的产品购买数量
plt.subplot(221)   # 两行两列,占据第一个位置
df.groupby(by='month')['order_products'].sum().plot()   # 默认折线图
plt.title('每月的产品购买数量')

# 每月的消费金额
plt.subplot(222)
df.groupby(by='month')['order_amounts'].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()
plt.title('每月的消费人数')
"""
图一可以看出,前三个月销量非常高,而以后销量较为稳定,并且稍微呈现下降趋势
图二可以看出,依然是前三个月消费金额较高,与消费数量成正比例关系,三月份过后下降严重,并呈现下降趋势
 思考原因:1.跟月份有关,在我看来1,2,3月份处于春节前后。
         2.公司在1,2,3月份的时候是否加大了促销力度
图三可以看出,前三个月订单数在10000左右,后续月份平均消费单数在2500左右
图四可以看出,前三个月消费人数在8000~10000左右,平均消费人数在2000不到的样子
总结:所有数据显示,97年前三月消费事态异常,后续趋于常态化
"""

# 用户消费分析
# 1.用户消费金额,消费次数(产品数量)描述统计
user_grouped = df.groupby(by='user_id').sum()
# 从用户的角度:用户数23570个,每位用户平均购买了7个CD,但是中位数只有3,并且最大购买量为1033,平均值大于中位数,属于典型的右偏分布(替购买量<7的用户背锅)
# 从消费金额角度:平均用户消费106,中位数43,并且存在土豪用户13990,结合分位数和最大值来看,平均数与75%分位数几乎相等,属于典型的右偏分布,说明存在小部分用户(后面的25%)高额消费(这些用户需要给消费金额<106的用户背锅,只有这样才能使平均数维持在106左右)

# 绘制每个用户的产品的购买量与消费金额散点图
df.plot(kind='scatter', x='order_products', y='order_amounts')
# 从图中可知,用户的消费金额与购买量呈现线性的趋势,每个商品均价15左右
# 订单的极值点比较少(消费金额>1000,或者购买量>60),对于样本来说影响不大,可以忽略不计。

# 2.用户消费分布图
plt.figure(figsize=(12, 4),dpi=80)
plt.subplot(121)
plt.xlabel('每个订单的消费金额')
df['order_amounts'].plot(kind='hist', bins=50)   # bins:区间分数,影响柱子的宽度,值越大柱子越细,宽度=(列最大值-最小值)/bins

# 消费金额在100以内的订单占据了绝大多数
plt.subplot(122)
plt.xlabel('每个UID购买数量')
df.groupby(by='user_id')['order_products'].sum().plot(kind='hist', bins=50)
# 图二可知,每个用户购买数量非常小,集中在50以内
# 两幅图得知,我们的用户主要是消费金额低,并且购买小于50的用户人数占据大多数(在电商领域是非常正常的现象)

# 3.用户累积消费金额占比分析(用户的贡献度)
# 进行用户分组,取出消费金额,进行求和,排序,重置索引
user_cumsum = df.groupby('user_id')['order_amounts'].sum().sort_values().reset_index()
# 每个用户消费金额累加
user_cumsum['amount_cumsum'] = user_cumsum['order_amounts'].cumsum()
# 消费金额总值
amount_total = user_cumsum['amount_cumsum'].max()
user_cumsum['prop'] = user_cumsum.apply(lambda x: x['amount_cumsum']/amount_total,axis=1)  # 前xx名用户的总贡献率
user_cumsum['prop'].plot()
# 由图分析可知,前20000名用户贡献总金额的40%,剩余3500用户贡献了60%。(2/8原则)


# 用户消费行为
# 1.首购时间
# 用户分组,取最小值,即为首购时间
df.groupby(by='user_id')['order_date'].min().value_counts().plot()
# plt.show()
# 由图可知,首次购买的用户量在1月1号~2月10号呈明显上升趋势,后续开始逐步下降,猜测:有可能是公司产品的推广力度或者价格调整所致

# 2.最后一次购买时间
df.groupby(by='user_id')['order_date'].max().value_counts().plot()
# 大多数用户最后一次购买时间集中在前3个月,说明缺少忠诚用户
# 随着时间的推移,最后一次购买产品的用户量呈现上升趋势,猜测:这份数据选择的是前三个月消费的用户在后面18个月的跟踪记录


# 用户分层
# 1.构建RFM模型
# 透视表的使用(index:相当于groupby,values:取出数据列,aggfunc:key值必须存在于values列中,并且必须跟随有效的聚合函数)
rfm = df.pivot_table(index='user_id',
                    values=['order_products', 'order_amounts', 'order_date'],
                    aggfunc={
                        'order_date': 'max',   # 最后一次购买
                        'order_products': 'sum',   # 购买产品的总数量
                        'order_amounts': 'sum'   # 消费总金额
                    })
# 用每个用户的最后一次购买时间-日期列中的最大值,最后再转换成天数,小数保留1位
rfm['R'] = -(rfm['order_date']-rfm['order_date'].max())/np.timedelta64(1, 'D')   # 取相差的天数,保留1位小数
rfm.rename(columns={'order_products': 'F', 'order_amounts': 'M'}, inplace=True)

# 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']    # 举例:100  001
   d = {
       '111': '重要价值客户',
       '011': '重要保持客户',
       '101': '重要发展客户',
       '110': '一般价值客户',
       '001': '重要挽留客户',
       '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)

# 客户分层可视化
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模型二维可视化)


"""
新老,活跃,回流用户分析
·新用户的定义是第一次消费
·活跃用户即老客,在某一个时间窗口内有过消费
·不活跃用户则是时间窗口内没有消费过的用户
·回流用户:相当于回头客的意思
·用户回流的动作可以分为自主回流与人工回流,自主回流指玩家自己回流了,而人工回流则是人为参与导致的
"""
pivoted_counts = df.pivot_table(
   index='user_id',
   columns='month',
   values='order_dt',
   aggfunc='count'
).fillna(0)
# 由于浮点数不直观,并且需要转成是否消费过即可,用0、1表示
df_purchase = pivoted_counts.applymap(lambda x: 1 if x > 0 else 0)
"""
apply:作用于dataframe数据中的一行或者一列数据
applymap:作用于dataframe数据中的每一个元素
map:本身是series的函数,在dataframe中无法使用map函数,map函数作用于series中的每一个元素
"""
# 判断是否是新用户,活跃用户,不活跃用户,回流用户


def active_status(data):   # data:整行数据,共18列
   status = []  # 负责存储18个月的状态:unreg|new|unactive|return
   for i in range(18):
       # 本月没有消费==0
       if data[i] == 0:
           if len(status) == 0:  # 前面没有任何记录(97年1月份)
               status.append('unreg')
           else:  # 开始判断上一个状态
               if status[i-1] == 'unreg':  # 一直未消费过
                   status.append('unreg')
               else:  # new/active/unactive/return
                   status.append('unactive')  # 不管上个月是否消费过,本月都是不活跃的
       # 本月有消费==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中的列名


purchase_states = df_purchase.apply(active_status, axis=1)

# 用Nan替换unreg
purchase_states_ct = purchase_states.replace('unreg', np.NaN).apply(lambda x: pd.value_counts(x))
purchase_states_ct.T.fillna(0).plot.area()
# 前三个月可知,红色活跃用户和蓝色新用户,占比较大;四月份过后,新用户和活跃用户开始下降,并且呈现稳定的趋势;回流用户主要产生在4月过后,呈现稳定趋势,是网站的重要客户
# 回流,活跃用户的占比
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()
# 回流用户:前五个月,回流用户上涨,过后呈现下降趋势,平均维持在5%比例
# 活跃用户:前三个月活跃用户大量增长,猜测由于活动吸引来很多新用户所导致,5月份过后开始下降,平均维持在2.5%左右
# 网站运营稳定后,回流用户占比大于活跃用户

用户活跃度


# 用户购买周期(计算购买日期的时间差值)
# shift函数:将数据移动到一定的位置
order_diff = df.groupby(by='user_id').apply(lambda x: x['order_date']-x['order_date'].shift())  # 当前订单日期-上一次订单日期
(order_diff/np.timedelta64(1, 'D')).hist(bins=20)
# 得知:平均消费周期为68天
# 大多数用户消费周期低于100天,呈现典型的长尾分布,只有小部分用户消费周期在200天以上(不积极消费的用户),可以在这批用户消费后三天后进行电话回访,或者短信赠送优惠券等活动,增大消费频率

购买周期


# 用户生命周期
# 计算方式:用户最后一次购买日期-第一次购买的日期,如果差值==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(['仅消费一次', '多次消费'])
# 一半以上用户仅仅消费了一次,说明运营不利,留存率不好
print((user_life['max']-user_life['min']).describe())   # 生命周期分析
# 用户平均生命周期为134天,但是中位数==0,再次验证了大多数用户消费了一次,低质量用户,75%分位数以后的用户,生命周期>294天,属于核心用户,需要着重维持前三个月的新用户数据,所以分析的是这些用户的生命周期


# 绘制所有用户生命周期直方图+多次消费
plt.figure(figsize=(12, 6), dpi=240)
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('用户人数')
plt.show()
# 对比可知,第二幅图过滤掉了生命周期==0的用户,呈现双峰结构;虽然二图中还有一部分用户的生命周期趋于0天,但是比第一幅图好了很多,虽然进行了多次消费,但是不能长期来消费,属于普通用户,可针对性进行营销推广活动;少部分用户生命周期集中在300~500天,属于我们的忠诚客户,需要大力度维护此类客户

"""
复购率和回购率分析
复购率分析(计算方式:在自然月内,购买多次的用户在总消费人数中的占比,若客户在同一天消费了多次,也称之复购客户)
消费者有三种:消费记录>=2次的;消费总人数;本月无消费用户
复购用户:1  非复购的消费用户:0  没有消费记录的用户:Nan
"""

purchase_r = pivoted_counts.applymap(lambda x: 1 if x > 1 else np.NaN if x == 0 else 0)
# sum():求出复购用户,count():求出所有用户(无nan)
(purchase_r.sum()/purchase_r.count()).plot(figsize=(12,6))
# 前三个月复购率开始上升,后续趋于平稳维持在20%~22%之间,分析前三个月复购率低的原因,可能是因为大批新用户仅仅购买一次造成的


# 回购率分析
# 计算方式:在一个时间窗口内进行了消费,在下一个时间窗口内又进行了消费


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)
# 回购率可视化
plt.figure(figsize=(20, 4), dpi=240)
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('用户回购率和复购率对比图')
# 回购率可知,平稳后在30%左右,波动性稍微较大;复购率低于回购率,平稳后在20%左右,波动性较小;前三个月不论是回购还是复购,都呈现上升趋势,说明新用户需要一定的时间来变成复购或者回购用户
# 结合新老用户分析,新客户忠诚度远低于老客户忠诚度

# 回购人数与购物总人数
plt.subplot(212)
plt.plot(purchase_b.sum(), label='回购人数')
plt.plot(purchase_b.count(), label='购物总人数')
plt.xlabel('month')
plt.ylabel('人数')
plt.legend()
# 前三个月购物总人数远远大于回购人数,主要是因为很多新用户在1月份进行了首次购买;三个月过后,回购人数和购物总数开始稳定,回购人数稳定在1000左右,购物总人数在2000左右


回购和复购用户占比

"""
方法总结:
1.针对用户进行按照月份做整体和个体分析,主要分析维度是人数,消费金额,购买量
2.消费分析:首购时间,最后一次购买时间,相邻两个购物时间的间隔,用户分层(RFM模型+数据透视表),分析维度主要是新用户,活跃用户,不活跃用户流失分析,回流用户占比
3.复购率和回购率进行分析
"""

用户消费行为数据行为(源码自用)的评论 (共 条)

分享到微博请遵守国家法律