单车案例分析报告

项目背景:
对一家公司18个月的销售记录进行了用户消费行为的相关分析。

分析流程如下:


分析流程.png

0.数据预处理

import pandas as pd  
import numpy as np 

columns=['user_id','order_dt','order_products','order_amount']
df=pd.read_table(r'Data\单车项目\bicycle_master.txt',names=columns, sep='\s+')
# 数据清洗
df['order_dt']=pd.to_datetime(df.order_dt,format="%Y%m%d") 
df['month']=df.order_dt.astype('datetime64[M]') #转换类型为datetime64[M],默认就会是每月的第一天了

数据结果如下:


数据导入
  • user_id:用户ID
  • order_dt:购买日期
  • order_products:购买产品数
  • order_amount:购买金额

1.进行用户消费趋势的分析(按月)

  • 以下维度展开
    • 每月的消费总金额
    • 每月的消费次数
    • 每月的产品购买量
    • 每月的消费人数
grouped_month = df.groupby('month')
grouped_month_info = grouped_month[['order_amount','user_id','order_products']].agg({'order_amount':'sum', 'user_id': 'count', 'order_products':sum})
grouped_month_info.rename(columns = {'order_amount':'消费金额', 'user_id': '消费次数', 'order_products': '产品购买量'}, inplace=True)
grouped_month_info['消费人数'] = grouped_month['user_id'].unique().map(lambda x:len(x)) #nunique()也可,去重统计user_id
grouped_month_info = grouped_month_info.reset_index()
grouped_month_info['month'] = grouped_month_info['month'].astype(str)

月统计表

利用tableau构造dashboard,观看整体趋势。


月销售额、销售次数、产品购买量、消费人数图

从上图可看出:

  • 消费额、产品销量、消费人次和消费次数折线图基本类似,都是前三个月较高,到底三个月达到顶峰,随后骤降在较低值趋于稳定,但整体仍有轻微下降趋势。
  • 初步猜测前三个月存在某种推广营销活动,使大量流量涌入,但后续并没有进行有效的后续活动导致用户粘性降低,留存率不高。

2.用户个体消费分析

  • 用户消费金额和产品购买量的散点图
  • 用户消费金额的分布图
  • 用户累计消费金额占比(百分之多少的用户占了百分之多少的消费额)
# 用户消费金额和产品购买量的散点图
grouped_user=df.groupby('user_id').sum()
grouped_user_sum_order_amount = grouped_user.sum().order_amount
# 用户消费金额的分布图
grouped_user_sum_order_amount_lst = [i for i in range(0,int(grouped_user_sum_order_amount.max())+50,50)]
grouped_user_sum_order_amount = pd.cut(grouped_user_sum_order_amount, bins=grouped_user_sum_order_amount_lst,labels = grouped_user_sum_order_amount_lst[1:])
# 累计消费金额表
user_cumsum=grouped_user.sum().sort_values('order_amount').apply(lambda x:x.cumsum()/x.sum())
user_cumsum.reset_index().order_amount
章节2一系列图
  • 销售额与消费产品散点图有极值影响,所以我们去除了总金额大于4000的散点图,由修正后的散点图可知,用户消费金额与产品购买量几乎成线性关系,购买的商品越多,消费金额越大。
  • 从直方图可知,用户消费金额,绝大部分呈现集中趋势,小部分异常值干扰了判断,可以使用过滤操作排除异常
    使用切比雪夫定理过滤掉异常值,因为切比雪夫定理说明,95%的数据都分布在5个标准差之内,剩下5%的极值就不要了
    order_amount (mean = 106 ,std = 241) mean+5std = 1311
  • 按照用户消费金额进行升序排序,由图可以知道50%的用户仅贡献了11%的消费额度,而排名前5000的用户就贡献了60%的消费额度

3.用户消费行为

  • 3.1 用户第一次消费(首购)
  • 3.1 用户最后一次消费
  • 3.2 新老客户消费比
    • 多少用户仅消费一次
    • 每月新客占比
  • 3.3 用户分层
    • 3.3.1 RFM模型
    • 3.3.2 新、老、活跃、回流、流失
  • 3.4 用户购买周期(按订单)
    • 用户消费周期分布
  • 3.5 用户生命周期(按第一次和最后一次消费)
    • 用户生命周期分布

3.1 首购分布&最后一次购买分布

