电商用户消费行为数据分析

一、分析目的

对于初级阶段的新电商来说,积累数据,找准运营方向,关注流量,开源是重点;
对于中级阶段的电商,稳定客流,提高店铺销量是首要任务;
对于很有规模的电商,更侧重留存与活跃,提升整体运营水平。

不同的阶段,对于数据分析指标的侧重点也不同。
本篇以某电商用户订单记录为例,侧重用户消费整体趋势和用户消费行为,对用户规模和用户黏性中的几个核心数据点进行分析展示:

  • 找出用户流量趋势变化
  • 考察用户分层、生命周期和购买周期

分析过程思维导图:


电商用户消费数据分析.png

二、数据描述

数据来源于一家电商网站用户订单记录

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
plt.style.use('ggplot')

df = pd.read_csv('CDNOW_master.txt',sep='\s+',names = ['user_id','order_dt','pt_quantity','order_amount'])
df.head()
  • user_id::用户编号
  • order_dt:订单日期
  • pt_quantity:产品数量
  • order_amount:订单金额
image.png
df.info()
WeChatb0f9116ec6f8925a2ead3ae67a052edd.png
df.describe()
WeChat16fb3d83f4459f741a57aae328e45382.png

观察数据:
1、日期需要转换格式
2、大部分的订单购买商品数量较少,平均值在2个左右,极值99很大,存在干扰
3、用户消费金额稳定,同样也存在极值干扰

三、数据清洗

时间格式转换:需要按月分析数据,这里直接转为月份,忽略具体日期

df['order_dt']=pd.to_datetime(df.order_dt, format='%Y%m%d')
df['Month']=df['order_dt'].astype('datetime64[M]')
df.head()
WeChateb9d543cde1a6529a9fa6f551ec9e2d4.png
查看是否有空值
df.isnull().any()
WeChatd6cb61cb8ccc36d0744cf109944b694e.png

四、分析数据

  • 用户消费趋势分析

1、每月销量和销售额分布情况

grouped_month = df.groupby('Month')
plt.rcParams['figure.figsize'] = (10, 6)
plt.subplot(221)
grouped_month.sum().pt_quantity.plot()
plt.title('pt_quantity')
plt.subplot(222)
grouped_month.sum().order_amount.plot()
plt.title('order_amount')
WeChat1f736f8db2db221a311357cf58c74d76.png

销量与销售额走势一致

  • 前三个月,销售数量在25000左右,销售额在
    350000左右;
  • 后续月份,销售数量在7000左右,销售额在100000左右。

2、用户数量、订单数量分布情况

plt.subplot(223)
grouped_month['pt_quantity'].count().plot()
plt.title('order')
plt.subplot(224)
grouped_month.nunique().user_id.plot()
plt.title('user')
WeChatd1f198b48ed2d2aa2e47cbf470a46653.png

订单量和用户数量线性分布图

  • 前三个月订单数量在9000--11000之间,后续月份在2000左右
  • 前三个月用户数量在8000--1000之间,后续月份在2000左右
  • 每月消费次数与消费人数差异不大

3、用户数量分布情况
使用数据透视表,查看每月用户数量、销量和销售额

pd.pivot_table(df, index='Month',
                  values=['user_id','pt_quantity','order_amount'],
                  aggfunc={'user_id':'count','pt_quantity':'sum','order_amount':'sum'})
WeChatd451b54c52832ba7d766e682bf0c7604.png
grouped_month['order_amount'].mean().plot()
WeChat0f6e4328018284094be07f8f7418585d.png

用户平均消费金额不稳定,此消彼长

pd.pivot_table(df, index='Month',
                    columns='user_id',
                    values=['order_dt'],
                    aggfunc='count').mean(axis=1).plot()
WeChat72ff297068942d329f746f5093d1fe63.png

用户平均消费次数在1-2次之间,1997-1998呈上涨趋势

消费趋势汇总:
  1. 1997年前三个月消费总金额和产品总销量比较高,后续时期迅速下降,后基本稳定;
  2. 用户平均消费金额此消彼长;
  3. 用户平均消费次数稳定在1-2次之间;
  • 用户消费行为分析

1、用户消费次数与消费金额

grouped_user = df.groupby('user_id')
grouped_user.sum().describe()
WeChated591f53916e8271c5e06c4353cd7d75.png
  • 共有23570个用户
  • 用户平均消费数量为7,平均消费金额106元
  • 标准差都为平均数的两倍以上,从购买数量来看,中位数为3,最大值为1033,说明小部分人购买了大量产品;从购买金额来看,平均消费金额106,最大值13990,存在较大的极值干扰。
  • 消费数量和金额的平均值都在75%位置,说明25%的用户购买金额和数量较大,拉高了平均值,整体分布左偏
  • 消费金额最小值为0,是因为有促销订单

