CD网站销售数据分析

2020-10-14  本文已影响0人  十七点五里

分析目标

本篇分析基于CDNow网站18个月的CD销售数据。数据来源于互联网(txt格式)一共有用户ID,购买日期,购买数量,购买金额四个字段。本次分析侧重于通过历史消费数据分析,总结用户流失原因;依据用户消费行为将其划分为不同类别的用户,从而进行个性化管理,以提高利润。

分析结论及建议导读

1、用户消费趋势的分析(按月)

2、用户个体消费分析

3.用户消费行为分析

详细内容阅读指导:

CD销售数据分析.png

1、数据预处理与描述性统计

1.1观察数据及预处理

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline               # 魔法方法,让图形在每一个空格中呈现
plt.rcParams['font.sans-serif'] = ['SimHei']     # 字体设置    正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False       # 坐标轴可以显示负数
plt.style.use('ggplot')                          # 设置图形风格,自带的比较丑,这里的用的是R语言的图形风格              

数据读取

df = pd.read_table('CDNOW_master.txt',sep = '\s+')    # 这份数据通过多个空格分隔,\s+ 表示匹配任意空白字符
df.head()

观察以上数据,发现没有字段名,需要手动添加

columns = ['user_id','order_dt','order_products','order_amount'] # 添加字段名
df.columns = columns
df.head()
df.info() 观察数据大体情况
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 69658 entries, 0 to 69657
Data columns (total 4 columns):
user_id           69658 non-null int64
order_dt          69658 non-null int64
order_products    69658 non-null int64
order_amount      69658 non-null float64
dtypes: float64(1), int64(3)
memory usage: 2.1 MB

通过上述信息可以知道数据十分干净,没有空行等,但是order_dt应该为日期时间类型,这里却是int类型,需要完成数据类型转换

df['order_dt'] = pd.to_datetime(df.order_dt,format = '%Y%m%d')  # Y为四位数的日年份,y表示两位数的年份
# 为了按照月份分析,新增字段month,[M]表示订单日期所在的月份,类型是时间数据,显示为月份的第一天   [Y]则显示为日期所在年份的第一天
df['month'] = df.order_dt.values.astype('datetime64[M]') 
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 69658 entries, 0 to 69657
Data columns (total 5 columns):
user_id           69658 non-null int64
order_dt          69658 non-null datetime64[ns]
order_products    69658 non-null int64
order_amount      69658 non-null float64
month             69658 non-null datetime64[ns]
dtypes: datetime64[ns](2), float64(1), int64(2)
memory usage: 2.7 MB

1.2 描述性统计

df.describe()

从销量方面看,平均每笔订单购买2.4张cd,标准差为2.33,稍有波动,购买量上四分位数为3张,说明大部分用户订单购买量不大,大部分集中在3张以下;消费金额呈现类似趋势,单笔订单最大值为99,说明有少部分狂热打榜粉丝,一般消费类的数据都是呈现长尾形态,即大部分用户都是小额消费者,小部分用户贡献了主要的消费收益,俗称二八理论

2 按月度进行消费趋势分析

2.1 每月的消费情况

先做每月消费总额和订单量的变化图

grouped_month = df.groupby(by=['month'])
order_month_amount = grouped_month['order_amount'].sum()
order_month_amount.plot()
plt.title('每月消费总额趋势图')
grouped_month.user_id.count().plot() 
plt.title('每月订单数量')

前3个月消费金额数据为最高峰,后续较为平稳,有轻微下降趋势

前3个月订单量都在1万左右,后续订单量急剧下滑,订单量维持在2500左右,猜测前期应为粉丝打榜或者促销活动释放了顾客购买力

2.2每月用户量

每月用户量稍稍复杂,主要是计算每个月下过订单的用户量,所以需要去重

grouped_month.user_id.apply(lambda x:len(x.drop_duplicates())).plot()   # 去重操作,使用匿名函数将grouped_month.user_id传入x,将每个月内重复id去除,输出的是每个月用户id去重后的数据长度  grouped_month.user_id等同于 grouped_month['user_id']
plt.title('每月用户量')
# 另一种去重方式:
df.groupby(['month','user_id']).count().reset_index()  #实际上是分组去重,先按月份分分完在按id分组,这样就去掉了重复值,接着去对每个唯一值去计数,reset_index() 去掉索引
s.groupby(['month']).user_id.count().plot()

