单车案例

本文是对单车案例的一个总结,主要是根据用户的消费记录,深入分析用户消费行为,建立RFM模型,进行用户分层,发掘高价值用户并进行针对性管理和维护,实现用户运营精细化。

目录

  • 分析背景
  • 分析思路
  • 分析过程
  • 总结

一、分析背景

数据来源于网上,是用户在一家单车网上18个月的消费记录

二、分析思路

单车案例分析思路.png

本文为可视化查看方便,使用Matplotlib来画图呈现,也可以导出数据为csv,然后使用powerBI等BI工具画图查看

三、分析过程

1. 数据的导入观察与清洗

  • 数据导入和观察
import numpy as np
import pandas as pd
import datetime
df = pd.read_csv('bicycle_master.txt')
df.head()
1.PNG
df.info()
2.PNG

从上面可以看出数据

  • 数据以table键作为分隔符,需要进行分隔符的解析
    可以使用pd.read_csv中的参数进行解析,sep='\s+',可以将table键和多个空格当成一样的分隔符
  • 数据是没有列名的,需要提前指定列名称
    使用 pd.read_csv()中names可以在读取时指定列名称
  • 日期列需要解析为日期格式
    可以使用pd.read_csv()中的parse_dates 解析指定的列为日期格式
  • 需要提取月的信息,后续需要By月进行统计
    月信息提取有两种方法:df.astype() 强制类型转换和pd.to_datetime 指定类型日期转换两种方式,这里使用的df.astype()进行强制转换,datetime64[M],表示转换为每月的第一天了,同理设置为[Y]就是每年的1月1日
    重新读取如下
cols = ['user_id','date','quantity','money']
df = pd.read_table('bicycle_master.txt', sep='\s+', names=cols, parse_dates=[1])
df['month'] = df.date.values.astype('datetime64[M]')
df['year'] = df.date.values.astype('datetime64[Y]')
df.head()
3.PNG
df.info()
4.PNG

pd.to_datetime()应用如下


5.PNG
  • 查看数据的缺失值和统计性描述情况
df.isnull().sum()
6.PNG
df.describe()
7.PNG
  • 数据中没有缺失值
  • 可以看出大部分订单只购买了少量商品(均值2.4, 3/4分位数是3),有个别极值的干扰
  • 用户的消费金额较稳定,平均消费约35,75%用户消费43.7元,中位数25

2. 用户消费趋势的分析(按月)
1) 每月的消费总金额,对消费金额求和:np.sum()
2)每月的产品购买数量,对消费数量求和:np.sum()
3)每月的订单量,对订单进行计数:pd.count()
4) 每月的消费人数,对user_id进行去重后计数,pd.nunique()
5) 每月的用户的平均消费金额,对消费金额的取平均,np.mean()

df_month = df.groupby('month').agg({'user_id':'nunique', 'date':'count', 'quantity':np.sum, 'money':[np.sum, np.mean]})
df_month
8.PNG

可以看出,列名称需要重新命名,且要重置索引

df_month.columns = ['user_count','order_count','quantity_sum','monetary_sum','monetary_mean']
df_month.reset_index(inplace=True)
df_month
9.PNG

6) 每月用户平均消费次数的趋势,每月的订单数/每月用户人数

df_month['order_avguser'] = df_month['order_count'] / df_month['user_count']
df_month
捕获10.PNG

7)画图,可视化查看

  • 月消费总金额和月购买产品数量趋势
# 画图展示
import matplotlib.pyplot as plt
%matplotlib inline
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
x = df_month['month']
y = df_month['monetary_sum']
y2 = df_month['quantity_sum']

plt.plot(x, y, 'm')
plt.ylabel('消费金额')
plt.title('月消费总金额趋势')
plt.legend()
11.PNG
plt.plot(x, y2)
plt.ylabel('产品购买数量')
plt.title('月产品购买数量')
plt.legend()
12.PNG

从月消费总金额和购买数量查看,在前三个月呈上升趋势,第三个月达到顶峰,后续每月消费逐渐稳定,趋于轻微下降趋势

  • 月消费人数和订单数查看
# 月消费人数和订单数
x = df_month['month']
y3= df_month['user_count']
y4 = df_month['order_count']

