CDNow网站消费数据分析(Python)

本文结构:

CDNow网站消费数据分析(Python)_第1张图片
CD网站用户购买数据分析思路.png

一、项目背景:

1.1、背景介绍:

CDNow曾经是一家在线音乐零售平台,后被德国波泰尔斯曼娱乐集团公司出资收购,其资产总价值在最辉煌时曾超过10亿美元。
本文主要通过分析CDNow网站的用户购买明细来分析该网站的用户消费行为,使运营部门在营销时更加具有针对性,从而节省成本,提升效率。

1.2、数据源介绍

本次分析数据来源CDNow网站的用户在1997年1月1日至1998年6月30日期间内购买CD交易明细。

数据源:
地址:https://pan.baidu.com/s/11sPLzNRUpGghkaIfs1ARfQ
提取码:re0d

字段:
数据集一共有用户ID,购买日期,订单数,订单金额四个字段。

二、提出问题

参照数据分析思路中的数据分析部分。

三、数据处理

3.1、导入数据

3.1.1、导入要用到的模块

import pandas as pd 
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
from datetime import datetime # 用来将字符串转换成时间类型
plt.style.use('ggplot') # 更改设计风格,使用自带形式进行美化
%matplotlib inline  # %内置的命令,jupyter专有的定义,比如在pycharm不常用到,inline意思是做图之后可以在html页面的单元格进行显示

3.1.2、导入数据源

# 1.查看数据发现数据中未包含列标题,所以需要导入数据的时候添加列名
columns = ["用户id","购买日期","订单数量","订单金额"] 
# 2.源数据为txt形式的,所以用pd.read_table;字符串是空格分割,所以用\s+表示匹配任意空白字符(csv数据分分隔是以逗号形式)
data = pd.read_table(r"路径",names=columns,sep='\s+')
  • 说明:
    消费行业或者是电商行业一般都是通过用户id、购买日期、订单数、订单金额这四个字段来分析。基本上这四个字段就可以进行非常丰富的分析了。

3.1.3、查看数据基本信息

# 查看数据字段和记录,默认显示前5行数据
data.head(10) 
CDNow网站消费数据分析(Python)_第2张图片
image.png
  • 观察数据发现:
    数据中存在同一个用户同一天多次购买和不同天多次购买的情况,所以数据集中每一条记录代表用户的一次行为,因为日期最小粒度是天,所以四个字段无法唯一确定一条用户记录,那么同一用户同一天购买同样数量、同样金额的商品的行为是成立的,所以后续也不对此进行去重。
# 查看数据集详细信息(data.shape 以元组形式返回数据集的行数和列数)
data.info() 
image.png
  • 观察数据发现:
    数据总行数69659行,共4列,没有缺失值;
    购买日期是int类型的,需要转换成日期类型。其他数据类型正常。

3.2、描述性统计

# 按列对数据进行描述性统计,对用户数据特征进行整体性判断
data.describe()
CDNow网站消费数据分析(Python)_第3张图片
image.png
  • 观察数据发现:
    订单数量:平均值2.4,标准差2.3,稍有波动;75%位数3,说明大部分订单都不大;最大值为99,说明存在一部分疑似狂热粉丝;
    订单金额:平均值35,标准差36,数据波动较大,75%分位43,说明大部分用户都是小额消费;
    一般消费类的数据都是长尾形态,分布呈二八开,从整体数据来看基本符合这一规律。

3.3、数据处理

3.3.1、缺失值/空值处理

# 查看数据详细信息
data.info()
CDNow网站消费数据分析(Python)_第4张图片
image.png
  • 观察发现:
    数据集没有空值需要处理,数据非常干净,所以接下来要做的就是将购买日期类型转换成日期类型
  • 备注:
    当利用pandas进行数据处理的时候,经常会遇见数据类型的问题,当拿到数据的时候,首先要确定拿到的是正确的数据类型,如果数据类型不正确需要进行数据类型的转化,再进行数据处理。
  • 关于日期类型的转换可参考:https://www.cnblogs.com/onemorepoint/p/9404753.htmlfrom=singlemessage

3.3.2、数据类型转换

# 将购买日期由数值型转换成日期型
data['购买日期'] = pd.to_datetime(data['购买日期'],format = '%Y%m%d')
data.info() # 验证下数据类型,修改成功
CDNow网站消费数据分析(Python)_第5张图片
image.png
  • to_datetime函数说明:
    pd.to_datetime()可以将特定的字符串或者数字转换成时间格式,参数format用于匹配格式。例如19980101,%Y代表4位数的年1998,%y表示2位数的年98,%m表示月份01,%d表示天01;
    另外小时%h,分钟%M,注意与%m月份进行区分,秒是%s,日期1998-01-01,即是%Y-%m-%d。
  • 备注:
    格式转换的另一种方法:引入parser模块
    "from dateutil.parser import parse
    data["购买日期"] = [parse(str(time)) for time in data["购买日期"]]"
    想取月份方法差不多,只是需要注意,使用for循环返回值为字符串类型的,如果需要转换可以使用相应的转换方法
    data["月份"] = [i.month for i in data["购买日期"]]
    字符转转时间:datetime.strptime('2018-09-08','%Y-%m-%d')
    时间转字符串:datetime.strftime('%Y-%m-%d')
    数字转日期:先将数字转换成字符串,然后再将字符串转换成时间:datetime.strptime(str(int),'%Y%m%d')

3.3.3、新增列

# 新增列‘月份’,用于后续月维度的消费行为分析,具体提取哪个时间粒度的视情况而定
data["月份"] = data["购买日期"].values.astype('datetime64[M]')
data.head() # 验证操作结果
CDNow网站消费数据分析(Python)_第6张图片
image.png
  • astype()函数说明:
    这个函数可以将时间格式进行转换,代码中的[M]代表月份,需要注意的是这里返回值日期格式是用每月第一日代表当月,比如1997年01月所有日期均表示为1997-01-01;
    因为本案例中月份是主要的时间窗口,数据集是1997-1998之间18个月的数据集,所以确定唯一月份必须是年+月,这也是为什么这里采用astype的原因;