# 首购分布
grouped_user_min = grouped_user.min().order_dt.value_counts().reset_index().rename(columns={'index':'first_date'})
grouped_user_min['first_date'] =grouped_user_min['first_date'].astype(str)
# 最后一次购买分布
grouped_user_max = grouped_user.max().order_dt.value_counts().reset_index().rename(columns={'index':'last_date'})
grouped_user_max['last_date'] =grouped_user_max['last_date'].astype(str)
首购分布&最后一次购买分布
  • 首购
    • 用户第一次购买的分布详情,主要集中在前三个月。其中,在2月有剧烈的波动。
  • 最后一次购买
    • 断崖式下跌:可以理解用户流失比例基本一致,一开始用户迅猛增长数量比较多流失的也比较多,后面没有用户大面积流失,处于平衡状态
    • 用户最后一次购买的分布比首购分布广
    • 大部分最后一次购买,集中在前三个月,说明很多用户购买了一次后就不再进行购买
    • 随着时间的递增,最后一次购买数量也在递增,消费呈现流失上升的状况(随着时间的增长,可能运营没有跟上用户的变化,或者用户被别的竞争公司吸引走了)

3.2 新老客户消费比

3.2.1多少用户仅消费一次

# 得到第一次和最后一次消费情况,如果 min、max 日期相同,说明只消费了一次
user_life=grouped_user.order_dt.agg(['min','max'])
user_life_1 = (user_life['min']==user_life['max']).value_counts()
消费1次与n次用户统计表

将上表进行可视化展示:

import warnings
warnings.filterwarnings('ignore')
%matplotlib inline
plt.figure(figsize=(4,4),dpi=100)
labels = user_life_1.index
x = user_life_1.values
plt.pie(x,labels=labels,autopct='%.0f%%',colors = 'br', startangle = 90 )
plt.legend()
plt.show()
消费1次与n次用户占比

有一半左右的用户,只消费了一次。

3.2.2 每月新客占比

grouped_um = df.groupby(['month','user_id']).order_dt.agg(["min","max"])     #按月分组下的userid分组,求每月的最早购买日期和最晚消费日期
grouped_um["new"] = (grouped_um["min"] == grouped_um["max"] )                # 新增列 True 为 新用户

a = grouped_um.reset_index().groupby("month").new.value_counts()
a = a.unstack() # 多重索引进行unstack()顺时针转化
# 并列柱状图可视化
a.columns = ['老用户','新用户']
plt.figure(figsize=(8,8),dpi=100)
x1 = np.arange(0,18,1)
x = a.index.date
y1 = a.新用户
y2 = a.老用户

plt.bar(x1, y1, color='g', width=0.25, label='新客户')
plt.bar(x1+0.25, y2, color='b', width=0.25, label='老客户')
plt.xticks(x1, x, rotation=-45)
plt.legend()
每月新老客户对比

由上图可以看出前三个月的新客户增长迅速,接下来的13个月趋于稳定,老用户也是在接下来的13个月内稳定不增,基本可以阐述多数用户只购买了一次该产品。

3.3 用户分层

3.3.1 RFM模型

# 画 RFM,先对原始数据进行透视
rfm=df.pivot_table(index='user_id',
                  values=['order_products','order_amount','order_dt'],
                  aggfunc={'order_dt':'max',       # 最近的时间
                          'order_amount':'sum',    # 总费用
                          'order_products':'sum'}) # 总产品数
# 得到最近一次消费,一般是计算距离当天最近一次消费的时间间隔,这里因为时间太久远,就用表格中order_dt的max值
rfm['R']= (rfm.order_dt - rfm.order_dt.max())/np.timedelta64(1,'D')
# 重命名,也就是 R:最后一次消费距今天数,M:消费总金额 ,F:消费总产品数
rfm.rename(columns={'order_products':"F",'order_amount':'M'},inplace=True)

def rfm_func(x):
    level=x.apply(lambda x:'1' if x>=0 else '0')   
    #level=x.map(lambda x:'1' if x>=0 else '0')也可以
    # level 的类型是 pd.Series,index 是 R、F、M
    label=level.R + level.F + level.M
    d={
        # R 为1 表示比均值大,购买时间近,F为1 表示 消费金额比较多,M 为1 表示消费频次比较多,所以是重要价值客户
        '111':'重要价值客户',
        '011':'重要保持客户',
        '101':'重要发展客户',
        '001':'重要挽留客户',
        '110':'一般价值客户',
        '010':'一般保持客户',
        '100':'一般发展客户',
        '000':'一般挽留客户',
    }
    result=d[label]
    return result

#  axis=1,传递一行,然后匹配返回一个值
rfm['label']=rfm[['R','F','M']].apply(lambda x:x-x.mean()).apply(rfm_func,axis=1)
# 为了简化用户分层,将8分层简化为2分层
rfm.loc[rfm.label == '重要价值客户','color'] = '重要价值客户'
rfm.loc[rfm.label != '重要价值客户','color'] = '非重要价值客户'