plt.plot(x, y3, 'y', label='消费人数')
plt.plot(x, y4, 'c', label='订单数')
plt.legend()
13.PNG
  1. 前三个月的消费人数和订单数呈上升趋势,前三个月的订单数在1000左右,第三个月达到顶峰,后续趋于平稳,呈轻微下降趋势,在2500左右
  2. 每月消费人数低于订单数,但差异不大
  3. 前三个月每月的消费人数在8000-10000之间,后续每月平均消费人数2000不到
  • 平均消费金额和消费次数查看
x = df_month['month']
y5 = df_month['monetary_mean']
y6 = df_month['order_avguser']

plt.subplot(2,1,1)
plt.plot(x, y5, 'r', label='平均消费金额')
plt.legend()
plt.subplot(2,1,2)
plt.plot(x, y6, 'b', label='平均订单数')
plt.legend()
14.PNG
  1. 用户的平均消费金额呈波动上升在11月达到顶峰后,趋于波动下降,整体差异不大,在33-41之间
  2. 平均订单数在前三月从1.1上升到1.3,后续趋于稳定在1.3-1.4区间内
    3. 用户个体分析
    1) 用户消费金额和消费数量的分布
    2) 用户消费金额的分布,按消费金额区间统计用户人数
    3) 用户消费次数的分布,按消费次数分布区间统计用户人数
    4) 用户累计消费金额占比,百分之多少的用户贡献了百分之多少的消费金额

1) 用户消费金额和消费数量的分布

df_user = df.groupby('user_id').agg({'quantity':np.sum,'money':np.sum, 'date':'count'}).reset_index()
df_user.rename(columns={'date':'order_count'}, inplace=True)
df_user.describe()
15.PNG
  • 可以看出消费数量quantity消费数量和消费金额Money 的平均值是16,240,中位数是3,43;说明有个别用户购买了数量较多的产品
  • 散点图查看
df_user.plot.scatter('money', 'quantity', alpha=0.5, title='用户的消费金额和消费数量')
16.PNG

从上图中看出受极值的影响,大部分的用户购买金额在4000以下,有12位用户累计购金额大于4000

df_user1 = df_user[df_user['money'] < 4000]
plt.scatter(df_user1['money'],df_user1['quantity'], c='c', alpha=0.5)
plt.title('用户的消费金额和消费数量散点图')
plt.xlabel('消费金额')
plt.ylabel('消费数量')
plt.legend()
17.PNG

2) 用户消费金额的分布,按消费金额区间统计用户人数
将用户消费金额分组统计,查看在哪个区间内用户人数最多;使用pd.cut可以自动将数据划分区间或者按指定的数据列表进行划分,pd.cut划分时每组包含右边的数值,不包含左边的数值

bi = [x for x in range(0, int(df_user.money.max())+50, 50)]
df_user['money_b'] = pd.cut(df_user['money'], bins=bi, labels=bi[1:])
df_user.head()
18.PNG

可以看出在对用户消费金额分组后,金额标签一栏存在缺失值,因为我们的划分是从0开始的,pd.cut划分含右不含左,所以0的数值会显示为缺失


19.PNG

金额为0 的数据是一些异常数据,可以删除这些数据后再进行统计

df_user_2 = df_user.dropna(axis=0)
df_user_22 = df_user_2.groupby('money_b').agg({'user_id':'count'}).reset_index()
df_user_22['money_label'] = df_user_22.money_b.astype('int')
df_user_22.sort_values('money_label',inplace=True)
df_user_22.plot.line('money_label','user_id')
20.PNG
  • 直接使用分组后的数据画图时,bar图无法直观呈现,暂时使用折线图,可以出当消费金额大于2000时,用户人数趋向0,有极值干扰。
  • 使用切比雪夫定理过滤掉极值的影响

切比雪夫定理

  1. 所有数据中,至少有3/4(或75%)的数据位于[平均数]2个标准差范围内。
  2. 所有数据中,至少有8/9(或88.9%)的数据位于平均数3个标准差范围内。
  3. 所有数据中,至少有24/25(或96%)的数据位于平均数5个标准差范围内 [2] 。
    金额平均值的5个标准差:(mean = 106 ,std = 241)106+5*241=1311


    21.PNG

    过滤前:280组数据,过滤后只有27组数据