到这里对数据集的整理就已经可以满足分析的需求了,接下来进行具体的分析过程:

四、数据分析

4.1、用户总体消费在时间维度的趋势分析

我们的分析思路是由整体到局部,所以就先从整体看看数据的特征/规律:

# 建立画布,设置画布规格,添加子图
plt.figure(figsize=(16,15)) # 参数21表示画布长度,15表示画布宽度

# 4.1.1 每月总销售额趋势图
plt.subplot(321) # 3x2的画布,在第1块子画布画图
data.groupby("月份").订单金额.sum().plot(fontsize=25) #sontsize设置字号
plt.title("销售额趋势图",fontsize=25) 

# 4.1.2 每月总销量趋势图
plt.subplot(322) # 3x2的画布,在第2块子画布画图
data.groupby("月份")["订单数量"].sum().plot(fontsize=25)  # 用"."和用"[]"均可
plt.title("销量趋势图",fontsize=25)

# 4.1.3 每月消费次数趋势图
plt.subplot(323) # 3x2的画布,在第3块子画布画图
data.groupby("月份").订单数量.count().plot(fontsize=25)
plt.title("消费次数趋势图",fontsize=25)

# 4.1.4 每月消费人数趋势图
plt.subplot(324)# 3x2的画布,在第4块子画布画图
data.groupby("月份").用户id.nunique().plot(fontsize=25) # unique返回值是数组,nunique返回值是数值
plt.title("消费人数趋势图",fontsize=25)

# 4.1.5 每月用户平均消费金额
plt.subplot(325)# 3x2的画布,在第5块子画布画图
(data.groupby("月份").订单金额.sum() / data.groupby("月份").用户id.nunique()).plot(fontsize=25)
plt.title("每月用户平均消费金额",fontsize=25)

# 4.1.6 每月用户平均消费次数
plt.subplot(326) # 3x2的画布,在第6块子画布画图
(data.groupby("月份").订单数量.count() / data.groupby("月份").用户id.nunique()).plot(fontsize=25)
plt.title("每月用户平均消费次数",fontsize=25)

plt.tight_layout(pad=2) # 用于设置子图见的间距
plt.show() # 查看图表
CDNow网站消费数据分析(Python)_第7张图片
image.png
  • 观察数据发现:

前四个折线图:消费金额、销量、消费次数、用户数;
整体趋势基本一致,都是1997年前三个月数据异常高,随后骤降,然后趋于平稳;
为什么会出现这种异常现象呢?初步猜测要么是前三个月有异常值,要么就是受营销活动或者其他外部因素的影响,但这里没有更多的数据提供验证素材,所以也无法判断其原因。

后两个折线图:平均消费金额、平均消费次数;
用户平消费金额大概为50,前三个月低,后续在[47,5]波动;用户平均消费次数,也是前三个月低,后续上升,并维持在1.35次左右,说明cd消费这个复购率着实有点让人着急;

两个数据趋势基本一致,相较于销售额等数据,前面三个月数据反而低,后续才慢慢上升,结合两种趋势图来看,在消费次数和金额稳定的情况下,用户复购下降或者用户数下降会导致总体销售额和销量的下降,关于这个问题的验证,稍后在复购率和留存率的分析中再探究。

4.2、用户个体消费数据分析

总体看完,大概明白了整体趋势是一个怎样的状况,那么接下来就深入细节,从个体角度看看又呈现什么样的特点呢?

4.2.1、消费金额和消费次数的描述性统计分析

# 根据用户id分组,统计单个用户的订单数和订单额
group_userid = data.groupby("用户id").sum()
group_userid.head() # 查看数据
CDNow网站消费数据分析(Python)_第8张图片
image.png
# 计算描述统计数据
group_userid.describe()
CDNow网站消费数据分析(Python)_第9张图片
image.png
  • 观察数据发现:
    订单数量:平均值7,标准差16,说明波动较大;中位数3,四分位数7,说明大部分用户购买数量都是小于7次的;最大值1033,无疑这种人够狂热的;
    订单金额:平均数106,接近75%分位数,中位数为50%,而标准差240,所以必然存在部分高额消费用户,这也符合二八法则,后续我们在用户质量的分析中查看用户销售额贡献率进行验证。

4.2.2、消费金额和消费次数关系(散点图)

# 查看消费数量随消费金额的变化关系
group_userid.plot.scatter(x="订单数量",y="订单金额",fontsize=13)
plt.title("消费金额和消费次数关系",fontsize= 18)

# 如果希望对销售金额设置限制条件,排除异常值,可以使用query()函数:
# group_userid.query('订单金额 < 4000').plot.scatter(x='订单金额',y='订单数')
CDNow网站消费数据分析(Python)_第10张图片
image.png
  • 观察数据发现:
    二者呈现线性关系,只有极少数异常值,考虑到这是CD网站的销售数据,商品单一,金额和数量呈线性关系属于正常现象。
  • query()函数:
    query()函数的使用对象是dataframe,而不是series,这里data.groupby("user_id").sum()返回的结果是dataframe类型的,但是data.groupby("user_id")["order_amount"].sum()返回值是一个sereis,所以如果在这使用query()函数如.query("order_amount < 100")就会报错。

4.2.3、消费金额分布(直方图)

group_userid["订单金额"].plot.hist(bins=30,fontsize=15) # bins=30,就是分成30组
# group_userid["订单金额"]和group_userid.订单金额返回值均为float64类型
# 纵坐标代表的是用户id,也就是人数。因为group_userid.订单金额实际上是个series
plt.xlabel("订单金额",fontsize=15)
plt.ylabel("订单人数",fontsize=15)
plt.title("消费金额分布图",fontsize=18)
CDNow网站消费数据分析(Python)_第11张图片
image.png
  • 观察图形:
    从图来看,用户消费集中趋势十分明显,我分成30组都看不到什么内容,无疑这里受到了异常值干扰,所以得把异常值排除一下再看看有什么规律。