用户消费金额、消费次数分布散点图

grouped_user.sum().plot.scatter(x='pt_quantity',y='order_amount')
image.png

根据散点图分布,极值影响严重,根据切比雪夫定理,筛选数据
95%的数据集中在距离平均值5个标准差之内

grouped_user.sum().query('order_amount<1306').plot.scatter(x='pt_quantity',y='order_amount')

去掉极值,重新调整后的分布图


image.png

图形大致呈现线性回归,说明客单价稳定

grouped_user.sum().query('pt_quantity<80').pt_quantity.plot.hist(bins=30)

用户消费次数直方图:


image.png

大部分集中在10次以内,小部分数据造成了干扰

grouped_user.sum().query('pt_quantity<80').order_amount.plot.hist(bins=30)

用户金额次数直方图


image.png

大部分集中在250元以下,绝大部分呈现集中趋势,小部分数据造成了干扰

2、用户累计消费额占比

a = grouped_user.sum().sort_values('order_amount').apply(lambda x : x.cumsum()/x.sum())
a.tail()

按消费金额排序,使用累计加和函数,计算用户消费额占比


image.png
user_cumsum = grouped_user.sum().sort_values('order_amount').apply(lambda x : x.cumsum()/x.sum())
user_cumsum.reset_index().order_amount.plot()
image.png

用户人数是23750 50%的人只占了15%的消费额 消费总金额前4000名贡献了60%的消费额度
也就是维护好这前4000名客户,可以完成KPI的60%

3、新老客消费比

grouped_user.order_dt.min().value_counts().plot()

每月新客趋势图


image.png
  • 波动此消彼长
  • 2.1-2.15有一次剧烈波动
grouped_user.order_dt.max().value_counts().plot()

每月老客趋势图


image.png
  • 前三个月数量很高,原因是前期有大量新客,只消费了一次后流失,后期逐渐稳定
  • 4月有一次剧烈波动

4、单次用户消费数量

b = grouped_user.order_dt.agg([np.min,np.max])
(b['amin'] == b['amax']).value_counts().plot.pie(autopct='%.2f%%',startangle=90,shadow=True)
image.png

只消费了一次的客户占比51.14%,有一半客户只购买了一次

c = df.groupby(['Month','user_id'])['order_dt'].agg(['min','max']).reset_index(level=1)
(c['min']==c['max']).groupby('Month').value_counts().plot.bar()

按月对比:


image.png
  • 每个月新客数多于老客数
  • 前三个月新客数量大,第四个月开始突然大幅度减少,后逐渐降低
  • 老客数量前三个月递增,后续月份递减

5、用户分层——rfm模型
使用数据透视表,提取出用户消费额、最后一次消费日期、消费数量数据

rfm = pd.pivot_table(df,index='user_id',values=['order_dt','pt_quantity','order_amount'],
                          aggfunc={'order_dt':'max','pt_quantity':'sum','order_amount':'sum'})
rfm.head()
image.png

将最后一次消费日期转为最后一次消费日距今的天数
(由于数据是很早之前的,为了更好的展示数据,将对比标准改为所有用户最后一次消费的日期)

rfm['order_dt'] = (rfm['order_dt'].max() - rfm['order_dt'])/np.timedelta64(1,'D')
rfm.rename(columns={'order_amount':'M','order_dt':'R','pt_quantity':'F'},inplace=True)
rfm.head()
image.png

数据以平均值作为x、y、z轴标准值,编写python函数,将用户M、R、F数据,划分象限,使用0、1作为标准值上下象限之分,给用户分别贴上标签。
8类标签分别是:重要保持客户、重要价值客户、重要发展客户、重要挽留客户、一般保持客户、一般价值客户、一般发展客户、一般挽留客户

def rfm_func(x):
    level = x.apply(lambda x : np.where(x>=0,'1','0'))
    label = level.R + level.F +level.M
    dict_n={'011':'重要保持客户',
           '111':'重要价值客户',
           '001':'重要发展客户',
           '101':'重要挽留客户',
           '010':'一般保持客户',
           '110':'一般价值客户',
           '000':'一般发展客户',
           '100':'一般挽留客户'}
    result=dict_n[label]
    return result
rfm['客户分类'] = rfm.apply(lambda x : x - x.mean()).apply(rfm_func,axis=1)
rfm.head()
image.png
rfm.groupby('客户分类').agg({'M':'sum','R':'count','F':'sum'})

统计各标签用户的总销售额、总的消费频率,和人数


image.png