#画图
x = df_user_23['money_label']
y = df_user_23['user_id']
x1 = range(len(df_user_23['money_label']))

plt.bar(x1, y, width=0.8)
plt.xlabel('消费金额')
plt.ylabel('用户量')
plt.xticks(x1,df_user_23['money_label'], fontsize=9, rotation=90)
plt.title('用户消费金额频次')
22.PNG

从直方图可知,用户消费金额,绝大部分呈现集中趋势

3) 用户消费次数的分布,按消费次数分布区间统计用户人数
按照如上同样的方法进行划分小组,再分组统计

order_count_label = [x for x in range(0, int((df_user['order_count']).max()+1))]
df_user['order_count_label'] = pd.cut(df_user['order_count'],bins=order_count_label, labels=order_count_label[1:])
#分组后的标签为目录格式,需要转换为整型数据,以便后面画图使用
df_user['order_count_label'] = df_user.order_count_label.astype('int')
#按消费次数分组求和
df_user_order = df_user.groupby('order_count_label').agg({'user_id':'count'}).reset_index().sort_values('order_count_label')
df_user_order.rename(columns={'user_id':'user_count'}, inplace=True)
df_user_order.plot.line('order_count_label','user_count')
23.PNG

从上图可以看出,消费次数也受极值的影响,同样使用切比雪夫定理进行过滤。

# 3+5*5
df_user_order_28 = df_user_order[df_user_order['order_count_label']<28]
x = range(len(df_user_order_28['order_count_label']))
x1 = df_user_order_28['order_count_label']
y = df_user_order_28['user_count']

# 画直方图时注意,可以先使用区间的长度按顺序来画图,再使用区间的标签映射x轴原先使用的顺序
plt.bar(x, y, width=0.8, color='c')
plt.xlabel('消费次数')
plt.ylabel('用户量')
plt.xticks(x, x1)
plt.title('用户消费次数频次')
plt.legend()
24.PNG

可以看出用户消费次数集中在一次
4) 用户累计消费金额曲线
累计消费金额可以使用pd.consum()来计算

# 累计消费金额曲线 
df_user_cumsum_money = df_user.sort_values('money').money.cumsum() / df_user.sort_values('money').money.sum()
# 重置索引,和重命名列
df_user_cumsum_money = df_user_cumsum_money.reset_index().rename(columns={'index':'user_id','money':'money'})

# 画图准备
y3 = df_user_cumsum_money['money']
x3 = range(df_user_cumsum_money.shape[0])
# 求中位数
y4 = [np.median(y3) for x in x3]

plt.plot(x3, y3, 'm')
plt.plot(x3, y4, 'r--', label='人数中位数')
plt.xlabel('消费人次')
plt.ylabel('消费金额占比')
plt.title('累积消费金额曲线')
plt.legend()

25.PNG

26.PNG

由上可以看出,按照用户消费金额进行升序排序,50%的用户仅贡献10%的消费金额,15%的用户贡献了60%的消费金额
4. 用户消费行为

  1. 首购时间分布
  2. 最后一次购买时间分布
  3. 每月新客占比
  4. 用户RFM模型分层
  5. 新、老、活跃、流失、回流用户分布情况
  6. 用户购买周期分布,按两次购买时间的间隔
  7. 用户生命周期分布,首购和最后一次购买消费时间

1)用户首购分布
即每个用户购买时间的最小值,再根据时间统计用户人数,除了使用分组统计groupby外,series的df.date.value_counts()也可以实现统计每个日期的人数

# 用户首购
df_user_a = df.groupby('user_id').agg({'date':np.min}).date.value_counts().reset_index()
df_user_a = df_user_a.rename(columns={'index':'first_date', 'date':'user_count'})
print(df_user_a.head())
df_user_a.plot.line('first_date','user_count', title='用户首购分布')
27.PNG

可以看到用户的首购人数在1月到2月初是上升的,从2月中旬后就开始逐渐下降
2) 用户最后一次消费,方法同上