# 只看订单金额小于1000的用户的消费金额
group_userid.query("订单金额 < 1000").订单金额.plot.hist(bins=40,fontsize=15)
plt.xlabel("订单金额",fontsize=15)
plt.ylabel("订单人数",fontsize=15)
plt.title("消费金额分布图",fontsize=18)
CDNow网站消费数据分析(Python)_第12张图片
image.png
  • 观察发现:
    数据整体呈现长尾分布形态,大部分用户消费金额在40元以内,消费金额大于200的不超过2000人,用户消费能力着实不高,高消费基本看不到,这跟消费行为行业规律也是相符的

4.2.4、消费次数分布(直方图)

# 有了前面的经验,结合描述性分析的结果,这里我们先对数据进行筛选后再制作可视化查看
group_userid.query("订单数量 < 80").订单数量.plot.hist(bins=20,fontsize=15)
plt.xlabel("订单数量",fontsize=15)
plt.ylabel("订单人数",fontsize=15)
plt.title("消费次数分布图",fontsize=18)
CDNow网站消费数据分析(Python)_第13张图片
image.png
  • 观察发现:
    消费次数在5次以内的人数还是占据绝大部分,高频消费人数非常少。

4.3、用户消费周期分析

4.3.1、用户购买周期(按订单日期)

4.3.1.1、用户消费周期描述性统计分析

# 先把每个用户每次购买的时间间隔计算出来,这里用到的方法是shift()
# 定义一个函数,用于计算购买时间间隔
def diff(data):  
    d = data.购买日期 - data.购买日期.shift(1)
    return d
# 计算购买时间间隔
purchase_period = data.groupby("用户id").apply(diff) # 需要注意的是 这里的返回值是一各timedelta,不能直接做直方图的
purchase_period.head(10) 
# 这里使用函数实现的意义是练习函数编写能力,不用编写函数实现也很简单
CDNow网站消费数据分析(Python)_第14张图片
image.png

上面就是每个用户每一次购买的时间间隔,下面计算用户购买间隔的描述性统计数据

purchase_period.describe() #用户消费周期描述性统计分析
CDNow网站消费数据分析(Python)_第15张图片
image.png
  • 观察数据:
    可以发现用户平均购买间隔(周期)为68天,标准差91,中位数31,75%分位数89,这说明大部分用户的购买周期都不超过90天,想要召回用户,60天左右的时间消费间隔是比较好的。
  • 关于shift()函数的一些补充说明:
    shift函数是一个偏移函数,和excel上的offset差不多。具体参数可以看下面的例子:
# 定义一组series,用于测试
x = pd.Series([1,2,3,4,5])
x
CDNow网站消费数据分析(Python)_第16张图片
image.png
x.shift() # 默认参数是1,也就是向下偏移一个单元/位置
CDNow网站消费数据分析(Python)_第17张图片
image.png
x.shift(-1) # 向上偏移1个位置
CDNow网站消费数据分析(Python)_第18张图片
image.png
  • 关于shift函数的说明:
    x.shift()是往下偏移一个位置;
    x.shift(-1)是往上偏移一个位置;
    默认axis=0,也就是上下偏移,加参数axis=1则是左右偏移;
    当我想求用户下一次距本次消费的时间间隔,用shift(1)减当前值即可。
    案例用的diff函数便借助shift方法,巧妙的求出了每位用户的两次消费间隔,若为NaN,则没有下一次。

4.3、用户消费周期分布

plt.figure(figsize=(15,6)) # 建立一张画布
plt.hist(purchase_period / np.timedelta64(1,"D"),bins= 30) # 将timedelta类型转换成数值类型
plt.xlabel("购买周期",fontsize=18)
plt.ylabel("人数",fontsize=18)
plt.title("用户消费周期愤分布",fontsize=25)
CDNow网站消费数据分析(Python)_第19张图片
image.png
  • 观察图表发现:
    仍然是长尾形态分布,大部分用户消费周期是很短的;
    不妨将用户找回点设置为消费后立即赠送优惠券,10天后回访用户体验,30天后提醒优惠券即将到期,60天后发送召回短信。
  • 关于purchase_period / np.timedelta64(1,"D")用处的说明:
    因为这里的数据类型是timedelta时间,它无法直接作出直方图,所以先换算成数值。换算的方式直接除timedelta函数即可,这里的np.timedelta64(1, 'D'),D表示天,1表示1天,作为单位使用的,两者相除就是周期的天数,是数值类型的就可以用于直方图的制作了。

4.3.2、用户生命周期(按用户首次和最后一次消费时间)

接下来计算每一位用户生命周期,这里定义用户第一次消费至最后一次消费为整个用户生命周期全过程。

# 用户首次购买时间
orderdate_min = data.groupby("用户id").购买日期.min()
orderdate_min.head()
CDNow网站消费数据分析(Python)_第20张图片
image.png
# 用户最后一次购买时间
orderdate_max= data.groupby("用户id").购买日期.max()
orderdate_max.head()
CDNow网站消费数据分析(Python)_第21张图片
image.png

4.3.2.1用户生命周期描述性统计分析

# 用户生命周期计算:最后一次减第一次
life_cycle = (orderdate_max - orderdate_min)
life_cycle.head()
CDNow网站消费数据分析(Python)_第22张图片
image.png
# 做描述性行统计
life_cycle.describe()
CDNow网站消费数据分析(Python)_第23张图片
image.png
  • 观察数据发现:
    用户平均生命周期为134天,中位数是0,75%分位数是294,最大值544,这说明用户两极分化极其严重?流失率很高,忠诚客又是死忠粉吗?显然仅凭这些信息还不足以判断,接下来看下生命周期的分布情况,验证一下这种猜想。

4.3.2.2、用户生命周期分布

