数据集来自:http://www.brucehardie.com/datasets/
工具:jupyter,python
一、项目背景
CDNow曾经是一家在线音乐零售*台,后被德国波泰尔斯曼娱乐集团公司出资收购,其资产总价值在最辉煌时曾超过10亿美元。本文主要通过分析CDNow网站的用户购买明细来分析该网站的用户消费行为,使运营部门在营销时更加具有针对性,从而节省成本,提升效率
二、主要从以下方面进行分析
1、用户整体消费分析
2、用户个人消费分析
3、用户消费周期分析
4、用户分层:RFM模型
三、分析过程
3.1 导入数据
#导入需要的库 import pandas as pd import numpy as np import matplotlib.pyplot as plt from datetime import datetime plt.style.use(\'ggplot\') plt.rcParams[\'font.sans-serif\']=[\'SimHei\'] #显示中文标签 plt.rcParams[\'axes.unicode_minus\']=False #显示负数 #导入数据 columns = [\'用户ID\',\'购买日期\',\'订单数\',\'订单金额\'] df = pd.read_table(\'CDNOW_master.txt\',names = columns,sep = \'\s+\') #查看前五行 df.head()
- 需要注意的是,原数据集没有表头,所以在导入时自定义表头名称
- 观察数据,数据中存在一个用户在同一天或不同天下多次订单的情况,如用户ID为2的用户就在1月12日买了两次
3.2 数据清洗
3.2.1 查看数据情况,观察数据是否有空值
df.info()
从结果可以看到原始数据没有空值,同时注意到购买日期的字段类型是int64,不是时间类型,后续的处理需要进行类型转换
3.2.2 查看是否有重复值
df[df.duplicated() == True]
可以看到存在255条重复数据,将这些重复值删去
df = df.drop_duplicates() #查看是否删除成功 df.info()
从结果可以看到重复数据已经删去
3.2.3 对购买日期字段转换类型
df[\'购买日期\'] = pd.to_datetime(df.购买日期,format = \'%Y%m%d\') #Y四位数的日期部分,y表示两位数的日期部分 df[\'月份\'] = df.购买日期.values.astype(\'datetime64[M]\') df.head()
- astype也可以将时间格式进行转换,比如[M]转化成月份。这里将月份作为消费行为的主要事件窗口
- 至此数据处理结束
3.3 描述性统计
#对订单数和订单金额做描述性统计 df[[\'订单数\', \'订单金额\']].describe()
从describe描述统计中可以看出:
- 用户每个订单*均购买2.41个商品,每个订单*均消费35.96元
- 关于订单数,标准差为2.34,说明数据具有一定的波动性;中位数为2个商品,75分位数为3个商品,说明大部分订单的购买数量都不多;最大值在99个,数字比较高
- 订单金额和订单数情况类似,大部分订单都集中在小额,少部分订单金额极高
- 一般而言,消费类的数据分布,都是长尾形态。大部分用户都是小额,而小部分用户贡献了收入的大头,俗称二八。初步判断CDNow网站消费符合二八分布
3.4 用户总体消费趋势分析
# 设置图的大小,添加子图 plt.figure(figsize=(20,15))
# 每月的总销售额 plt.subplot(221) df.groupby(\'月份\')[\'订单金额\'].sum().plot(fontsize=24) plt.title(\'总销售额\',fontsize=24) #每月的消费次数 plt.subplot(222) df.groupby(\'月份\')[\'购买日期\'].count().plot(fontsize=24) plt.title(\'消费次数\',fontsize=24) #每月的销量 plt.subplot(223) df.groupby(\'月份\')[\'订单数\'].sum().plot(fontsize=24) plt.title(\'总销量\',fontsize=24) #每月的消费人数 plt.subplot(224) df.groupby(\'月份\')[\'用户ID\'].apply(lambda x:len(x.unique())).plot(fontsize=24) plt.title(\'消费人数\',fontsize=24) plt.tight_layout() # 设置子图的间距
plt.show()
- 四个折线图的整体趋势基本一致,可以看出来,1997年前3个月的销量特别高,随之而来的销售额也是暴涨,在3月份之后骤然下降,接**稳
- 为什么会呈现这个原因呢?假设是用户身上出了问题,早期时间段的用户中有异常值,第二假设是各类促销营销,但这里只有消费数据,所以无法判断
- 另一方面,在2月到3月这段期间,可以发现消费人数稍有下降,但总销量与总销售额却依然上升,或许意味着3月份的用户中有需要重点发展的高价值客户
3.5 用户个体消费趋势分析
3.5.1 用户消费金额和消费次数的描述统计
#对用户进行分组 group_user = df.groupby(\'用户ID\').sum() group_user.describe()
- 从用户角度看,每位用户*均购买7张CD,最多的用户购买了1033张。
- 用户的*均消费金额100元,标准差是240;*均值和75分位接*,肯定存在小部分的高额消费用户,这部分用户拉高了*均值,这也符合二八法则
3.5.2 用户消费金额与消费次数的规律
#绘制散点图。查询条件:订单金额小于5000 group_user.query(\'订单金额 < 5000\').plot.scatter(x=\'订单金额\',y=\'订单数\')
绘制用户的散点图,用户比较健康而且规律性很强。因为这是CD网站的销售数据,商品比较单一,金额和商品量的关系也因此基本呈线性关系,离群点较少
3.5.3 用户消费金额的直方图(观察二八分布)
group_user[\'订单金额\'].plot.hist(bins = 20)
- 从图上看出,用户的消费呈集中趋势,可能是有个别的极大值干扰导致
- 可以排除极大值再看看分布情况
#查询条件:订单金额小于1000 group_user.query("订单金额 < 1000")[\'订单金额\'].plot.hist(bins=20)
对数据进一步探索,
#高消费用户数。因为已经对用户分组,所以求订单数的次数就是用户数 print(\'高消费用户数:\',group_user[group_user[\'订单金额\']>200][\'订单数\'].count()) #高消费用户贡献的销售额 print(\'高消费用户贡献的销售额:\',group_user[group_user[\'订单金额\']>200][\'订单金额\'].sum())
#全部用户的消费金额 print(\'总消费金额:\',group_user[\'订单金额\'].sum())
从上述结果可以看到:
- 大部分用户的消费能力并不高,消费50元以下的用户超过12000人,超过总用户数50%;
- 高消费用户( >200元 )不超过3000人,约占总用户数12.7%,但这部分用户贡献的销售额约占总销售额56%;
- 虽然存在高消费用户群,但是大部分的用户还是集中在比较低的消费档次
3.5.4 用户消费次数的分布图
group_user.query(\'订单数 < 100\').订单数.hist(bins = 40)
- 大部分用户购买CD的数量都是在3张以内,购买大量CD的用户数量并不多
3.6 用户消费周期分析
3.6.1 用户购买周期
#每个用户的购买时间间隔 order_diff = df.groupby(\'用户ID\').apply(lambda x:x[\'购买日期\'] - x[\'购买日期\'].shift()) order_diff.head(10)
order_diff.describe()
- *均每个用户的购买时间间隔是69天,间隔最长的是533天。想要召回用户,在60天左右的消费间隔是比较好的;
- 绝大部分用户的消费周期都低于100天
3.6.2 用户消费周期分布
plt.figure(figsize=(15,5)) plt.hist((order_diff / np.timedelta64(1, \'D\')).dropna(), bins = 50) plt.xlabel(\'消费周期\',fontsize=24) plt.ylabel(\'频数\',fontsize=24) plt.title(\'用户消费周期分布图\',fontsize=24);
- 大部分用户的消费周期比较短,可以考虑在消费者完成购买行为后30天左右发放优惠券,鼓励用户再次消费
3.6.3 用户生命周期
#消费的最后一天减去第一天得周期 orderdt_min=df.groupby(\'用户ID\').购买日期.min()#第一次消费 orderdt_max=df.groupby(\'用户ID\').购买日期.max()#最后一次消费 (orderdt_max-orderdt_min).head()
#用户*均生命周期 (orderdt_max-orderdt_min).mean()
- *均生命周期134天,这个值还是很高的,考虑到高消费用户群的存在,看看生命周期分布
((orderdt_max-orderdt_min)/np.timedelta64(1,\'D\')).hist(bins=15)
从直方图看:
- 大部分用户只消费了一次,即生命周期集中在0天
- 消费了一次以上的老客看起来生命周期分布较均匀,接下来可以单独看老客的分布
#计算所有消费过两次以上的老客的生命周期 life_time = (orderdt_max - orderdt_min).reset_index() plt.figure(figsize=(10,5)) life_time[\'life_time\'] = life_time.购买日期 / np.timedelta64(1,\'D\') life_time[life_time[\'life_time\'] > 0][\'life_time\'].hist(bins = 100, figsize = (12,6))
从直方图看:
- 用户生命周期呈现双峰趋势,20天内生命周期的用户是一个高峰,400至500天内生命周期的用户是另一个高峰
- 根据此情况,应该在20天内对客户进行引导,促进其再次消费并形成消费习惯,延长其生命周期;在100至400天的用户,也要根据其特点推出有针对性的营销活动,引导其持续消费
3.7 用户分层-RFM模型
rfm = df.pivot_table(index = \'用户ID\', values = [\'订单金额\',\'购买日期\',\'订单数\'], aggfunc = {\'订单金额\':\'sum\', \'购买日期\':\'max\', \'订单数\':\'sum\'}) # 日期的最大值与当前日期的差值为R rfm[\'R\'] = (rfm[\'购买日期\'].max() - rfm[\'购买日期\']) / np.timedelta64(1,\'D\') rfm.rename(columns = {\'订单金额\':\'M\', \'订单数\':\'F\'}, inplace=True) # 构建rfm模型公式 def get_rfm(x): level = x.apply(lambda x:\'1\' if x >= 0 else \'0\') label = level[\'R\'] + level[\'F\'] + level[\'M\'] d = {\'111\':\'重要价值客户\', \'011\':\'重要保持客户\', \'101\':\'重要挽留客户\', \'001\':\'重要发展客户\', \'110\':\'一般价值客户\', \'010\':\'一般保持客户\', \'100\':\'一般挽留客户\', \'000\':\'一般发展客户\'} result = d[label] return result rfm[\'label\'] = rfm[[\'R\',\'F\',\'M\']].apply(lambda x:(x-x.mean()) / x.std()).apply(get_rfm,axis=1)
#求和 rfm.groupby(\'label\').sum()
可以看到重要保持客户的消费金额是最高的
#统计各层次用户数 rfm.groupby(\'label\').size()
可以看到,一般挽留用户的消费人数排名第一,有14074人,重要保持客户排名第二,有4554人,与一般挽留用户差距比较大,但累计消费金额最多,业务方可以根据结果对客户分类运营,降低营销成本,提高ROI