# 用户最后一次消费
df_user_b = df.groupby('user_id').agg({'date':np.max}).date.value_counts().reset_index()
df_user_b.rename(columns={'index':'last_date','date':'user_count'}, inplace=True)
print(df_user_b.head())
df_user_b.plot.line('last_date', 'user_count', title='用户最后一次购买分布')
28.PNG
  • 在3月份后,用户最后一次购买分布断崖式下跌,可以理解用户流失也在3月份断崖式下跌;
  • 一开始用户迅猛增长数量比较多流失的也比较多,后面没有用户 用户最后一次购买的分布比第一次分布广
  • 大部分最后一次购买,集中在前三个月,说明很多用户购买了一次后就不再进行购买
  • 随着时间的递增,最后一次购买数量也在递增,消费呈现流失上升的状况(这也是正常,随着时间的增长,可能运营没跟上,或者用户忠诚度下降了)

3)统计多少用户仅消费一次,新老客户的占比

# 查看多少用户只购买了一次
df_user_d = df.groupby('user_id').date.agg([np.min, np.max])
df_user_d2 = (df_user_d['amax'] == df_user_d['amin']).value_counts()
df_user_d2
29.PNG
  • 将近一半的用户仅购买了一次
df_user_d2 = df_user_d2.reset_index()
df_user_d2.columns = ['客户', 'count']
df_user_d2.replace({'客户':{0:'老客户',1:'新客户'}}, inplace=True)
df_user_d2.set_index('客户',inplace=True)
df_user_d2.plot.pie('count',autopct='%.1f%%', radius = 1, startangle = 90, title='新老客户消费比', label='客户')
30.PNG
  • 每月的新客比
    按月分组下的userid分组,求每月的最早购买日期和最晚消费日期
# 每月新客比,只在每个月内看用户是否复购,如果没有复购,算作新客
df_user_e = df.groupby(['month','user_id']).date.agg([np.min, np.max])
# 当首购时间和末购时间相同则为新客户
df_user_e['new'] = (df_user_e['amin'] == df_user_e['amax'])
df_user_e = df_user_e.reset_index().groupby('month').new.value_counts()
df_user_e2 = pd.DataFrame(df_user_e)

# 多重索引的处理,先提取原来的索引数据,再删除掉多重索引
df_user_e2['flag'] = df_user_e2.index.droplevel('month')
df_user_e2['month1'] = df_user_e2.index.droplevel('new')
df_user_e2.index = df_user_e2.index.droplevel()
df_user_e3 = df_user_e2.reset_index(drop=True)

#标签的映射
df_user_e3['客户'] = df_user_e3.flag.map({True:'新客户', False:'老客户'})
# 数据合并,将新客户和老客户分列展示
df_user_e4 = pd.merge(df_user_e3[df_user_e3['客户'] == '新客户'], df_user_e3[df_user_e3['客户'] == '老客户'],how='outer',on='month1')
# 计算新客比
df_user_e4['新客比'] = df_user_e4.apply(lambda x: x.new_x/(x.new_x+x.new_y), axis=1)
# 画图
df_user_e4.plot.line(x='month1', y='新客比', title='每月新客比')
31.PNG

柱状图呈现每月新客比

import matplotlib.pyplot as plt
%matplotlib inline

x = df_user_e4['month1'].astype('str')
x1 = np.arange(len(x))
y1 = df_user_e4['new_x']
y2 = df_user_e4['new_y']

plt.bar(x1, y1, color='m', width=0.25, label='新客户')
plt.bar(x1+0.25, y2, color='b', width=0.25, label='老客户')
plt.xticks(x1, x, rotation=-45)
plt.legend()
32.PNG

可以看到前三个月新客占比大于0.85,后面每个月新客占比趋于稳定,说明大部分用户每个月只购买一次

4)RFM模型


RFM模型.PNG
# 用户分层:RFM,R最后一次消费,F:消费次数,购买数量,M:消费金额
rfm = df.groupby('user_id').agg({'date':np.max, 'quantity':np.sum, 'money':np.sum}).reset_index()