# life_cycle数据类型是timedelta,所以得先转换成数值
(life_cycle / np.timedelta64(1,"D")).hist(bins=30)
CDNow网站消费数据分析(Python)_第24张图片
image.png
  • 观察图表:
    大部分用户生命周期集中在0天,说明非常多的用户仅消费了一次就流失掉了,结合消费行业规律以及二八法则,用户流失是不可避免的,这部分值在此项分析中无疑是干扰值,所以我们将其排除后再进行观察分析:

先将 life_cycle数据结构由series转换成Dataframe,此处用到的方法是重置索引,方便后面筛选过滤

life_cycle = (orderdate_max - orderdate_min).reset_index()
life_cycle.head()  # 时间间隔仍为timedelta格式,后续做直方图还得转换成数值类型
CDNow网站消费数据分析(Python)_第25张图片
image.png
# 过滤掉仅消费一次的用户后,用户生命周期的分布
life_cycle["购买日期"] = life_cycle.购买日期 / np.timedelta64(1,"D") # 转换成数值类型
life_cycle.head()
CDNow网站消费数据分析(Python)_第26张图片
image.png
plt.figure(figsize=(15,6))
life_cycle[life_cycle["购买日期"] > 0].购买日期.hist(bins=100) # hist没有fontsize属性
CDNow网站消费数据分析(Python)_第27张图片
image.png
  • 观察图表:
    整体分布呈双峰结构,15天以内生命周期的用户是第一个高峰,第二个高峰出现在400-500之间;
  • 针对上述情况:
    0-20:应在用户消费20以内对其进行引导,促使其进行再次消费并逐步培养用户消费习惯,从而延长其生命周期;
    100-400:根据其偏好特征推出个性化的营销活动,引导其消费,促使其进入平台活跃用户阶段
    400以上:此部分用户已经是网站的忠诚客用户,需要增加其留存率,做好相关留存措施,尽可能让其保持活跃度

筛选掉仅消费一次后的用户之后是上述情况,再来看看此时的用户生命周期的描述性统计数据有什么变化:

life_cycle[life_cycle["购买日期"] > 0].购买日期.describe()
CDNow网站消费数据分析(Python)_第28张图片
image.png
  • 观察数据:
    用户平均生命周期为276,比未进行筛选之前的134大了一倍,那么也就是说如果用户在第一次消费之后对其进行再次消费的引导,可使用户生命周期增加1倍。

4.4、用户质量分析

4.4.1、仅消费一次用户占总用户的比例

# 按用户id分组计算其购买日期的最大值和最小值
group_userid_date_min_max = data.groupby("用户id")["购买日期"].agg(["min","max"]).reset_index()
group_userid_date_min_max.head() # 返回值是一个dataframe,这点很关键
CDNow网站消费数据分析(Python)_第29张图片
image.png
# 统计用户首次下单时间和最后一次下单时间相等的人数,然后做饼图观察比例
new_user = (group_userid_date_min_max["min"] == group_userid_date_min_max["max"] ).value_counts()
new_user
CDNow网站消费数据分析(Python)_第30张图片
image.png
# 用饼图观察百分比
plt.figure(figsize=(8,5))
plt.pie(new_user,
        autopct="%.1f%%",
        shadow= True,
        explode=[0.07,0], 
       textprops={"fontsize":15},)
# 设置x,y轴刻度一致,这样饼图才能是圆的
plt.axis("equal")
plt.legend(["仅消费一次人数","费多次人数"])

#autopct,圆里面的文本格式,%.1f%%表示小数有三位,整数有一位的浮点数
#shadow,饼是否有阴影
#explode,将某部分爆炸出来, 使用括号,将第一块分割出来,数值的大小是分割出来的与其他两块的间隙
#startangle,起始角度,0,表示从0开始逆时针转,为第一块。一般选择从90度开始比较好看
#pctdistance,百分比的text离圆心的距离
#patches, l_texts, p_texts,为了得到饼图的返回值,p_texts饼图内部文本的,l_texts饼图外label的文本
CDNow网站消费数据分析(Python)_第31张图片
image.png
  • 观察图表:
    仅消费一次的用户占比已经超过一半了,这说明运营出了问题,留存效果不佳。

4.4.2、复购率分析

复购率:
在某时间窗口内,消费两次及以上的用户在总消费用户中占比。
这里的时间窗口是月,如果一个用户在同一天下了两笔订单,这里也将他算作复购用户

#每个用户在每月的订单数
pivoted_data=data.pivot_table(index='用户id',columns='月份',values='购买日期',#pivot_table透视表
                       aggfunc='count').fillna(0) #某些用户在某月没有消费过,用nan表示,这里用0填充

pivoted_data.head()
CDNow网站消费数据分析(Python)_第32张图片
image.png
  • 代码解释:
    在pandas中,数据透视有专门的函数pivot_table。
    pivot_table参数中:
    index是设置数据透视后的索引
    column是设置数据透视后的列
    简而言之,index是你想要的行,column是想要的列。

案例中,我希望统计每个用户在每月的订单量,所以用户id是index,月份是column。values是将哪个值进行计算,aggfunc是用哪种方法。于是这里用values=购买日期和aggfunc=count,统计里order_dt出现的次数,即多少笔订单。
使用数据透视表,需要明确获得什么结果。有些用户在某月没有进行过消费,会用NaN表示,这里用fillna填充。

#转换:消费2次以上记为1,消费1次记为0,消费0次记为NAN  
#applymap针对dataframe所有数据
pivoted_data_trans=pivoted_data.applymap(lambda x: 1 if x>1 else np.nan if x==0 else 0)
pivoted_data_trans.head()
CDNow网站消费数据分析(Python)_第33张图片
image.png

applymap针对DataFrame里的所有数据。用lambda进行判断,因为这里涉及了多个结果,所以要两个if else,记住,lambda没有elif的用法。

#count统计所有非空数据个数表示总消费用户数,sum计算非0数据的和表示消费两次以上的用户数
repurchase_rate = pd.DataFrame(pivoted_data_trans.sum()/pivoted_data_trans.count()).reset_index()
repurchase_rate.columns = ["日期", "复购率"]
repurchase_rate["日期"] = repurchase_rate.日期.astype(str).apply(lambda x:x[0:8])