一般挽留客户最多,重要保持客户第二,重要保持客户销售金额占比最高

  color_dict= {
        '重要价值客户':'r',
        '重要保持客户':'g',
        '重要发展客户':'b',
        '重要挽留客户':'c',
        '一般价值客户':'m',
        '一般保持客户':'y',
        '一般发展客户':'k',
        '一般挽留客户':'w'
    }

rfm['color'] = rfm.客户分类.map(color_dict)
rfm.plot.scatter('R','F',color=rfm['color'])

rfm客户分层散点图:


image.png

从RFM分层可知,大部分用户为重要保持客户,但这是由于极值影响,拉高了平均值,用户划分不够准确

6、用户分层——新老用户、活跃、回流、流失用户

pivot_dt = pd.pivot_table(df,index='user_id',values='order_dt',columns='Month',aggfunc='count')
dt = pivot_dt.fillna(0).applymap(lambda x : np.where(x>0,1,0))
dt.head()

使用数据透视表,统计每月各用户消费情况,1表示当月购买过,0表示当月没有购买


image.png

使用python函数,根据用户每月消费情况,贴上标签

  • 未注册用户:unreg
  • 新用户:new
  • 当月未购买且已注册:不活跃 unactive
  • 当月购买上个月不活跃:回流用户 return
  • 当月购买上个月也购买:活跃 active
    (这个划分标准比较简单,不是很准确)
    status=[]
    for i in range(18):
        if row[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')
    
    for index,value in enumerate(status):
        row.iloc[index]=status[index]

    return row
                            
purchase_status = dt.apply(active_status,axis=1)
purchase_status.head()
image.png

统计每月各类用户的数量

user_fc = purchase_status.replace('unreg',np.nan).apply(lambda x : x.value_counts()).fillna(0).T
user_fc
image.png
user_fc.plot.area()

更直观的面积图:


image.png
  • 根据用户分层面积图可以看出,新用户集中在前三个月,每个月都有大量不活跃用户

计算回流率加入表中

user_fc['回流率']=(user_fc['return'].shift()/user_fc['unactive']).fillna(0)
user_fc
image.png
  • 活跃用户,对应的是持续消费的客户,对应消费运营的质量
  • 回流用户,上月不消费,本月消费,对应的是唤回运营质量
  • 不活跃用户,对应的是流失率

7、用户生命周期
计算用户第一次购买和最后一次购买的时间差

user_dt = df.groupby('user_id').order_dt.agg(['min','max'])
user_dt['diff'] = (user_dt['max']-user_dt['min'])/np.timedelta64(1,'D')
user_dt['diff'].describe()
image.png

平均生命周期为135天,最长544天

user_dt['diff'].plot.hist(bins=30)
image.png

用户的生命周期受只购买过一次的用户影响比较厉害,可以剔除

user_dt.query('diff>0')['diff'].plot.hist(bins=30)
image.png

剔除只购买一次的用户,可以看出,用户生命周期首位两端人数比较多,中间值相对少

8、用户购买周期

order=  df.groupby('user_id').apply(lambda x : x.order_dt-x.order_dt.shift())
order.describe()
image.png
(order/np.timedelta64(1,'D')).plot.hist(bins=20)
image.png
  • 订单周期呈指数分布
  • 用户平均购买周期是68天
  • 绝大部分用户购买周期小于100天

9、复购率
复购率指自然月内,购买多次的用户占比

order_dt = pivot_dt.applymap(lambda x : 1 if x>1 else 0 if x==1 else np.nan)
order_dt.head()

使用applymap函数对用户购买各月购买次数进行标记

  • 当月未购买标记为null
  • 购买一次标记为0
  • 购买1次以上标记为1
image.png
((order_dt.sum())/(order_dt.count())).plot()

复购率线形图


image.png

复购率稳定在20%左右,前一个月因为有大量新用户,只购买了一次,拉低了复购率

10、回购率
回购率指曾经购买过且在某一时期内再次购买的用户占比

dt.head()

使用前面分好的购买标记
0为本月未购买,1为本月购买


image.png

编写python函数,对用户回购情况贴上标签

  • 本月未购买标记为null,不存在计算回购率的情况
  • 本月购买,下个月回购,本月标记为1
  • 本月购买,下个月未回购,本月标记为0
  • 最后一个月份,由于不清楚下月情况,统一标记为null不做统计
def func_back(x):
    status=[]
    for i in range(17):
        if x[i] == 1:
            if x[i+1] ==1:
                status.append(1)
            if x[i+1] == 0:
                status.append(0)
        else:
            status.append(np.NaN)
    status.append(np.NaN)
    return pd.Series(status,index=x.index)

purchase_b = dt.apply(func_back,axis=1)
purchase_b.head()
image.png
(purchase_b.sum()/purchase_b.count()).plot()

回购率线形图


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