前三个月用户量维持在9000左右,后续购买用户数总体上呈现缓慢下降的趋势,用户量维持在2000左右

2.3 每月用户消费的平均金额与平均次数

((grouped_month.order_amount.sum())/(grouped_month.user_id.apply(lambda x:len(x.drop_duplicates())))).plot()
plt.title('每月用户平均消费金额')
((grouped_month.user_id.count()) / grouped_month.user_id.apply(lambda x:len(x.drop_duplicates()))).plot()
plt.title('用户平均消费次数')

前3个月用户平均消费金额在40元左右,后续有所上升,用户平均消费金额在[46,56]

平均消费次数总体上呈现上升趋势,前3个月平均消费次数在1.2次上下,后续在1.35上下波动,说明cd消费大部分是一锤子买卖,狂热粉丝消费极少,顾客消费较为理性

3 用户个体消费分析

3.1 消费商品数与消费总金额的描述性统计

grouped_user = df.groupby('user_id')
grouped_user.sum().describe()

从用户角度看,单个用户平均购买量为7,中位数为3,说明小部分用户购买了大量CD;
用户平均消费金额为106元,消费总额中位数为43,判断同上,存在小部分的高额消费人群。
基本上消费、金融等与钱相关的都符合二八法则

3.2 消费金额与消费商品数的散点图(plot.scatter)

grouped_user.sum().query('order_amount < 4000'). plot.scatter(x = 'order_amount' , y = 'order_products')
plt.title('消费金额与消费产品数散点图')
#query后面只支持string形式的值,相当于sql的where子句,不筛选的话,极值会把图标拉大,不易观察数据集中部分的情况

用户比较健康,且规律性比较强,cd网站产品单一,价格较统一,消费金额与消费产品数量呈现线性关系

3.3 用户消费金额与消费次数的分布图

# 过滤掉商品数大于100的订单,减小极值影响,方便观察
grouped_user.sum().query('order_products<100').order_amount.hist(bins=40)
plt.title('消费金额分布图')
plt.figure(figsize=(12,5))
grouped_user.count().query('order_products<100').order_dt.hist(bins=40)
plt.title('用户消费次数的分布图')

大部分用户集中在低价领域,消费能力不高,高档用户几乎没有出现,符合消费市场行业规律
可以用中心极限低定理计算出95%的消费者消费金额在[0,856]之间


从图上看大部分用户只消费1次两次,高频消费者很少这也满足cd消费市场行业规律

3.4 累计消费金额占比

user_cumsum=grouped_user.sum().sort_values('order_amount').apply(lambda x: x.cumsum()/x.sum())
user_cumsum.reset_index().order_amount.plot()#去掉索引,方便作图
plt.title('消费金额累计百分比')

按用户消费金额进行升序排序,由图可知50%的用户仅贡献了15%的销售额度。而排名前5000的用户就贡献了60%的消费额。也就是说我们只要维护了这5000个用户就可以把业绩KPI完成60%,如果能把5000个用户运营的更好就可以占比70%—80%之间。

4 用户消费行为分析

4.1 用户第一次消费时间与最后一次消费时间

grouped_user.order_dt.min().value_counts().plot(figsize=(12,5))
plt.title('用户首次消费')
grouped_user.order_dt.max().value_counts().plot(figsize=(12,5))
plt.title('用户最后一次消费时间')

用户首次消费集中在前3个月,2月10日至2月25日有较为强烈的波动


output_61_1.png

观察用户的最后一次消费时间。用户最后一次消费比第一次消费分布广,大部分最后一次消费集中在前三个月,说明很多客户购买一次就不再进行购买。随着时间的增长,最后一次购买数也在递增,消费呈现流失上升的情况,用户忠诚度在慢慢下降

4.2 新老客户消费比

#计算只买了一次的消费群体
grouped_user.count().query('order_dt == 1').order_dt.count()
#总消费人群
grouped_user.count().reset_index().user_id.count()
11907
23569

大部分人值消费了一次,买一次就跑