plt.figure(figsize = (15,6))
plt.plot(repurchase_rate.日期, repurchase_rate.复购率)
plt.xlabel("日期", fontsize=18)
plt.ylabel('复购率',fontsize=18)
plt.title('复购率的变化',fontsize=25)
CDNow网站消费数据分析(Python)_第34张图片
image.png
  • 观察图表发现:
    前三个月因为大量新用户加入的关系,新客的复购率并不高,1月新客们的复购率只有6%左右;
    而之后,复购率比较稳定,在20%附近波动。
    单看新客和老客,复购率有三倍左右的差距。
  • 逻辑说明:
    用sum和count相除即可计算出复购率。因为这两个函数都会忽略NaN,而NaN是没有消费的用户,count不论0还是1都会统计,所以是总的消费用户数,而sum求和计算了两次以上的消费用户。这里用了比较巧妙的替代法计算复购率,SQL中也可以用。

4.4.3、回购率分析

回购率:
某一个时间窗口内消费的用户,在下一个时间窗口仍旧消费的占比。
比如,1月消费用户是1000,他们中有300个用户2月依然消费,那么回购率就是30%。
这里的维度是月,所以一个月内消费多次计为一次。

# 建立用户每月消费的数据透视表
group_month_user_order = data.pivot_table(index= "用户id",
                                         columns= "月份",
                                         values="订单金额", 
                                         aggfunc= "mean").fillna(0)
columns_month=data.月份.sort_values().astype('str').unique() # 定义的列名后续还会用到
group_month_user_order.columns = columns_month
group_month_user_order.head()
CDNow网站消费数据分析(Python)_第35张图片
image.png

因为这里只需要用户当月有消费记录即可,所以只是随便找一个字段用于计数,这里会用的是订单金额的平均值,使用订单数量或者sum也是完全可以的

# 格式化数据,将有消费的记录为1,后续用sum求和,没有消费的记为0,后续用count计数
# 用applymap+lambda转换数据,只要有过购买,记为1,反之为0。
pivoted_repurchase = group_month_user_order.applymap(lambda x : 1 if x > 0 else 0)
pivoted_repurchase.head()
CDNow网站消费数据分析(Python)_第36张图片
image.png
# 定义一个函数,用于判断用户当月是否属于回流用户
def repurchase(table):
    status = []
    for i in range(18):
        # 先判断第一个月的消费情况
        if i == 0:
            if table[0] == 1:  #第一个月有消费
                status.append(0)
            else:             # 第一个月没有消费
                status.append(np.nan)
        # 判断后续17个月的消费情况
        else:
            if table[i] == 1: # 当月有消费
                if table[i - 1] == 1: # 上月有消费
                    status.append(1) # 回流用户
                else:               #上月无消费
                    status.append(0)
            else:               # 当月无消费
                if table[i - 1] == 1:   #上月有消费
                    status.append(0)
                else:                   #上月无消费
                    status.append(np.nan)
    return pd.Series(status,index=columns_month)  # 返回一个dataframe,使用pd.Series方法
pivoted_repurchase_return = pivoted_repurchase.apply(repurchase,axis=1)
pivoted_repurchase_return.head()
CDNow网站消费数据分析(Python)_第37张图片
image.png
  • 函数判断逻辑说明:
    新建一个判断函数,table是输入的数据,即用户在18个月内是否消费的记录,status是空列表,后续用来保存用户是否回购的字段。
    因为有18个月,所以每个月都要进行一次判断,需要用到循环。
    if的主要逻辑是:
    先判断第一个月:如果第一个月有消费,第二个月有消费,则为回流客户,记为1;如果第一个月有消费,第二个月没有消费,则为有过消费的记录,记为0;
    然后判断2-18个月: 如果上个月有消费,当月有消费,则为回流客户号,记为1,如果当月没有消费则记为0;如果上个月没有消费,当月有消费记为0,没有消费记为NAN,后续进行排除。
# 计算每月回购率,并将结果通过重置索引,添加列名保存为Datafram格式。 重置索引转换成dataframe
return_purchase_rate = (pivoted_repurchase_return.sum() / pivoted_repurchase_return.count()).reset_index()
return_purchase_rate.head() 
CDNow网站消费数据分析(Python)_第38张图片
image.png
# 添加列名
return_purchase_rate.columns = ["日期","回购率"] 
# 修改日期列名为 年+月 格式
return_purchase_rate["日期"] = return_purchase_rate.日期.astype(str).apply(lambda x:x[:-3])
return_purchase_rate.head()
CDNow网站消费数据分析(Python)_第39张图片
image.png
# 可视化展示观察特征
plt.figure(figsize=(15,6))
plt.plot(return_purchase_rate.日期,return_purchase_rate.回购率)
plt.xlabel("日期",fontsize= 25)
plt.ylabel("回购率",fontsize= 25)
plt.title("回购率变化情况",fontsize=25)
CDNow网站消费数据分析(Python)_第40张图片
image.png
  • 观察图表:
    在用户进入平台后,回购率随之快速上升,从1997-05开始基本稳定在17.5%-20%之间且有下降趋势,这说明用户留存率存在问题,所以应该在用户第一次消费后的几个月内通过营销策略积极引导其再次消费,甚至是持续消费。而对于持续消费的用户,也应该适时推出反馈老用户的活动,使其保持活跃和忠诚。这种精细化的运营策略同样适用于前面分析的用户复购率。
  • 复购率和回购率的区别:
    复购率的定义是在某时间窗口内消费两次及以上的用户在总消费用户中占比
    回购率是某一个时间窗口内消费的用户,在下一个时间窗口仍旧消费的占比

4.4.4、留存率分析

留存率是指用户在第一次消费后,有多少比率进行第二次消费。
建立计算留存率的基表,基表结构为统计不同用户每一次消费举例第一次消费的时间间隔 具体方法是通过merge连接用户数据和用户消费时间最小值,然后计算时间差,具体如下:

# 用户第一次消费的统计表,用于后续merge的子表,计算用户每一次的消费时间间隔
user_orderdate_min = data.groupby("用户id").购买日期.min().reset_index()
user_orderdate_min.head()
CDNow网站消费数据分析(Python)_第41张图片
image.png
# 选取data子集,用于建立基表的子表
data2 = data[["用户id","购买日期","订单数量","订单金额"]]
data2.head()
CDNow网站消费数据分析(Python)_第42张图片
image.png
# 然后将data2和user_orderdate_min进行merge,用于计算消费时间间隔,也是此部分计算的基表
user_retention = pd.merge(data2,user_orderdate_min,how="inner",on="用户id",suffixes=("","_min"))
user_retention.head()
CDNow网站消费数据分析(Python)_第43张图片
image.png
  • merger()函数说明:
    这里用到merge函数,它和SQL中的join差不多,用来将两个DataFrame进行合并。选择inner 的方式,对标inner join。即只合并能对应得上的数据。这里以on=用户id为对应标准。这里merge的目的是将用户消费行为和第一次消费时间对应上,形成一个新的DataFrame。suffxes参数是如果合并的内容中有重名column,加上后缀。除了merge,还有join,concat,前面用到过的是join。
# 新增一列 消费间隔,用户储存用户每次消费距离第一次消费的时间间隔
user_retention["消费间隔"] = user_retention.购买日期 - user_retention["购买日期_min"]
user_retention.head()
# 上面的返回值是timedelta类型的,将其转换成数值类型的
user_retention["消费间隔"] = user_retention.消费间隔 / np.timedelta64(1,"D")
user_retention.head()
CDNow网站消费数据分析(Python)_第44张图片
image.png
  • 将时间差值分桶:
    分成0~3天内,3~7天内,7~15天等。
    代表用户当前消费时间距第一次消费属于哪个时间段呢。这里date_diff=0并没有被划分入0~3天,因为计算的是留存率,如果用户仅消费了一次,留存率应该是0。另外一方面,如果用户第一天内消费了多次,但是往后没有消费,也算作留存率0。
# 将消费间隔分组
bin=[0,3,7,15,30,60,90,180,365]
# 新增一列 消费间隔_bin 用于储存分组后的数据
user_retention["消费间隔_bin"] = pd.cut(user_retention.消费间隔,bins=bin)
user_retention.head()
CDNow网站消费数据分析(Python)_第45张图片
image.png
# 建立数据透视表,用于统计不同时间窗口的用户消费金额
#用户第一次消费之后,后续各时间段的消费总额
pivoted_user_retention = user_retention.pivot_table(index='用户id',
                                                    columns='消费间隔_bin',
                                                    values='订单金额',
                                                    aggfunc=sum, # 有无引号均可
                                                    
                                                    # Pandas函数pivot_table会默认删除含有空值的行,用dropna=False保持NaN的值。
                                                    dropna=False) 
pivoted_user_retention.head()
CDNow网站消费数据分析(Python)_第46张图片
image.png

这样就把所有用户的所有消费时间间隔划分到了每一个组当中,下面先看下其描述性数据统计,然后进行格式化,制作直方图观察留存率的分布情况

# 用户留存描述统计,这里主要看下平均值
pivoted_user_retention.mean()
CDNow网站消费数据分析(Python)_第47张图片
image.png
  • 观察数据:
    虽然后面时间段的金额高,但是它的时间范围也宽广。从平均效果看,用户第一次消费后的0~3天内,更可能消费更多。
    接下来再看看每个组用户所占的比例,也即不同组的分布情况:
# 格式化 pivoted_user_retention 表,用于统计人数
pivoted_user_retention_tmp = pivoted_user_retention.applymap(lambda x: 1 if x > 0 else 0)
pivoted_user_retention_tmp.head()
CDNow网站消费数据分析(Python)_第48张图片
image.png
# 计算各组所占百分比,这里同样用的是sum和count,与之前一样。
((pivoted_user_retention_tmp.sum()/ pivoted_user_retention_tmp.count())).plot.bar(figsize=(10,6))
plt.xlabel('留存周期',fontsize=18)
plt.title('留存率占比',fontsize=25)
CDNow网站消费数据分析(Python)_第49张图片
image.png
  • 观察图表:
    只有2.5%的用户在第一次消费的次日至3天内有过消费,3%的用户在3~7天内有过消费。数字并不好看,CD购买确实不是高频消费行为。时间范围放宽后数字好看了不少,有20%的用户在第一次消费后的三个月到半年之间有过购买,27%的用户在半年后至1年内有过购买。
    从运营角度看,CD机营销在教育新用户的同时,应该注重用户忠诚度的培养,放长线掉大鱼,在一定时间内召回用户购买。

4.4.5、客户贡献率分析

按照用户id分组,对用户的消费金额/销量进行累计求和 ,然后与总销售额比,得到比率,横坐标是用户的id

4.4.5.1、用户销售额贡献分析

计算百分之多少的用户占了百分之多少的销售额

# 先将用户订单金额按升序排列,逐行累计订单金额,最后一行是订单总金额
user_amount = data.groupby("用户id").订单金额.sum().sort_values().reset_index()
user_amount.head()
CDNow网站消费数据分析(Python)_第50张图片
image.png
# 新增一列 计算订单金额的累加和
user_amount["累计订单额"] = user_amount.订单金额.cumsum()
user_amount.tail()
CDNow网站消费数据分析(Python)_第51张图片
image.png
# 订单金额总计
amount_total = user_amount.累计订单额.max()
amount_total
CDNow网站消费数据分析(Python)_第52张图片
image.png
# 转化成百分比
user_amount["占比"] = user_amount.累计订单额 / amount_total
user_amount.tail()
CDNow网站消费数据分析(Python)_第53张图片
image.png
# 做折线图,观察规律
user_amount.占比.plot(figsize= (9,5))
plt.xlabel("用户id",fontsize=18)
plt.ylabel("占比",fontsize=18)
plt.title("用户销售额贡献率", fontsize=25)
CDNow网站消费数据分析(Python)_第54张图片
image.png
  • 观察图表:
    横坐标是按贡献金额大小排序而成,纵坐标则是用户累计贡献。
    可以很清楚的看到,前20000个用户贡献了40%的消费。后面4000位用户贡献了60%,确实呈现28倾向。