#最近一次消费时间距最近的天数,因案例中的时间过于远,所以采用案例中的最大时间做参考
import datetime
# -(rfm['date'] -np.datetime64('today','D')) / np.timedelta64(1, 'D')
rfm['R'] = (rfm['date'] -rfm.date.max()) / np.timedelta64(1, 'D')
# 列名重命名
rfm = rfm.rename(columns={'quantity':'F','money':'M'})
rfm.head()
33.PNG
# 归一化,将RFM三个参数处理为中心化,零均值化,方便后续数据分层
rfm1 = rfm[['R','F','M']].apply(lambda x:x - x.mean())
rfm1.head()
34.PNG
# 标签映射
level = rfm1.applymap(lambda x :'1' if x >=0 else '0')
label = level.R + level.F + level.M
d = {
        '111':'重要价值客户',
        '011':'重要唤回客户',
        '101':'重要深耕客户',
        '001':'重要挽留客户',
        '110':'潜力客户',
        '100':'新客户',
        '010':'一般维持客户',
        '000':'流失客户' 
    }
rfm['level'] = label.map(d)
rfm['color'] = rfm.level.apply(lambda x :'重要价值客户' if x=='重要价值客户' else '非重要价值客户')
rfm.head()
35.PNG

画图查看,主要查看重要价值客户和非重要价值客户的分布

import matplotlib.pyplot as plt
%matplotlib inline
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

plt.scatter(rfm[rfm['color']=='重要价值客户'].F, rfm[rfm['color']=='重要价值客户'].R, c = 'c',label='重要', alpha=0.5)
plt.scatter(rfm[rfm['color']=='非重要价值客户'].F, rfm[rfm['color']=='非重要价值客户'].R, c = 'm',alpha=0.5,label='非重要')

plt.xlabel('F')
plt.ylabel('R')
plt.title('客户标签分布')
plt.legend()
36.PNG

49.PNG
  • 从RFM 分层可知,大部分用户是重要保持客户,但是这是由于极值的影响
  • 所以 RFM 的划分标准应该以业务为准,也可以通过切比雪夫去除极值后求均值,并且 RFM 的各个划分标准可以都不一样

5)新、老、活跃、流失、回流用户分布情况
按每个用户每个月是否有消费进行判断

# 用户生命周期, userid为索引,月为列,求每月的消费次数,这里缺失值使用0填充
pivot_count = pd.pivot_table(df, index='user_id', columns='month', values='date',
          aggfunc='count').fillna(0)
# 有消费为1,无消费为0
df_purchase = pivot_count.applymap(lambda x:1 if x>0 else 0)
df_purchase.head()
37.PNG

判断用户分布状况

  • 新用户:之前从没购买,也就是之前月的购买次数为0
  • 活跃用户:这个周期和上个周期都购买了,且不是新客户
  • 回流用户:上个周期没买,这个周期买了,且不是新客户
  • 流失用户:本周期没有购买,且不是新客户
  • 未注册:从未购买过
  • return:回流
  • new:新客
  • unreg:未注册
  • active:活跃
  • unactive:流失
def status_m(data):
    status = []
    for i in range(18):
        if len(status) ==0:
            if data[i] == 1:
                status.append('new')
            else:
                status.append('unreg')

        else:
            if data[i] ==1:
                if status[i-1] == 'unreg':
                    status.append('new')
                elif status[i-1] == 'unactive':
                    status.append('return')
                else:
                    status.append('active')
            else:
                if status[i-1] == 'unreg':
                    status.append('unreg')
                else:
                    status.append('unactive')

    return status

p1 = df_purchase.apply(status_m, axis=1)

#将未注册的用户使用np.nan替换,从未购买,这样count计算人数时不会计算在内
p2 = p1.replace('unreg',np.nan).apply(lambda x :pd.value_counts(x))
#填充np.nan为0,方便后续画图
p_p=  p2.fillna(0).T
p_p.head()
38.PNG

画图查看用户分布情况

from matplotlib import pyplot as plt
label = ['active','new','return','unactive']
y1 = p_p.active
y2 = p_p.new
y3 = p_p['return']
y4 = p_p.unactive
plt.stackplot(p_p.index, y1, y2,y3, y4, labels=['active','new','return','unactive'],alpha=0.4)
plt.title('用户分层')
plt.legend()
39.PNG

可以看出前三个月新增用户和回流用户达到最大,3月之后几乎无新客户,回流用户和活跃用处于基本稳定的状态,不活跃用户3月份后占了大部分

  • 计算用户分布的占比