rfm表如下:


rfm

图表看出重要价值客户和非重要价值客户的分布:


output_59_1.png
# 精确分层下的用户总数
rfm.groupby('label').count()
各分层人数
  • 从RFM 分层可知,大部分用户是一般挽留客户,但是这是由于极值的影响,所以 RFM 的划分标准应该以业务为准,也可以通过切比雪夫去除极值后求均值,并且 RFM 的各个划分标准可以都不一样
  • 尽量用小部分的数据覆盖大部分的区间。
  • 靠具体的业务去划分用户等级,不要为了数据优异去做反常态的划分

3.3.2 新、老、活跃、回流、流失

pivoted_counts=df.pivot_table(index='user_id',
                             columns='month',
                             values='order_dt',
                             aggfunc='count').fillna(0)
# 有的用户消费了两次,有的用户消费了一次,我们将消费了的设置为1,未消费的设置为0
df_purchase=pivoted_counts.applymap(lambda x:1 if x>0 else 0)
df_purchase[:5]

如下表所示:
image.png

判断用户分布状况

新用户(new):某一周期前从未发生过购买行为(eg:0,0,1,0)
活跃用户(active):本周期和上周期都发生购买行为,且不属于新客户(eg:0,1,1,0,1,1)
回流用户(return):上周期没买,本周期买了,且不属于新客户(eg:0,1,0,1)
流失用户(unactive):沉寂几个周期未发生购买行为(eg:1,1,0,0,0,)
未注册(unreg):从未购买过(eg:0,0,0,0)

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 status   

df_purchase.apply(lambda x:pd.Series(active_status(x),index = df_purchase.columns),axis=1)
# 传递一行数据给x后active_status(x)输出列表,再把它包装成Series构成二维表 
 
# 这里把未注册的替换为空值,这样 count 计算时不会计算,最终得到每个月的用户分布
purchase_stats_ct=purchase_stats.replace('unreg',np.nan).apply(lambda x:pd.value_counts(x))
purchase_stats_ct   
purchase_stats_ct_info = purchase_stats_ct.fillna(0).T
purchase_stats_ct_info['total_num'] = purchase_stats_ct_info['active'] + purchase_stats_ct_info['new'] + purchase_stats_ct_info['return']+purchase_stats_ct_info['unactive']
purchase_stats_ct_info 
用户分布表
用户分布图
  • 前三个月有大量新用户涌入,新用户占比很高,而后面几个月不活跃用户占比非常高,普遍在90%以上。
  • 持续消费的用户,一直维持在5%左右,对应的是消费运营质量一般。
  • 回流用户在5%到8%之间,唤回运营这方面做的是很不错的。
  • 流失率是最高的,所以可以加强对活跃用户的福利待遇,提高换回用户的手段。

3.4 用户购买周期

# 计算相邻两个订单的时间间隔,shift 函数是对数据进行错位,所有数据会往下平移一下,所以可以
order_diff=grouped_user.apply(lambda x:x.order_dt-x.order_dt.shift())
# 将后缀单位去除
order_diff_info = (order_diff/np.timedelta64(1,'D'))
order_diff_cut_lst = [i for i in range(0,int(order_diff_info.max())+1,10)]
order_diff_info_hist = pd.cut(order_diff_info,bins=order_diff_cut_lst,labels=order_diff_cut_lst[1:]).fillna(10)
用户购买周期图.png
  • 订单周期呈指数分布
  • 用户的平均购买周期是68天
  • 绝大部分用户的购买周期都低于100天

3.5 用户生命周期(第一次购买和最后一次购买的差值)

user_life_info = ((user_life['max']-user_life['min'])/np.timedelta64(1,"D"))
user_life_lst = [i for i in range(0,int(user_life_info.max())+1,10)]
user_life_info_hist1 = pd.cut(user_life_info,bins=user_life_lst,labels=user_life_lst[1:]).fillna(10)
user_life_info.describe()
# mean     134
# 50%       0
未修正用户生命周期图.png
  • 生命周期的均值为134,中位数为0。
  • 由图可看出,用户的生命周期受只购买一次的用户影响非常厉害因此考虑把这一部分数据用户排除掉再次分区间统计。
user_life_info_hist1 = pd.cut(user_life_info,bins=user_life_lst,labels=user_life_lst[1:])
user_life_info[user_life_info>0].describe()
# mean       276.044807
# 50%        302.000000
修正用户生命周期图
  • 去除极值之后,用户平均购买周期为276天,中位数是302天。
  • 可以多进行促销互动,减小购买周期的间隔。

4.复购率和回购率