下面继续看销量方面的贡献率

4.4.5.2、用户销量贡献分析

计算百分之多少的用户占了百分之多少的销量:

# 先将用户订单数量按升序排列,逐行累计订单数量,最后一行是订单总量
user_amount = data.groupby("用户id").订单数量.sum().sort_values().reset_index()
# 新增一列 计算订单数的累加和
user_amount["累计订单量"] = user_amount.订单数量.cumsum()
# 订单金额总计
amount_total = user_amount.累计订单量.max()
# 转化成百分比
user_amount["占比"] = user_amount.累计订单量 / amount_total
# 做折线图,观察规律
user_amount.占比.plot(figsize= (10,6))
plt.xlabel("用户id",fontsize=18)
plt.ylabel("占比",fontsize=18)
plt.title('用户累计销量贡献比', fontsize=25)
CDNow网站消费数据分析(Python)_第55张图片
image.png
  • 观察图表:
    可以很明显的发现前20000名用户贡献了40%的消费,而后3500名用户贡献了60%的消费。符合二八趋势。
    也就是说只要维护了这3500个用户就可以把业绩KPI完成60%,如果能把3500个用户运营的更好就可以占比70%—80%之间。
    在消费领域中,狠抓高质量用户是万古不变的道理。

4.5、用户分层

4.5.1、按用户价值分层(RFM模型)

为了进行精细化运营,可以利用RMF模型对用户价值指数(衡量历史到当前用户贡献的收益)进行计算,其中:
最近一次消费-R:客户最近一次交易时间的间隔。R值越大,表示客户交易发生的日期越久,反之则交易发生的日期越近。
消费频率-F:客户在最近一段时间内交易的次数。F值越大,表示客户交易越频繁,反之则表示客户交易不够活跃。
消费金额-M:客户在最近一段时间内交易的金额。M值越大,表示客户价值越高,反之则表示客户价值越低。 根据上述三个维度,对客户做细分。


CDNow网站消费数据分析(Python)_第56张图片
image.png

CDNow网站消费数据分析(Python)_第57张图片
image.png
# 建立rfm基表
rfm = data.pivot_table(index = '用户id',
                       values = ['订单金额','购买日期','订单数量'],
                       aggfunc = {'订单金额':'sum',
                                  '购买日期':'max',
                                  '订单数量':'sum'})
rfm.head()
CDNow网站消费数据分析(Python)_第58张图片
image.png
# 计算r值:日期的最大值与当前日期的差值为R
rfm['R'] = (rfm['购买日期'].max() - rfm['购买日期']) / np.timedelta64(1,'D')
rfm.head()
CDNow网站消费数据分析(Python)_第59张图片
image.png
# 对列名进行重命名:
rfm.rename(columns = {'订单金额':'M',
                      '订单数量':'F'},
                       inplace=True)
rfm.head()
CDNow网站消费数据分析(Python)_第60张图片
image.png
# 客户层次的定义
def rfm_func(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':'一般发展客户'
       }
    return d[label]
rfm['label'] = rfm[['R','F','M']].apply(lambda x: x-x.mean()).apply(rfm_func,axis=1)
CDNow网站消费数据分析(Python)_第61张图片
image.png
# 按照标签分组计算不同标签的销售额和销量
rfm.groupby("label").sum()
CDNow网站消费数据分析(Python)_第62张图片
image.png
  • 观察数据发现:
    M列中不同层次客户的消费累计金额,重要保持客户的累计消费金额为159203.62,排名最高,也就是说维护好这部分用户对于完成kpi目标非常关键
# 再看看不同标签用户的人数
rfm.groupby('label').count()
CDNow网站消费数据分析(Python)_第63张图片
image.png
  • 观察数据发现:
    一般挽留用户的消费人数排名第一,有14074人,重要保持客户排名第二,有4554人,与一般挽留用户差距比较大,但累计消费金额最多;
    业务方可以根据结果对客户分类运营,降低营销成本,提高ROI。
# 增加字段color,为下面作图做准备
rfm.loc[rfm.label == '重要价值客户','color'] = 'g'
rfm.loc[~(rfm.label == '重要价值客户'),'color'] = 'r'
rfm.plot.scatter("F",'R',c = rfm.color)
CDNow网站消费数据分析(Python)_第64张图片
image.png
  • 观察图表:
    从RFM分层可知,大部分用户为重要保持客户,但是这是由于极致的影响,所以RFM的划分应该尽量以业务为准。尽量用小部分的用户覆盖大部分的额度,不要为了数据好看划分等级。
    RFM是人工使用象限法把数据划分为几个立方体,立方体对应相应的标签,我们可以把标签运用到业务层面上。比如重要保持客户贡献金额最多159203.62,我们如何与业务方配合把数据提高或者维护;而重要发展客户和重要挽留客户他们有一段时间没有消费了,我们如何把他们拉回来等等。

4.5.2、按用户活跃程度分层(按活跃度)

新用户的定义是第一次消费;
活跃用户即老客,在某一个时间窗口内有过消费。
不活跃用户则是时间窗口内没有消费过的老客。
回流用户是在上一个窗口中没有消费,而在当前时间窗口内有过消费。
以上的时间窗口都是按月统计,
比如某用户在1月第一次消费,那么他在1月的分层就是新用户;他在2月消费国,则是活跃用户;3月没有消费,此时是不活跃用户;4月再次消费,此时是回流用户,5月还是消费,是活跃用户。

下面是具体实现过程:

#将用户消费数据进行数据透视:
data3 = data.pivot_table(index = "用户id",
                         columns = "月份",
                         values = '购买日期',
                         aggfunc = 'count').fillna(0)
data3.head() 
CDNow网站消费数据分析(Python)_第65张图片
image.png

