CDNow曾经是一家在线音乐零售平台,后被德国波泰尔斯曼娱乐集团公司出资收购,其资产总价值在最辉煌时曾超过10亿美元。本文主要通过分析CDNow网站的用户购买明细来分析该网站的用户消费行为,使运营部门在营销时更加具有针对性,从而节省成本,提升效率。
数据来源
本次分析数据来源CDNow网站的用户在1997年1月1日至1998年6月30日期间内购买CD交易明细。
数据下载地址
——提取码: va7h
数据集一共有用户ID,购买日期,订单数,订单金额四个字段。
import numpy as np
import pandas as pd
from datetime import datetime
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
#读取数据
#4列特征,分别为用户id,购买日期,购买数量,购买金额
columns=['user_id','order_dt','order_products','order_amount']
data=pd.read_table('./CDNOW_master.txt',names=columns,sep='\s+')
data.head()
|
user_id
|
order_dt
|
order_products
|
order_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
|
data.describe() # 数据的基本信息
|
user_id
|
order_dt
|
order_products
|
order_amount
|
count
|
69659.000000
|
6.965900e+04
|
69659.000000
|
69659.000000
|
mean
|
11470.854592
|
1.997228e+07
|
2.410040
|
35.893648
|
std
|
6819.904848
|
3.837735e+03
|
2.333924
|
36.281942
|
min
|
1.000000
|
1.997010e+07
|
1.000000
|
0.000000
|
25%
|
5506.000000
|
1.997022e+07
|
1.000000
|
14.490000
|
50%
|
11410.000000
|
1.997042e+07
|
2.000000
|
25.980000
|
75%
|
17273.000000
|
1.997111e+07
|
3.000000
|
43.700000
|
max
|
23570.000000
|
1.998063e+07
|
99.000000
|
1286.010000
|
从上可知,看数据6关键指标:样本数:69659,最小值:0,最大值:1286,平均值:36,方差36,中位数:26。平均数大于中位数,右偏分布。没有空缺数据,不用清理直接分析,注意到日期数据是整数型,需要转换成时间格式。
#增加两个新时间序列
#将order_dt转化成时间格式
data['order_date']=pd.to_datetime(data.order_dt,format='%Y%m%d')
#将order_date转化成每月起始第一天#将order_date转化成每月起始第一天
data['month']=data.order_date.values.astype('datetime64[M]')
#按用户ID进行分组
user_grouped=data.groupby('user_id').sum()
user_grouped
|
order_dt
|
order_products
|
order_amount
|
user_id
|
|
|
|
1
|
19970101
|
1
|
11.77
|
2
|
39940224
|
6
|
89.00
|
3
|
119833602
|
16
|
156.46
|
4
|
79882233
|
7
|
100.50
|
5
|
219686137
|
29
|
385.61
|
6
|
19970101
|
1
|
20.99
|
7
|
59921434
|
18
|
264.67
|
8
|
159775420
|
18
|
197.66
|
9
|
59921222
|
6
|
95.85
|
10
|
19970121
|
3
|
39.31
|
11
|
79890766
|
4
|
58.55
|
12
|
19970101
|
4
|
57.06
|
13
|
19970101
|
4
|
72.94
|
14
|
19970101
|
2
|
29.92
|
15
|
19970101
|
4
|
52.87
|
16
|
79882450
|
8
|
79.87
|
17
|
19970101
|
5
|
73.22
|
18
|
19970104
|
1
|
14.96
|
19
|
39940711
|
11
|
175.12
|
20
|
39940219
|
46
|
653.01
|
21
|
39940214
|
4
|
75.11
|
22
|
19970101
|
1
|
14.37
|
23
|
19970101
|
2
|
24.74
|
24
|
39950221
|
4
|
57.77
|
25
|
159804813
|
12
|
137.53
|
26
|
39940227
|
6
|
102.69
|
27
|
39940219
|
10
|
135.87
|
28
|
59910536
|
7
|
90.99
|
29
|
239656335
|
28
|
435.81
|
30
|
39940315
|
2
|
28.34
|
...
|
...
|
...
|
...
|
23541
|
39940727
|
2
|
57.34
|
23542
|
19970325
|
5
|
77.43
|
23543
|
19970325
|
2
|
50.76
|
23544
|
59920964
|
12
|
134.63
|
23545
|
19970325
|
1
|
24.99
|
23546
|
19970325
|
1
|
13.97
|
23547
|
39940732
|
2
|
23.54
|
23548
|
19970325
|
2
|
23.54
|
23549
|
19970325
|
2
|
27.13
|
23550
|
19970325
|
2
|
25.28
|
23551
|
119824382
|
12
|
264.63
|
23552
|
39940728
|
4
|
49.38
|
23553
|
39940653
|
8
|
98.58
|
23554
|
39950526
|
3
|
36.37
|
23555
|
99873685
|
14
|
189.18
|
23556
|
139814017
|
15
|
203.00
|
23557
|
19970325
|
1
|
14.37
|
23558
|
79891692
|
11
|
145.60
|
23559
|
59911470
|
8
|
111.65
|
23560
|
19970325
|
1
|
18.36
|
23561
|
59930982
|
6
|
83.46
|
23562
|
19970325
|
2
|
29.33
|
23563
|
39941329
|
3
|
58.75
|
23564
|
59911976
|
5
|
70.01
|
23565
|
19970325
|
1
|
11.77
|
23566
|
19970325
|
2
|
36.00
|
23567
|
19970325
|
1
|
20.97
|
23568
|
59911152
|
6
|
121.70
|
23569
|
19970325
|
2
|
25.74
|
23570
|
39940651
|
5
|
94.08
|
23570 rows × 3 columns
user_grouped.describe()
|
order_dt
|
order_products
|
order_amount
|
count
|
2.357000e+04
|
23570.000000
|
23570.000000
|
mean
|
5.902627e+07
|
7.122656
|
106.080426
|
std
|
9.460684e+07
|
16.983531
|
240.925195
|
min
|
1.997010e+07
|
1.000000
|
0.000000
|
25%
|
1.997021e+07
|
1.000000
|
19.970000
|
50%
|
1.997032e+07
|
3.000000
|
43.395000
|
75%
|
5.992125e+07
|
7.000000
|
106.475000
|
max
|
4.334408e+09
|
1033.000000
|
13990.930000
|
原表只按订单来记录,现按用户分组看数据集概述。
从购买数量(单位:PC)角度看,用户数量:23570,平均值购买7张,最多购买1033张,属于狂热用户,中位数3张,平均值大于中位数,是右偏分布,存在小部分购买多张碟的用户。
从消费金额(单位:美元)角度看,平均数106,最高值:13990,属于土豪用户,中位数43,平均值大于中位数,是右偏分布,存在小部分高消费用户。
#设定绘图风格
plt.style.use('ggplot')
plt.rcParams['font.sans-serif']=['SimHei']
ax=data.groupby('month').order_products.sum().plot()
ax.set_xlabel('月份')
ax.set_ylabel('数量(张)')
ax.set_title('不同月份的用户购买数量')
plt.show()
ax=data.groupby('month').order_amount.sum().plot()
ax.set_xlabel('月份')
ax.set_ylabel('消费金额')
ax.set_title('不同月份的用户消费金额')
plt.show()
由图片可知,无论是消费金额还是CD碟数都呈现相同的趋势,而且前三月数据都呈现出异常状态,由于我们不知道原数据到底从何获得,只能做出这三个月有促销活动,抑或是这是新开的店之类的,前三个月大多都是新人之类的假设,我们不得而知,继续往下看。
ax=data.groupby('user_id').sum().plot.scatter('order_amount','order_products')
ax.set_xlabel('消费金额')
ax.set_ylabel('数量')
ax.set_title('每个用户消费金额与购买个数的关系')
plt.show()
每个用户的消费金额跟购买数量呈现一定的线性关系。
plt.figure(figsize=(12,4))
ax=plt.subplot(121)
ax=data.groupby('user_id').order_products.sum().plot.hist(bins=50)
ax.set_xlabel('数量')
ax.set_ylabel('用户数')
ax.set_xlim(0,150)
ax.set_title('用户购买数量分布直方图')ax1=plt.subplot(122)
ax1=data.groupby('user_id').order_amount.sum().plot.hist(bins=50)
ax1.set_xlabel('消费金额')
ax1.set_ylabel('用户人数')
ax1.set_xlim(0,2000)
ax1.set_title('用户消费金额分布直方图')
plt.show(ax,ax1)
我们可以从直方图看到,大部分用户的消费能力确实不高,整个计算周期里的购买碟数在20张以内,消费金额在250美金以内,再图上几乎看不到高消费用户。
data.groupby('user_id').month.min().value_counts()
1997-02-01 8476
1997-01-01 7846
1997-03-01 7248
Name: month, dtype: int64
data.groupby('user_id').month.max().value_counts()
1997-02-01 4912
1997-03-01 4478
1997-01-01 4192
1998-06-01 1506
1998-05-01 1042
1998-03-01 993
1998-04-01 769
1997-04-01 677
1997-12-01 620
1997-11-01 609
1998-02-01 550
1998-01-01 514
1997-06-01 499
1997-07-01 493
1997-05-01 480
1997-10-01 455
1997-09-01 397
1997-08-01 384
Name: month, dtype: int64
我们可以看到,貌似每个用户的第一次购买记录都是前三个月,最后次购买记录也是前三个月居前三。由此我们可以推测,这份数据集大概是选择了这三个月时间段消费的用户在后面的18个月的追踪记录数据。也能很好的解释我们之前看到的前三个月无论是消费金额还是购买数量的异常。
#创建数据透视表,对每个用户的每月订单数计数
pivoted_count=data.pivot_table(index='user_id',columns='month',values='order_dt',aggfunc='count').fillna(0)
pivoted_count.head()
month
|
1997-01-01
|
1997-02-01
|
1997-03-01
|
1997-04-01
|
1997-05-01
|
1997-06-01
|
1997-07-01
|
1997-08-01
|
1997-09-01
|
1997-10-01
|
1997-11-01
|
1997-12-01
|
1998-01-01
|
1998-02-01
|
1998-03-01
|
1998-04-01
|
1998-05-01
|
1998-06-01
|
user_id
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1
|
1.0
|
0.0
|
0.0
|
0.0
|
0.0
|
0.0
|
0.0
|
0.0
|
0.0
|
0.0
|
0.0
|
0.0
|
0.0
|
0.0
|
0.0
|
0.0
|
0.0
|
0.0
|
2
|
2.0
|
0.0
|
0.0
|
0.0
|
0.0
|
0.0
|
0.0
|
0.0
|
0.0
|
0.0
|
0.0
|
0.0
|
0.0
|
0.0
|
0.0
|
0.0
|
0.0
|
0.0
|
3
|
1.0
|
0.0
|
1.0
|
1.0
|
0.0
|
0.0
|
0.0
|
0.0
|
0.0
|
0.0
|
2.0
|
0.0
|
0.0
|
0.0
|
0.0
|
0.0
|
1.0
|
0.0
|
4
|
2.0
|
0.0
|
0.0
|
0.0
|
0.0
|
0.0
|
0.0
|
1.0
|
0.0
|
0.0
|
0.0
|
1.0
|
0.0
|
0.0
|
0.0
|
0.0
|
0.0
|
0.0
|
5
|
2.0
|
1.0
|
0.0
|
1.0
|
1.0
|
1.0
|
1.0
|
0.0
|
1.0
|
0.0
|
0.0
|
2.0
|
1.0
|
0.0
|
0.0
|
0.0
|
0.0
|
0.0
|
pivoted_count_transf =pivoted_count.applymap(lambda x:1 if x>1 else np.NAN if x ==0 else 0)
pivoted_count_transf.head()
month
|
1997-01-01
|
1997-02-01
|
1997-03-01
|
1997-04-01
|
1997-05-01
|
1997-06-01
|
1997-07-01
|
1997-08-01
|
1997-09-01
|
1997-10-01
|
1997-11-01
|
1997-12-01
|
1998-01-01
|
1998-02-01
|
1998-03-01
|
1998-04-01
|
1998-05-01
|
1998-06-01
|
user_id
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1
|
0.0
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
2
|
1.0
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
3
|
0.0
|
NaN
|
0.0
|
0.0
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
1.0
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
0.0
|
NaN
|
4
|
1.0
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
0.0
|
NaN
|
NaN
|
NaN
|
0.0
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
5
|
1.0
|
0.0
|
NaN
|
0.0
|
0.0
|
0.0
|
0.0
|
NaN
|
0.0
|
NaN
|
NaN
|
1.0
|
0.0
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
消费行为中的复购率和回购率
复购率:复购率的定义是在某时间窗口内消费两次及以上的用户在总消费用户中占比。这里的时间窗口是月,如果一个用户在同一天下了两笔订单,这里也将他算作复购用户。
# 复购率
db=pivoted_count_transf.sum()/pivoted_count_transf.count()
ax=db.plot(figsize=(10,4))
ax.set_xlabel('时间(月)')
ax.set_ylabel('百分比(%)')
ax.set_title('每月用户复购率图')
plt.show()
从图中我们可以看出复购率因为大量新用户加入的关系,不断扩大分母,导致初期的复购率不怎么高,譬如刚开始的97年一月份复购率只有6%左右,而后直线上升,到第四个月后到达最高点,因为这次数据集是前三个月新客跟踪数据,三个月后,没有新客的加入,可以认定是前三个月的客户大浪淘沙剩下的还有购买欲望的老客,这时候的复购率比较稳定,在20%左右徘徊。
fig,ax=plt.subplots(figsize=(10,4))
ax.plot(pivoted_count_transf.sum())
ax.plot(pivoted_count_transf.count())
ax.set_xlabel('时间(月)')
ax.set_ylabel('用户数(人)')
ax.set_title('每月消费和二次消费以上用户数')
legends=['二次消费以上人数','消费人数']
ax.legend(legends,loc='upper right')
plt.show()
如图所示,三个月后,用户迅速沉淀,前三个月的新客大概在1891正负387左右人数继续消费波动,而二次消费的客户则很稳定,曲线也趋近直线,大概在400人左右波动。这部分的客户放到现实中应该是重点维护的优质客户。
回购率:回购率是某一个时间窗口内消费的用户,在下一个时间窗口仍旧消费的占比。我前一月消费用户1000,后一个月他们中仍有200个消费,则回购率是20%。由于牵扯两个时间窗口的计算,所以较之复购率稍稍复杂点。
pivoted_amount=data.pivot_table(index='user_id',columns='month',values='order_dt',aggfunc='count').fillna(0)
#转化数据,有过购买行为的为1,没有购买行为的为0
pivoted_purchase=pivoted_amount.applymap(lambda x:1 if x>0 else 0)
pivoted_purchase.head()
month
|
1997-01-01
|
1997-02-01
|
1997-03-01
|
1997-04-01
|
1997-05-01
|
1997-06-01
|
1997-07-01
|
1997-08-01
|
1997-09-01
|
1997-10-01
|
1997-11-01
|
1997-12-01
|
1998-01-01
|
1998-02-01
|
1998-03-01
|
1998-04-01
|
1998-05-01
|
1998-06-01
|
user_id
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1
|
1
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
2
|
1
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
3
|
1
|
0
|
1
|
1
|
0
|
0
|
0
|
0
|
0
|
0
|
1
|
0
|
0
|
0
|
0
|
0
|
1
|
0
|
4
|
1
|
0
|
0
|
0
|
0
|
0
|
0
|
1
|
0
|
0
|
0
|
1
|
0
|
0
|
0
|
0
|
0
|
0
|
5
|
1
|
1
|
0
|
1
|
1
|
1
|
1
|
0
|
1
|
0
|
0
|
1
|
1
|
0
|
0
|
0
|
0
|
0
|
#定义函数,每个月都要跟后面一个月对比下,本月有消费且下月也有消费,则本月记为1,
#下月没有消费则为0,本月没有消费则为NaN,由于最后个月没有下月数据,规定全为NaN
def purchase_return(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)status.append(np.NaN) return pd.Series(status)
#应用并且绘图
pivoted_purchase_return=pivoted_purchase.apply(purchase_return,axis=1)
pivoted_purchase_return.head()
|
0
|
1
|
2
|
3
|
4
|
5
|
6
|
7
|
8
|
9
|
10
|
11
|
12
|
13
|
14
|
15
|
16
|
17
|
user_id
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1
|
0.0
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
2
|
0.0
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
3
|
0.0
|
NaN
|
1.0
|
0.0
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
0.0
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
0.0
|
NaN
|
4
|
0.0
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
0.0
|
NaN
|
NaN
|
NaN
|
0.0
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
5
|
1.0
|
0.0
|
NaN
|
1.0
|
1.0
|
1.0
|
0.0
|
NaN
|
0.0
|
NaN
|
NaN
|
1.0
|
0.0
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
columns=data.month.sort_values().unique()
pivoted_purchase_return.columns = columns
ax=(pivoted_purchase_return.sum()/pivoted_purchase_return.count()).plot(figsize=(10,4))
ax.set_xlabel('时间(月)')
ax.set_ylabel('百分比(%)')
ax.set_title('十八个月内用户回购率图')
plt.show()
fig,ax=plt.subplots(figsize=(10,4))
ax.plot(pivoted_purchase_return.sum())
ax.plot(pivoted_purchase_return.count())
ax.set_xlabel('时间(月)')
ax.set_ylabel('用户数(人)')
legends = ['每月回购人数','每月消费人数']
ax.legend(legends,loc = 'upper right')
plt.show()
由图可知,用户的回购率大于复购率,约在30%左右正负五个点波动,看人数分布表则发现回购人数趋近稳定,那么波动则是偶尔月份间的消费人数基数的变动,可能有营销者淡旺季,但是这部分回购用户的消费行为大抵稳定,应该跟之前每月复购的用户有一定重合,是属于优质用户。对回购率和复购率进行综合分析,可以得出,新客的整体质量低于老客,老客的忠诚度(回购率)表现较好,消费频次稍次,这是这个网站的用户消费特征
(3)消费行为中的用户分层
我们按照用户的消费行为,简单划分成几个维度:新用户、活跃用户、不活跃用户、回流用户。
新用户(new):新用户的定义是第一次消费的用户。
活跃用户(active):即连续两个时间窗口都消费过的用户。
不活跃用户(unactive):不活跃用户则是时间窗口内没有消费过的活跃用户,即一二月份都消费过,三月份没消费过。
回流用户(return):回流用户是在上一个窗口中没有消费,而在当前时间窗口内有过消费。
def active_status(date):status=[]for i in range(18):#若本月没有消费if date[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 pd.Series(status)pivoted_purchase_status=pivoted_purchase.apply(active_status,axis=1)
pivoted_purchase_status.columns=columns
pivoted_purchase_status.head()
|
1997-01-01
|
1997-02-01
|
1997-03-01
|
1997-04-01
|
1997-05-01
|
1997-06-01
|
1997-07-01
|
1997-08-01
|
1997-09-01
|
1997-10-01
|
1997-11-01
|
1997-12-01
|
1998-01-01
|
1998-02-01
|
1998-03-01
|
1998-04-01
|
1998-05-01
|
1998-06-01
|
user_id
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1
|
new
|
unactive
|
unactive
|
unactive
|
unactive
|
unactive
|
unactive
|
unactive
|
unactive
|
unactive
|
unactive
|
unactive
|
unactive
|
unactive
|
unactive
|
unactive
|
unactive
|
unactive
|
2
|
new
|
unactive
|
unactive
|
unactive
|
unactive
|
unactive
|
unactive
|
unactive
|
unactive
|
unactive
|
unactive
|
unactive
|
unactive
|
unactive
|
unactive
|
unactive
|
unactive
|
unactive
|
3
|
new
|
unactive
|
return
|
active
|
unactive
|
unactive
|
unactive
|
unactive
|
unactive
|
unactive
|
return
|
unactive
|
unactive
|
unactive
|
unactive
|
unactive
|
return
|
unactive
|
4
|
new
|
unactive
|
unactive
|
unactive
|
unactive
|
unactive
|
unactive
|
return
|
unactive
|
unactive
|
unactive
|
return
|
unactive
|
unactive
|
unactive
|
unactive
|
unactive
|
unactive
|
5
|
new
|
active
|
unactive
|
return
|
active
|
active
|
active
|
unactive
|
return
|
unactive
|
unactive
|
return
|
active
|
unactive
|
unactive
|
unactive
|
unactive
|
unactive
|
这个函数主要分为两部分的判断,以本月是否消费为界。本月若没有消费,则判断是不是第一个月,是的话直接返回unreg(不辨别),不是第一个月的话,我们就可以找出前一月标签,前一个月还是不辨别的话,就返回unreg(不辨别),因为这个月还是没消费,如果前一个月是新课还是活跃用户,还是回流用户,因为这个月都没消费,重新定义为unactive(不活跃用户)。
本月若有消费,则需要判断是不是第一次消费,若是第一月的消费,因为没有前一个月数据,所以直接是new(新客)。接下来就是不是第一个月又有消费的情况了,此时可以找出前一个月的标签,如果前一个月是不活跃,则返回return(回流用户),如果是unreg,则是第一次消费,返回new(新客),剩下的就是前一个月是新客或者是活跃用户了,此时都返回active(活跃用户)。
pivoted_status_counts=pivoted_purchase_status.replace('unreg',np.NaN).apply(pd.value_counts)
pivoted_status_counts.head()
|
1997-01-01
|
1997-02-01
|
1997-03-01
|
1997-04-01
|
1997-05-01
|
1997-06-01
|
1997-07-01
|
1997-08-01
|
1997-09-01
|
1997-10-01
|
1997-11-01
|
1997-12-01
|
1998-01-01
|
1998-02-01
|
1998-03-01
|
1998-04-01
|
1998-05-01
|
1998-06-01
|
active
|
NaN
|
1157.0
|
1681
|
1773.0
|
852.0
|
747.0
|
746.0
|
604.0
|
528.0
|
532.0
|
624.0
|
632.0
|
512.0
|
472.0
|
571.0
|
518.0
|
459.0
|
446.0
|
new
|
7846.0
|
8476.0
|
7248
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
NaN
|
return
|
NaN
|
NaN
|
595
|
1049.0
|
1362.0
|
1592.0
|
1434.0
|
1168.0
|
1211.0
|
1307.0
|
1404.0
|
1232.0
|
1025.0
|
1079.0
|
1489.0
|
919.0
|
1029.0
|
1060.0
|
unactive
|
NaN
|
6689.0
|
14046
|
20748.0
|
21356.0
|
21231.0
|
21390.0
|
21798.0
|
21831.0
|
21731.0
|
21542.0
|
21706.0
|
22033.0
|
22019.0
|
21510.0
|
22133.0
|
22082.0
|
22064.0
|
ax=pivoted_status_counts.fillna(0).T.plot.area(figsize=(12,6))
ax.set_xlabel('时间(月)')
ax.set_ylabel('用户数(人)')
ax.set_title('每月各类用户类型占比面积图')
ax.legend(loc='upper left')
plt.show()
由图可知,黑色的不活跃用户始终是占据大头的,这也跟我们之前的图表结果相符,其次红色代表的活跃用户非常稳定,是属于核心用户,以及紫色的回流用户,这两个分层相加,大抵是当月的消费用户人数的占比,与我们之前的复购率和回购率的表结果一致,这两层的用户大抵接近两千人左右。
回流占比:某个时间窗口内回流用户在总用户中的占比。
活跃占比:某个时间窗口内活跃用户在总用户中的占比。
return_rate=pivoted_status_counts.apply(lambda x:x/x.sum())
ax=return_rate.loc['return'].plot(figsize=(12,6))
ax.set_xlabel('时间(月)')
ax.set_ylabel('百分数(%)')
ax.set_title('每月回流用户占比')
plt.show()
ax = return_rate.loc['active'].plot(figsize = (12,6))
ax.set_xlabel('时间(月)')
ax.set_ylabel('百分数(%)')
ax.set_title('每月活跃用户占比')
plt.show()
由图可知,用户回流占比在5%~8%之间波动,趋势向下,有客户流失的预警。用户活跃占比在3%~5%间,作为连续消费用户,质量在一定程度上高于回流用户。结合回流用户和活跃用户看,在后期的消费用户中,60%是回流用户,40%是活跃用户,整体质量还好,但是针对这两个分层依旧有改进的空间,可以继续细化数据。
(4)用户质量
因为消费行为有明显的二八倾向,我们需要知道高质量用户为消费贡献了多少份额。
user_amount=data.groupby('user_id').order_amount.sum().sort_values().reset_index()
user_amount['amount_cumsum']=user_amount.order_amount.cumsum()
user_amount.tail()
|
user_id
|
order_amount
|
amount_cumsum
|
23565
|
7931
|
6497.18
|
2463822.60
|
23566
|
19339
|
6552.70
|
2470375.30
|
23567
|
7983
|
6973.07
|
2477348.37
|
23568
|
14048
|
8976.33
|
2486324.70
|
23569
|
7592
|
13990.93
|
2500315.63
|
#计算出各阶段金额占总金额的百分比
amount_total = user_amount.amount_cumsum.max()
user_amount['prop'] = user_amount.amount_cumsum.apply(lambda x: x / amount_total)
ax = user_amount.prop.plot()
ax.set_xlabel('人数(人)')
ax.set_ylabel('百分数(%)')
ax.set_title('用户累计贡献金额百分比')
plt.show()
由图我们可以清晰的看到,金额排名靠后的15000名用户才贡献不到20%的销售量,而此次数据集的总用户人数为23569人,我们可以进一步再看,排名靠后的20000名用户,贡献了接近40%的消费金额,也就是说后面的3569人接近贡献了60%的消费金额,如果我们换算成金额/人单位,这后面的3569名客户相当于签名20000名客户的8.4倍!这也反应了在消费领域中,狠抓高质量用户是万古不变的道理。
(5)用户生命周期
这里我们定义第一次消费至最后一次消费为整个用户生命。我们需要找出每个用户的第一次消费和最后次消费
#用户生命周期
user_purchase = data[['user_id','order_products','order_amount','order_date']]
order_date_min=user_purchase.groupby('user_id').order_date.min()
order_date_max=user_purchase.groupby('user_id').order_date.max()
life_time = (order_date_max-order_date_min).reset_index()
display(life_time.head(),life_time.describe())
|
user_id
|
order_date
|
0
|
1
|
0 days
|
1
|
2
|
0 days
|
2
|
3
|
511 days
|
3
|
4
|
345 days
|
4
|
5
|
367 days
|
|
user_id
|
order_date
|
count
|
23570.000000
|
23570
|
mean
|
11785.500000
|
134 days 20:55:36.987696
|
std
|
6804.217258
|
180 days 13:46:43.039788
|
min
|
1.000000
|
0 days 00:00:00
|
25%
|
5893.250000
|
0 days 00:00:00
|
50%
|
11785.500000
|
0 days 00:00:00
|
75%
|
17677.750000
|
294 days 00:00:00
|
max
|
23570.000000
|
544 days 00:00:00
|
由描述可知,所有用户的平均生命周期是134天,中位数是0天,也就是存在一半的用户是第一次消费就是最后次消费,也就是我们前面所说的低质量客户。而最大是544天,相当于我们这个数据集的总天数,说明这用户是从开始到最后都有消费意愿的高质量用户。
因为数据中的用户都是前三个月第一次消费,所以这里的生命周期代表的是1月~3月用户的生命周期。因为这里数据只截取到了18个月为止,这时间过后,用户仍然会持续消费,所以理论上,用户的平均生命周期还会增长。接下来我们看下直方图的分布,更加直观。
life_time['life_time'] = life_time.order_date/np.timedelta64(1,'D')
ax=life_time.life_time.plot.hist(bins =100,figsize = (12,6))
ax.set_xlabel('天数(天)')
ax.set_ylabel('人数(人)')
ax.set_title('二次消费以上用户的生命周期直方图')
plt.show()
life_time['life_time'] = life_time.order_date/np.timedelta64(1,'D')
ax=life_time[life_time.life_time>0].life_time.plot.hist(bins =100,figsize = (12,6))
ax.set_xlabel('天数(天)')
ax.set_ylabel('人数(人)')
ax.set_title('二次消费以上用户的生命周期直方图')
plt.show()
看图我们可以出图像呈双峰结构,部分质量差的用户,虽然消费了两次,但是仍旧无法持续,此时若想提高点用户转化率,应在用户首次消费30天内应该尽量引导,不然就会流失。少部分用户集中在50天~300天,属于普通型的生命周期,有一定忠诚度。高质量用户的生命周期,集中在400天以后,这时候途中人数又逐渐上升,这已经属于高忠诚用户了,尽量维护好这批高质量用户。
消费两次以上用户的生命周期接近消费只有一次用户的两倍,所以如何在用户首次消费后进行有效的引导促使其多次消费,可以有效的提高用户的生命周期和用户质量
len(life_time[life_time.life_time > 400]) #结果:3651
3651
我们同时计算了用户生命周期大于400天的人数,为3651人,跟我们之前的高贡献率人数非常接近,暗示我们这批人应该接近同一批人,也就是说消费金额高的用户往往他们的生命周期也越长,这也符合我们的常规认识。
#链接两表
user_purchase_retention = pd.merge(left = user_purchase, right = order_date_min.reset_index(), how = 'inner', on = 'user_id', suffixes=('', '_min'))
user_purchase_retention['order_date_diff'] = user_purchase_retention.order_date-user_purchase_retention.order_date_min
user_purchase_retention['date_diff'] = user_purchase_retention.order_date_diff.apply(lambda x: x/np.timedelta64(1,'D'))
这里用到merge函数,它和SQL中的join差不多,用来将两个DataFrame进行合并。我们选择了inner 的方式,对标inner join。即只合并能对应得上的数据。这里以on=user_id为对应标准。这里merge的目的是将用户消费行为和第一次消费时间对应上,形成一个新的DataFrame。suffxes参数是如果合并的内容中有重名column,加上后缀。
然后将order_date和order_date_min相减。获得一个新的列,为用户每一次消费距第一次消费的时间差值,然后在整除np.timedelta64(1,‘D’)来获得日期差数值。
user_purchase_retention.head()
|
user_id
|
order_products
|
order_amount
|
order_date
|
order_date_min
|
order_date_diff
|
date_diff
|
0
|
1
|
1
|
11.77
|
1997-01-01
|
1997-01-01
|
0 days
|
0.0
|
1
|
2
|
1
|
12.00
|
1997-01-12
|
1997-01-12
|
0 days
|
0.0
|
2
|
2
|
5
|
77.00
|
1997-01-12
|
1997-01-12
|
0 days
|
0.0
|
3
|
3
|
2
|
20.76
|
1997-01-02
|
1997-01-02
|
0 days
|
0.0
|
4
|
3
|
2
|
20.76
|
1997-03-30
|
1997-01-02
|
87 days
|
87.0
|
def convert(date):if date==0.0:return np.NaNelif 0<date<=30:return '(0,30]'elif 30<date<=60:return '(30,60]'elif 60<date<=90:return '(60,90]'else:return date
user_purchase_retention['date_diff_bin']=user_purchase_retention['date_diff'].apply(convert)
user_purchase_retention.dropna(inplace=True)
然后将时间差值分桶。我这里分成0~30天内,30~60天内,60~90天等,代表用户当前消费时间距第一次消费属于哪个时间段呢。这里date_diff=0并没有被划分入0~30天,因为计算的是留存率,如果用户仅消费了一次,留存率应该是0。另外一方面,如果用户第一天内消费了多次,但是往后没有消费,也算作留存率0。
bin = [0,30,60,90,120,150,180,365]
user_purchase_retention['date_diff_bin'] = pd.cut(user_purchase_retention.date_diff, bins = bin)
pivoted_retention= user_purchase_retention.groupby(['user_id','date_diff_bin']).order_amount.sum().unstack()
pivoted_retention_trans = pivoted_retention.fillna(0).applymap(lambda x: 1 if x >0 else 0)
pivoted_retention_trans.head()
date_diff_bin
|
(0, 30]
|
(30, 60]
|
(60, 90]
|
(90, 120]
|
(120, 150]
|
(150, 180]
|
(180, 365]
|
user_id
|
|
|
|
|
|
|
|
3
|
0
|
0
|
1
|
0
|
0
|
0
|
1
|
4
|
1
|
0
|
0
|
0
|
0
|
0
|
1
|
5
|
1
|
1
|
0
|
1
|
1
|
1
|
1
|
7
|
0
|
0
|
0
|
0
|
0
|
0
|
1
|
8
|
0
|
1
|
0
|
0
|
0
|
1
|
1
|
直接用金额表达不出留存率,我们还是按照之前的算法,把消费过的转换成1,未消费过的转换成0,毕竟留存率看的是是否有消费。
ax = (pivoted_retention_trans.sum()/pivoted_retention_trans.count()).plot.bar()
ax.set_xlabel('时间跨度(天)')
ax.set_ylabel('百分数(%)')
ax.set_title('各时间段的用户留存率')
plt.show()
如图,第一个月的留存率高达46%,第二个月就下降到35%左右,之后几个月逐渐趋近稳定在25%左右,说明后面的用户逐渐开始稳定下来,说明通过用户在前三个月的使用中,逐渐开始喜爱本店铺的业务或者转换别家的店铺,所以这时候流失率会增大。从运营的角度来看,留存的玩家都是至少消费两次以上的玩家,比起拉新用户来讲,如何提高这些已消费玩家的持续消费是运营的重点,有些活动还是营销,最好放在前三个月,特别是第一个月来进行比较好。为了更好的确定营销的最好时机,我们来看看用户的平均购买周期。
(6)平均购买周期
平均购买周期:用户的两次消费行为的时间间隔。
#创建函数,返回时间差
def diff(group):d= abs(group.date_diff - group.date_diff.shift(-1))return d
last_diff = user_purchase_retention.groupby('user_id').apply(diff)
ax = last_diff.hist(bins = 20)
ax.set_xlabel('时间跨度(天)')
ax.set_ylabel('人数(人)')
ax.set_title('用户平均购买周期直方图')
plt.show()
如图,典型的长尾分布,大部分用户的消费间隔确实比较短。不妨将时间召回点设为消费后立即赠送优惠券,消费后10天询问用户CD怎么样,消费后30天提醒优惠券到期,消费后60天短信推送。
电商用户数据分析报告相关推荐
- 电商用户行为数据分析实战(MySQL +PowerBI)
目录 一.项目概况 二.数据源 三.数据清洗 3.1 选择子集导入,匹配适合的数据类型 3.2 列重命名 3.3重复值处理 3.4 缺失值处理 3.5 异常值处理 从timestamps字段中提取日期 ...
- 对电商用户的数据分析!
一.以淘宝等为主的用户分析场景 以淘宝.京东和拼多多为典型的用户+商品消费场景,是国内用户量最多的业务场景,也是产生利润最多的业务场景.在这其中,客户拥有最大的自主权,如何有效地加以利用或辨别客户在行 ...
- 基于 flink 的电商用户行为数据分析【8】| 订单支付实时监控
本文已收录github:https://github.com/BigDataScholar/TheKingOfBigData,里面有大数据高频考点,Java一线大厂面试题资源,上百本免费电子书籍,作者 ...
- 数据分析实战(六):英国电商用户行为分析
案例:英国电商用户行为数据分析 Part 1. 数据获取 1.1 数据集简介 https://archive.ics.uci.edu/ml/datasets/online+retail# 该数据集为英 ...
- 基于 flink 的电商用户行为数据分析【9】| 电商常见指标汇总 + 项目总结
本文已收录github:https://github.com/BigDataScholar/TheKingOfBigData,里面有大数据高频考点,Java一线大厂面试题资源,上百本免费电子书籍,作者 ...
- 尚硅谷大数据技术之电商用户行为数据分析
尚硅谷大数据技术之电商用户行为分析 第1章 项目整体介绍 1.1 电商的用户行为 电商平台中的用户行为频繁且较复杂,系统上线运行一段时间后,可以收集到大量的用户行为数据,进而利用大数据技术进行深入挖掘 ...
- mysql统计今日首充用户_电商用户行为MySQL分析
一:分析背景 近年来电商行业发展迅猛,发展的模式也从最开始粗糙的供给结构开始向精细化电商发展.我国政策也指出: 促进电子商务发展是推进供给侧结构性改革重要的途径.因此分析用户的行为,可打造爆款商品,爆 ...
- 电商用户价值分析(应用RFM模型)
有很多种分析电商用户价值的方式,今天来谈谈传统企业和电商用得较多的RFM模型.在众多的客户细分模型中,RFM模型是被广泛提到和使用的. 一.为什么分析 内部因素:增加用户的好感度,因为不同的活动会有 ...
- 电商用户价值分析——基于RFM模型、KMeans聚类
电商用户价值分析--基于RFM模型.KMeans聚类 一.背景 二.RFM模型.KMeans聚类 三.分析框架 四.具体分析 1. 导入所需的库 2. 导入数据 3. 数据清洗 4. 数据分析 4.1 ...
最新文章
- 研究学者、医师与产业投资者齐聚一堂,将碰撞出何种火花?
- Windows Mobile使用Web Service上传和下载二进制数据流
- 第十、十一周项目一-点-圆-圆柱类族的设计(1)
- Entrust - Laravel 用户权限系统解决方案
- Winform 三层架构小例子
- 【JAVA】使用IntelliJ IDEA创建Java控制台工程
- 实现基于虚拟用户的邮件系统架构
- Vue项目如何实现国际化?分享一下基于vue-i18n实现国际化的经验
- 买电脑主要看什么配置_买笔记本电脑主要看什么? 配置要什么样的才算好?...
- 小程序助手多功能微信小程序反编译工具
- selenium获取新页面标签页(只弹出一个新页面的切换)
- 两向量点乘坐标运算_向量点乘(内积)和叉乘(外积、向量积)概念及几何意义解读...
- 迅雷插件会导致IE8假死
- 在CityEngine中制作绘制路网
- PPT中正文和标题字体的选择技巧
- PSINS_Toolbox使用心得1
- 独立开发变现周刊(第41期):一个开源项目一个人每月收入8万美金
- 外媒评选出来的中国五大人工智能(AI) 公司
- AGC中振幅与dB的转换关系
- 【POJ3208】 (DP)
热门文章
- CentOs给文件授权命令
- Python学习之解决“千年虫”问题
- 一个实例用matlab分别画出拟合和插值,数学建模培训之四--拟合与插值专题(hzd).doc...
- JAVA面试题解惑系列(八)——聊聊基本类型(内置类型)
- R语言进行线性回归的拟合度
- 数字身份认证已经成为数字身份的关键命题
- 2016年12月12日学习总结----各类链表操作
- id 查找apple_厉害!竟能利用苹果ID锁找回丢失手机?
- 基于vue的仿饿了么webapp
- 使用easyBCD在Win10下安装Ubuntu16.04LS双系统详细教程