# 区分一个,和一个以上的情况,以便于计算复购率,大于1为1,等于0为np.nan,等于1为0。因为复购统计时sum函数不会计算np.nan,count函数会计为1。
purchase_r=pivoted_counts.applymap(lambda x: 1 if x>1 else np.NaN if x == 0 else 0)
purchase_r_reshop = (purchase_r.sum()/purchase_r.count()).reset_index(name = 'reshape')
复购率
# 需要使用函数来判断是否回购:当月消费过的用户下个月也消费了叫做回购,时间可以改变
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)
    # 第18个月补充NaN
    status.append(np.NaN)
    return status

indexs=df_purchase.columns
purchase_b = df_purchase.apply(lambda x :pd.Series(purchase_back(x),index = indexs),axis =1)
purchase_b_backshop = (purchase_b.sum()/purchase_b.count()).reset_index(name = 'huigou_rate')

本周期(在此为月)有购买且下个周期也有购买时,则为回购:1
本周期有购买下个周期没有购买,没有回购:0
本周期没有购买则为空值:np.nan
最后一个月补充为np.nan

回购率

复购率和回购率方面,复购率稳定在20%左右,回购率稳定在30%左右,前3个月因为有大量新用户涌入,而这批用户仅购买了一次,所以导致前三个月的复购率和回购率都比较低。

结论:

  • 1.用户消费趋势分析(按月)

    • 前3个月有大量新用户涌入,消费金额、消费次数、消费人数以及产品购买量均达到高峰,后续每月较为稳定。前3个月消费次数都在10000笔左右, 后续月份平均在2500上下波动;
    • 前3个月产品购买量达到20000,后续月份平均7000;前3个月消费人数在8000-10000之间,后续月份平均2000不到;
    • 推断可能是举办了促销降价活动,但是活动过去后,后续新用户量并没有显著提升,可见活动并没有给产品带来实质性的流量。在活动举办期间,全部指标都达到了顶峰,所以建议加大活动力度,并保持一定的时间维度,给买家充分了解产品的时间,才能持续提高用户消费。
  • 2.用户个体消费

    • 个别用户购买了大量的产品,升高了平均消费水平,绝大部分的用户购买金额在200元以下;
    • 用户累计消费金额曲线得出50%的用户仅贡献了11%的消费额度,而排名前5000的用户就贡献了60%的消费额度,满足二八法则;
  • 3.用户消费行为

    • 首购和最后一次购买时间集中在前三个月,说明很多用户只购买了一次,而且用户最后一次的购买时间逐步上升,流失的客户在逐步增加。
    • 有一半的用户仅仅消费了一次,而且每个月的新客在第四个月起逐步稳定在80%左右。
    • 利用RFM模型分出八种用户,其中一般挽留用户最多,可以多注意这些用户。该模型实现了对用户的精细化运营。
    • 针对用户生命周期,前三个月有大量新用户涌入,新用户占比很高,而后面几个月不活跃用户占比非常高,普遍在90%以上。持续消费的用户,一直维持在5%左右,对应的消费运营质量一般。回流用户在5%到8%之间,唤回运营这方面做的是很不错的。流失率是最高的,所以可以采取加强对活跃用户的福利待遇,提高换回用户的手段。排除50%的仅消费一次的用户影响后,平均购买周期为276天,中位数是302天,可采取降价手段,降低购买周期。
    • 用户购买周期呈现指数分布,绝大部分用户的购买周期都低于100天,高于100天的呈指数形下降。
  • 4.复购率和回购率方面,复购率稳定在20%左右,回购率稳定在30%左右,前3个月因为有大量新用户涌入,而这批用户仅购买了一次,所以导致前三个月的复购率和回购率都比较低。

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

推荐阅读更多精彩内容

  • 目录 分析目标 分析过程 总结建议 一、分析目标 本次分析报告的数据来源与这家单车网站上的用户消费记录,通过分析用...
    我就是那个无敌大长腿阅读 423评论 0 2
  • 本文主要是主要是根据用户消费记录,分析用户消费行为,建立RFM模型,分析复购率、回购率等关键指标。希望对其他产品的...
    分类讨论阅读 383评论 0 1
  • 本次案例的数据来源于CDNOW,是美国的一家网上唱片公司,数据描述的是这家公司网站上的用户消费记录,分析目标旨在分...
    胖波波玻璃球阅读 704评论 0 0
  • 一、数据集介绍 该数据集是跨国数据集,包含2010年12月12日至2011年12月9日期间发生的所有在英国注册的非...
    努力进步的大白菜阅读 23,398评论 5 46
  • 单车销售可视化案例总结老师指路->https://www.jianshu.com/u/1f32f227da5f使用...
    Elena_fan阅读 1,868评论 0 2