图中的数字0/1/2代表当月的消费次数

# 格式化透视表里的数据,方便后续函数判断用户状态
data3 = data3.applymap(lambda x:1 if x>0 else 0)
data3.tail()
CDNow网站消费数据分析(Python)_第66张图片
image.png

0代表当月没有消费,1代表有消费

# 创建列表,储存列名
col = ['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']
def active_status(data):
    status=[]
    for i in range(18): #共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 pd.Series(status,index = col)
# 可得到一张不同用户在不同月份的不同状态主要为了统计起来更加方便而加进去。
data4 = data3.apply(lambda x : active_status(x),axis=1)
data4.head()
CDNow网站消费数据分析(Python)_第67张图片
image.png
  • 定义说明:
    new=新、active=活跃、return=回流、unactive=流失,unreg相当于未注册,指这个用户在这个月及以前从未购买过产品
  • 对于以上代码逻辑说明:
    首先对于参数data,data是单独的一行,也即是secris类型的数据apply方法默认取出的数据是一列,但是在后面直接明确了取数据的方式axis=1,代表每次取一行,一行进行函数中间的逻辑运算。既然是一行数据了,一行里有十八中类型,则需要遍历循环十八次,所以for i in range(18):默认从0到十八,但是包括0不包括十八(python的半包)。先定义一个空数组status,然后开始讨论本月是否有消费,假设本月没有消费,那么data[i]==0,判断status里是否有值即判断data[i-1],如果status里有值,看他里边的值是什么状态,如果说值等于unreg那本月依然是未注册。就往status里加入“unreg”,如果上个月是不活跃或者回流,这个月不消费依然是不活跃,status加入“unactive”。其他如果本身status里都没有值,这个月又不消费那么就是还没开始注册。假设本月已经消费那么data[i-1]==1,看一下status的状态,如果len(status)>0即是status里有值就要判断一下这里边值的状态了,如果这里边data[i-1]==‘unreg’就是说上个月就没注册,这个月突然消费了,那他就是新客啦!如果上个月是不活跃那这个月就是回流用户了,其他情况都是活跃用户。
#把unreg替换成NaN,它是「未来」才作为新客.再用fillna(0)把空值填为0。然后转置,把月份作为索引行,状态作为列,得到如下的表
data5 = data4.replace('unreg',np.NaN).apply(lambda x:pd.value_counts(x)).fillna(0).T
data5
CDNow网站消费数据分析(Python)_第68张图片
image.png

从表中可以看出,新客都是集中在前三个月,回流用户整体在1000左右,而不活跃用户数量随时间稍有上升。

#作出非堆积效果图:
data6 = data5.reset_index()
labels = data6[['active','new','return','unactive']].columns
plt.figure(figsize=(15,6))
data5.plot.area(figsize=(12,5))
plt.xlabel('月份')
plt.ylabel('消费人数')
plt.title('每月的消费人数')
plt.legend()
CDNow网站消费数据分析(Python)_第69张图片
image.png

生成面积图,比较丑。因为它只是某时间段消费过的用户的后续行为,蓝色和灰色区域都可以不看。只看紫色回流和红色活跃这两个分层,用户数比较稳定。这两个分层相加,就是消费用户占比(后期没新客)。

#每一层用户占总用户的比例
data7 = data5.apply(lambda x : x / x.sum(),axis=1)
data7.head()
CDNow网站消费数据分析(Python)_第70张图片
image.png

回流用户占总用户的比例

data7.plot(figsize=(12,6))
plt.xlabel('月份',fontsize=18)
plt.ylabel('占比',fontsize=18)
plt.title('不同活跃度用户扎占比',fontsize=25)
plt.legend()
CDNow网站消费数据分析(Python)_第71张图片
image.png
  • 观察图表:
    新客用户:仅在前三个月,后续再无新增客户;
    活跃用户:比例持续下降,说明持续消费的用户数量在减少,也说明运营效果并不好;
    回流用户:较为稳定;
    不活跃用户:比例上升,流失较大。

结论/建议 :

CDNow网站消费数据分析(Python)_第72张图片
image.png

1、整体趋势

  • 按年的月份趋势销量和销售额在1-3月份相对极高,然后骤降,原因可能跟这段时间的大力促销或与商品的季度属性有关。

2、用户个体特征

  • 每笔订单的金额和商品购买量都集中在区间的低段水平,都是小金额小批量进行购买,此类交易群体,可在丰富产品线和增加促销活动提高转换率和购买率。
  • 大部分用户的消费总额和购买总量都集中刚在低段,长尾分布,这个跟用户需求有关,可以对商品进行多元文化价值的赋予,增强其社交价值属性,提高用户的价值需求。

3、用户的消费周期

  • 用户的消费周期:有二次以上消费的用户,平均68天,所以在50天到60天期间,应该对这批用户进行刺激召回,细致点,比如10天回访满意度,30天发放优惠券,50天的时候提醒优惠券的使用。
  • 用户的生命周期:有二次及以上消费的用户的平均生命周期是276天。用户的生命周期分别在20天内与400至500天间,应该在20天内对客户进行引导,促进其再次消费并形成消费习惯,延长其生命周期;在100至400天的用户,也要根据其特点推出有针对性的营销活动,引导其持续消费,保持活跃。

4、用户质量

  • 新客户的复购率约为6%,老客户的复购率在20%左右;老客户的回购率在20%左右,需要营销策略积极引导其再次消费及持续消费。
  • 用户质量:用户个体消费有一定规律性,大部分用户的消费集中在2000以下,用户消费反应了2/8法则,消费排名前20%的用户贡献了80%的消费额。所以说,狠抓高质量用户是万古不变的道理,这些高质量客户都是“会员”类型,需要专门为会员优化购物体验,比如专线接听、特殊优惠等等。
  • 留存率来看,一半的用户会流失,所以应该注重对用户的忠诚度的培养,比如打卡签到,积分制度,老用户打折制度会员升级制度。

你可能感兴趣的:(CDNow网站消费数据分析(Python))