# 按月份和用户ID分组
grouped_month_user=df.groupby(['month','user_id'])
# 用当月用户订单日期最小值与用户订单日期最小值联结
tmp=grouped_month_user.order_dt.agg(['min']).join(grouped_user.order_dt.min())
# 判断用户当月订单日期最小值是否与用户订单日期最小值相等,新建字段new
tmp['new']=(tmp['min']==tmp.order_dt)
# 作新客占比折线图
tmp.reset_index().groupby('month').new.apply(lambda x: x.sum()/x.count()).plot()
plt.title('每月新客占比百分比')
## 注意sum()是计算值的和,count()是计算统计非空值

只有前三个月有新客户,后续全是原有老客消费

4.3 用户分层

1)RFM模型

R:(Recency)最后一次消费时间的度量,数值越大越好(这里用距离所有用户最后一次消费时间来代替,越小越好)
F:(Frequency)消费的次数(本数据消费次数比较集中,用总商品数代替),数值越大越好
M:(Monetary)消费的总金额,数值越大越好

# 作透视表
RFM = df.pivot_table(index = 'user_id',
                     values = ['order_dt','order_amount','order_products'],
                     aggfunc = {'order_dt':'max','order_products':'sum','order_amount':'sum'})
# 计算每位用户最后一次消费时间与全部用户最后一次消费时间的差值
RFM['R'] = - (RFM.order_dt-RFM.order_dt.max())/np.timedelta64(1,'D') # 除以np.timedelta64(1,'D')  消除单位days
RFM.rename(columns = {'order_products':'F','order_amount':'M'},inplace = True)
RFM.head()
# 客户层次的定义
def RFM_func(x):
    level=x.apply(lambda x: '1' if x>=0 else '0')#把正负转换为10
    label=level.R+level.F+level.M#三个1或0拼接起来
    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()).apply(RFM_func,axis=1)#把数值与平均数相减,得到正负值,逐行应用

但这里平均数容易受到极值影响,所以,可以换成中位数,或者其他自定义的数值

RFM.head()
# 计算每层客户R、F、M的和
RFM.groupby('label').sum()

绝大部分消费是由重要保持客户产生的,维持好这部分客户,完成kpi有极大的帮助

# 计算每层客户R、F、M的个数和
RFM.groupby('label').count()

一般挽留客户客户基数较大,可对这部分客户进行挽留以保持高额增长

2)用户分层:新客、回流、活跃、流失

pivoted_counts = df.pivot_table(index = 'user_id',
                               columns = 'month',
                               values = 'order_dt',
                               aggfunc = 'count').fillna(0)
pivoted_counts.head()

用户每个月的消费次数,对于生命周期的划分只需要知道用户本月是否消费,消费次数在这里并不重要,需要将模型进行简化
使用数据透视表,需要明确获得什么结果。有些用户在某月没有进行过消费,会用NaA表示,这里用fillna填充。

df_purchase = pivoted_counts.applymap(lambda x  : 1 if x > 0 else 0)
df_purchase.tail()

对于尾部数据,user_id为2W+的数据是有问题的,因为从实际的业务场景上说,他们一月和二月都没有注册三月份才是他们第一次消费。透视会把他们一月和二月的数据补上为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 pd.Series (status,index=columns_month)
columns_month=df.groupby('month').sum().reset_index().month
pivoted_status = df_purchase.apply( active_status,axis = 1)
pivoted_status.head()
#每月不同活跃用户的计数
purchase_status_ct = pivoted_status.replace('unreg',np.NaN).apply(lambda x:pd.value_counts(x)) #不希望再后面count()时被计算,所以替代一下
purchase_status_ct
purchase_status_ct.fillna(0).T.plot.area()#填充nan值
plt.title("各类用户面积分布图")

active:活跃用户越来越少,说明运营的质量在降低,可能是用户体验不好、也可能是竞争加剧;
new:新用户前三个月之后就显著降低,说明市场和渠道部门需要加大拉新;
return:回流客户多,说明唤回运营(促销)起效;
unactive:不活跃用户正在增加,说明存在用户流失(也可能因为CD购买周期较长的原因

上一篇 下一篇

猜你喜欢

热点阅读