# 求出所有用户的占比
p2.fillna(0).apply(lambda x : x/x.sum(), axis=0).T.head()
40.PNG
  • 前三个月有大量新用户涌入,新用户占比很高,而后面几个月不活跃用户占比非常高,普遍在90%以上。
  • 活跃用户,持续消费的用户,对应的是消费运营的质量。
  • 回流用户,之前不消费,本月才消费,对应的是唤回运营。
  • 不活跃用户,对应的是流失

6)用户购买周期的分布

# 用户购买周期
order_diff = df.groupby('user_id').apply(lambda x :x.date - x.date.shift())
# 将间隔日期转换为数值
order_diff_i = order_diff / np.timedelta64(1,'D')
order_diff_i.describe()
# count    46089.000000
# mean        68.973768
# std         91.033032
# min          0.000000
# 25%         10.000000
# 50%         31.000000
# 75%         89.000000
# max        533.000000
# Name: date, dtype: float64

# 将间隔日期分组切割
ls = [i for i in range(0, int(order_diff_i.max())+10,10)]
order_diff_i1 = pd.cut(order_diff_i, bins=ls, labels=ls[1:]).fillna(10)
# 按切割后的每组统计人数
order_diff_i2 = order_diff_i1.reset_index().groupby('date').agg({'date':'count'})
order_diff_i2.columns =['count']
# 重置索引和强制转换日期天数目录为整型
data = order_diff_i2.reset_index()

# 画图,因为bar画图在matplot呈现不直观,使用折线图
y = data['count']
x1 = list(data.date.astype(int))
plt.plot(x1, y)
plt.title('用户购买周期')
plt.xlabel('天数')
plt.ylabel('人数')
plt.legend()
42.PNG
  • 订单周期呈指数分布
  • 用户的平均购买周期是68天
  • 绝大部分用户的购买周期都低于100天

7)用户生命周期分布,第一次购买和最后一次购买的差值

# 用户生命周期分布
user_l1 = df.groupby('user_id').agg({'date':[np.max, np.min]}).reset_index()
user_l1.columns = ['user_id', 'max_date', 'min_date']
# 时间格式转为数字类型
user_l1['life'] = (user_l1['max_date'] - user_l1['min_date']) / np.timedelta64(1,'D')
user_l1['life'].describe()
# count    23570.000000
# mean       134.871956
# std        180.574109
# min          0.000000
# 25%          0.000000
# 50%          0.000000
# 75%        294.000000
# max        544.000000
# Name: life, dtype: float64
# 生命周期的均值为134,中位数为0

user_l1[user_l1['life'] >0 ].describe()
#   user_id life
# count 11516.000000    11516.000000
# mean  11718.596301    276.044807
# std   6822.464439 166.633990
# min   3.000000    1.000000
# 25%   5747.750000 117.000000
# 50%   11778.000000    302.000000
# 75%   17632.500000    429.000000
# max   23570.000000    544.000000
# 过滤生命周期为0 的数据后,平均购买周期为276天,中位数302

# 列表生成周期的分布标签,切割周期数据后再分组统计
ls = [i for i in range(0, int(user_l1.life.max()+10),10)]
# 填充缺失值为最小值
user_l1['lf_label'] = pd.cut(user_l1['life'], bins=ls, labels=ls[1:]).fillna(10)
# 分组统计
user_l2 = user_l1.groupby('lf_label').agg({'user_id':'count'}).reset_index()

# 周期的目录格式转为数字格式,方便画图统计
user_l2['lf_label'] = user_l2.lf_label.astype(int)
# 画图统计
plt.plot(user_l2['lf_label'], user_l2['user_id'])
plt.xlabel('天数')
plt.ylabel('人数')
plt.title('用户生命周期')
plt.legend()
43.PNG
  • 受只购买一次的用户影响较大,过滤生命周期为0 的数据后,平均购买周期为276天,中位数302
  • 用户人均生命周期134天,中位数仅为0天
# 筛选用户生命周期大于1次的数据
user_l3 = user_l2[user_l2['lf_label']>10]

plt.plot(user_l3['lf_label'], user_l3['user_id'])
plt.xlabel('天数')
plt.ylabel('人数')
plt.title('用户生命周期')
plt.legend()
44.PNG

5. 复购率和回购率
复购率:自然月内,购买多次的用户占比(即,购买了两次以上)
回购率:在本周期内购买了且下个周期也购买了的用户占本周期总用户的比例,曾经购买过的用户在某一时期的再次购买的占比(可能是在三个月内)

1)回购率计算

# 回购率计算
df_um = pd.pivot_table(df, values='money', index='user_id', columns='month', aggfunc='count')
df_um.head()
45.PNG

回购率函数的定义:
本月有购买且下个月也有购买时,则为回购:1
本月有购买下个月没有购买,未回购:0
本月没有购买应为空值:np.nan,这样计算时count不会计算在内
最后一个月的回购应为np.nan

# 是否回购的判断
def huigou(data):
    status = []
    for i in range(len(data)-1):
        if data[i] >= 1:
            if data[i+1] >= 1:
                status.append(1)
            else:
                status.append(0)
        else:
            status.append(np.nan)
    status.append(np.nan)
    return status

df_um1 = df_um.apply(lambda x :huigou(x), axis=1)

# 每月的回购率
# df_um1.sum() 每月回购人数统计
# df_um1.count() 每月购买总人数统计
hg = df_um1.sum()/df_um1.count()
# 重置索引&重命名列
hg1 = hg.reset_index()
hg1.columns = ['month','hg_rate']
hg1
46.PNG
# 回购率曲线绘制
import matplotlib.pyplot as plt
%matplotlib inline
plt.rcParams['font.sans-serif'] = 'SimHei'
plt.rcParams['axes.unicode_minus'] =False

plt.plot(hg1['month'], hg1['hg_rate'])
plt.ylabel('回购率')
plt.title('回购曲线')
plt.legend()
47.PNG

2)复购率计算

# 每月购买的人数:df_um.count()
# 每月复购的人数:df_um.applymap(lambda x : 1 if x>1 else 0).sum()
df_fg = df_um.applymap(lambda x : 1 if x>1 else 0).sum() / df_um.count()
# 索引重置&列重命名
df_fg1 = df_fg.reset_index()
df_fg1.columns = ['month', 'fg_rate']

plt.plot(df_fg1['month'], df_fg1['fg_rate'])
plt.ylabel('复购率')
plt.title('复购率曲线')
plt.legend()
48.PNG

小结:

  • 回购率稳定在30%左右,前三个月有大量新用户涌入,导致回购率较低
  • 复购率稳定在20%左右

四、总结

  1. 用户消费趋势分析
  • 前三个月有大量的新用户涌入,消费金额、产品消费数量
    、消费人数、订单量均达到最高峰,后续每个月较为稳定且呈轻微下降趋势
  • 平均消费金额呈波动上升在11月达到顶峰后,趋于波动下降,整体差异不大,在33-41之间
  • 平均订单数在前三月从1.1上升到1.3,后续趋于稳定在1.3-1.4区间内
  1. 用户个体消费
  • 个别用户购买了大量的产品,拉高了平均消费金额,绝大部分的用户购买金额在200以下,有12位用户累计购金额大于4000;
  • 绝大部分的用户只消费了一次
  • 用户累计消费金额曲线看出,50%的用户仅贡献10%的消费金额,15%的用户贡献了60%的消费金额,符合二八法则
  1. 用户消费行为
  • 首购和最后一次购买时间集中在前三个月,说明很多用户购买一次后就不买了,而且用户最后一次的购买时间在随时间上升,消费呈现流失状态
  • 新老客户看,有一半的用户仅消费一次;每月新客查看,1月的新客占比达到90%以上,后续有所下降,3月后开始维持在81%左右
  • RFM模型看,在8种客户中,重要价值客户的消费频次和消费金额最高,人数排在第二位
  • 用户分布情况来看,新用户从第4月份以后没有新增;活跃用户有所下降;回流用户数量趋于稳定,每月1000多。流失/不活跃用户,数量非常多,基本上每月都在20000以上。
  • 用户购买周期方面,用户的平均购买周期是68天,绝大部分用户的购买周期都低于100天
  • 用户生命周期方面,受只购买一次的用户影响较大,排除这部分后,平均购买周期为276天,中位数302
  1. 复购率和回购率方面,复购率稳定在20%左右,回购率稳定在30%左右,前3个月因为有大量新用户涌入,而这批用户只购买了一次,所以导致复购率和回购率都比较低。

你可能感兴趣的:(单车案例)